Skip to content

REST API

Language-agnostic REST API reference for webhooks.cc. Use any HTTP client to manage endpoints, inspect requests and captured emails, configure forwarding, and stream webhooks.

Updated Nov 2026

Overview

The TypeScript SDK wraps this API, but you can call it directly from any language using standard HTTP requests. All request and response bodies use JSON.

Base URL: https://webhooks.cc

Webhook receiver URL: https://go.webhooks.cc

Email address: {slug}@mailhooks.cc (see Email capture)

Authentication

Authenticate by passing your API key in the Authorization header:

Authorization: Bearer whcc_...

API keys are generated from the API keys page. Keys use the whcc_ prefix and are stored as SHA-256 hashes; the raw key is shown only once at creation time.

Some sensitive operations (account settings and deletion, API key management, billing, share links, AI setup) require a Supabase session token instead of an API key. These operations return 403 when called with an API key.

Endpoints

Create endpoint

curl -X POST https://webhooks.cc/api/endpoints \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "my-endpoint"}'

Response:

{
  "id": "...",
  "slug": "abc123",
  "name": "my-endpoint",
  "url": "https://go.webhooks.cc/w/abc123",
  "createdAt": 1234567890000,
  "emailAddress": "abc123@mailhooks.cc",
  "showEmailExtracts": true,
  "site": "both",
  "forwardEnabled": false,
  "forwardUrl": null,
  "hasForwardSecret": false,
  "forwardHttp": false,
  "forwardEmail": true,
  "forwardFormat": "auto",
  "forwardHeaders": [],
  "forwardAppendPath": true,
  "forwardRetrySeconds": 86400,
  "forwardKeepOrder": false,
  "forwardSentField": null
}

Email and forwarding fields on every endpoint:

FieldDescription
emailAddressThe endpoint's email address. null for guest endpoints, which receive no email.
showEmailExtractsWhether codes and links found in emails are shown in the dashboard, returned by the API (extracts), and included in forwarded JSON. Default true.
siteWhich site's dashboard lists the endpoint: webhooks, mailhooks or both. Only the listing: both addresses capture either way. Send it when creating (both when left out; the dashboard sends the site it is on) or updating (owner only).
emailFieldsNamed fields picked out of every email: [{ "name", "from", "header"?, "pattern" }], at most 20 (Fields you pick out). from is subject, text, html, from, to or header; pattern is RE2, its first group the value. Owner only to change; send the whole list, or [] to remove them.
forwardEnabledWhether captured requests are forwarded to forwardUrl.
forwardUrlWhere requests are forwarded. Only returned to the endpoint's owner.
hasForwardSecretWhether a forwarding secret exists. The secret itself is never included.
forwardHttp, forwardEmailWhich kinds are forwarded: HTTP requests, emails.
forwardFormatauto (picked from the URL), as_received, json or chat. See Formats.
forwardHeadersYour own headers, [{ "name", "value" }] with masked values. Only returned to the endpoint's owner.
forwardAppendPathWhether the path after the slug is added to the URL for relayed requests.
forwardRetrySecondsHow long failed deliveries are retried: 0, 3600 or 86400.
forwardKeepOrderOne delivery at a time, in capture order.
forwardSentFieldThe JSON body field holding the sender's own timestamp, or null. See Timing.
destinationCountHow many destinations the endpoint has.
signingProvider, hasSigningSecret, signingHeaderSignature verification settings; the secret itself is never included. Send signingProvider, signingSecret and signingHeader (for generic-hmac) to change them, null to clear.
layouts, layoutsCapThe endpoint's layouts and how many of them the plan applies. Send the whole list to change it, null or [] to remove them.
retentionDays, retentionCapDaysThe endpoint's own retention in days (null: the plan's) and the most it can be on the plan.
deleteAfterDeliveryWhether requests are deleted once every destination has them ("deliver, then delete").

List endpoints

curl https://webhooks.cc/api/endpoints \
  -H "Authorization: Bearer whcc_..."

Returns { "owned": [...], "shared": [...] }: the endpoints you own, and the endpoints shared with you through a team. Each carries favorite, whether you marked it as a favourite. Shared endpoints leave out notificationUrl, forwardUrl and forwardHeaders.

Get endpoint

curl https://webhooks.cc/api/endpoints/abc123 \
  -H "Authorization: Bearer whcc_..."

Update endpoint

Update an endpoint's name, mock response, notification webhook, signing, email, layout or retention settings. Only the fields you send change.

curl -X PATCH https://webhooks.cc/api/endpoints/abc123 \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "new-name", "mockResponse": {"status": 200, "body": "{\"ok\":true}", "headers": {"Content-Type": "application/json"}}, "notificationUrl": "https://hooks.slack.com/services/..."}'

Set "mockResponse": null to clear the mock response and return to the default 200 OK. Set "notificationUrl": null to stop sending notifications. The notificationUrl follows the rules of a destination URL: https on a public host name, max 2048 characters.

Set "showEmailExtracts": false to stop picking out codes and links in emails. The forward* fields are covered under Forwarding.

Team members can change name, mockResponse, and responseRules on shared endpoints. showEmailExtracts, site, emailFields, layouts, retentionDays, deleteAfterDelivery and the notification, signing, and forwarding settings are owner only and return 403 for anyone else, because showEmailExtracts also decides what forwarding sends and site where the endpoint is listed for everyone.

Favourite an endpoint

Marks the endpoint as one of your favourites, which the dashboard lists first and opens by default. DELETE removes it again. Both are idempotent and answer { "favorite": true } or { "favorite": false }. A favourite is yours, not the endpoint's: team members can favourite a shared endpoint without changing it for anyone else.

curl -X PUT https://webhooks.cc/api/endpoints/abc123/favorite \
  -H "Authorization: Bearer whcc_..."
 
curl -X DELETE https://webhooks.cc/api/endpoints/abc123/favorite \
  -H "Authorization: Bearer whcc_..."

Every endpoint that List endpoints and Get endpoint return carries favorite, your own choice for it.

Delete endpoint

Deletes the endpoint and all its captured requests.

curl -X DELETE https://webhooks.cc/api/endpoints/abc123 \
  -H "Authorization: Bearer whcc_..."

Requests

List requests

curl "https://webhooks.cc/api/endpoints/abc123/requests?limit=50" \
  -H "Authorization: Bearer whcc_..."

Returns an array of request objects, newest first. Add kind=email for captured emails only, or kind=http for HTTP requests only; the paginated listing below takes the same parameter (send it on every page).

List requests (paginated)

curl "https://webhooks.cc/api/endpoints/abc123/requests/paginated?limit=20&cursor=..." \
  -H "Authorization: Bearer whcc_..."

Response:

{
  "items": [{ "id": "...", "method": "POST", "...": "..." }],
  "cursor": "eyJ...",
  "hasMore": true
}

Pass the returned cursor to the next request to fetch the next page.

Get single request

curl https://webhooks.cc/api/requests/REQUEST_ID \
  -H "Authorization: Bearer whcc_..."

Response:

{
  "id": "REQUEST_ID",
  "endpointId": "...",
  "method": "POST",
  "path": "/",
  "headers": { "content-type": "application/json", "...": "..." },
  "body": "{\"event\": \"test\"}",
  "queryParams": {},
  "contentType": "application/json",
  "ip": "203.0.113.1",
  "size": 18,
  "receivedAt": 1234567890000,
  "detectedProvider": "stripe",
  "detectedEvent": "checkout.session.completed",
  "signatureVerified": true,
  "signatureError": null,
  "signingProvider": "stripe",
  "responseStatus": 402,
  "responseRule": "Payment failed",
  "kind": "http",
  "email": null
}

responseStatus is the HTTP status the endpoint answered the sender with, and responseRule the response rule that chose it: its name, or Rule 2 for an unnamed one by its place in the list. responseRule is null when no rule matched and the endpoint's own response or the plain 200 answered. Both are recorded when the request arrives, so later edits to the rules do not change them, and both are null for emails and for requests captured before answers were recorded, in November 2026.

detectedProvider and detectedEvent are best-effort metadata derived from headers and body shape. They power the provider icon and event badge in the dashboard. Signature fields are present when endpoint-level verification is configured.

kind is email for captured emails. Those have method set to EMAIL, the recipient address as path, the original message as body, and the parsed message (subject, addresses, text and HTML parts, attachment list, sender checks) as email. On an endpoint with layouts, an email also carries parsed: the layout that matched, its fields, line items and parsed attachments.

Add extracts=true to the request list, the paginated list or a single request, and each email also carries extracts: the one-time codes found in it, in the order they appear, and its links, the one it asks you to click first: the same ones the dashboard shows. A signup or login test usually needs only extracts.codes[0] or extracts.links[0].url. extracts is null when the endpoint's showEmailExtracts is off; HTTP requests never have it. At most 20 codes and 20 links are returned. Search and the stream do not offer it.

"extracts": {
  "codes": ["482913"],
  "links": [
    { "url": "https://app.example.com/confirm?token=Zk3q9v", "label": "Confirm email", "action": true }
  ]
}

action is true for a link that looks like what the email asks you to click (confirm, verify, reset, sign in); those come first. label is the link text when the link came from the HTML part, otherwise null.

With extracts=true an email also has emailFields, the endpoint's named fields by name (each the text its pattern found, or null; emailFields is null when the endpoint has none), and replyText, the new text of a reply without the quoted message (Reply text). Both are there whatever showEmailExtracts says.

Render an email

curl -X POST https://webhooks.cc/api/requests/REQUEST_ID/render \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"format": "postmark"}'

Returns a captured email the way an inbound mail service posts it, to send to the handler you wrote for that service: { "format", "method": "POST", "headers": { "content-type", "user-agent" }, "body" }. format is postmark, sendgrid (a multipart form; the boundary is in content-type), mailgun (a form; add "mailgunSigningKey" to sign it the way Mailgun does), cloudmailin, or json for webhooks.cc's own email.received JSON, unsigned. See In a mail provider's shape for what each holds. Anyone who can read the email may render it; 400 for an HTTP request.

Try email fields

curl -X POST https://webhooks.cc/api/endpoints/abc123/email-fields/preview \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"emailFields": [{"name": "invoice", "from": "subject", "pattern": "(INV-\\d+)"}]}'

Runs unsaved fields (or the saved ones, without emailFields) on the endpoint's newest email, or on requestId, and returns { "problems": [...], "request": { "id", "subject", "receivedAt" } | null, "values": { "invoice": "INV-2041" } }. A field with a problem (a pattern RE2 cannot run) is listed in problems and left out of values. Nothing is saved. Owner only.

Clear requests

Delete all captured requests for an endpoint without deleting the endpoint itself.

curl -X DELETE "https://webhooks.cc/api/endpoints/abc123/requests" \
  -H "Authorization: Bearer whcc_..."

Add ?before=<timestamp in ms> to delete only requests received before then.

Send test webhook

Send a test webhook to one of your endpoints through the API.

curl -X POST https://webhooks.cc/api/send-test \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"slug": "abc123", "method": "POST", "headers": {"content-type": "application/json"}, "body": "{\"event\": \"test\"}"}'

Send test email

Deliver a sample email (with a one-time code and a link) to one of your endpoints. It is captured and counted like any other email, but it does not travel over SMTP, so no sender checks run on it.

curl -X POST https://webhooks.cc/api/send-test-email \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"slug": "abc123", "tag": "run-42"}'

tag is optional: with it, the sample goes to abc123+run-42@mailhooks.cc, so a test can wait for its own email. Returns { "status": "captured", "requestId": "..." }. A slug you cannot access returns 404, and so does a guest endpoint, since only endpoints on an account receive email. An endpoint whose quota is used up returns 409. Rate limited to 10 requests per minute.

Forwarding

Forwarding sends what an endpoint captures to up to 10 destinations: relayed as received, as signed JSON, as a JSON body you shape with placeholders, or as a chat message for Slack and Discord, each with its own conditions and timing, and retried until it is accepted. The formats and how to verify them are described in Forwarding. Only the endpoint's owner can configure forwarding: the owner-only routes below return 404 to anyone else, and the endpoint update returns 403.

The forward* fields of an endpoint stand for its primary destination, and notificationUrl for a chat destination named Notifications (at most one message a second, not retried). The destination routes manage all of them.

Turn forwarding on

curl -X PATCH https://webhooks.cc/api/endpoints/abc123 \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"forwardUrl": "https://example.com/hooks", "forwardHttp": true, "forwardEmail": true, "forwardEnabled": true}'

"forwardEnabled": true needs a forwardUrl, sent in the same request or saved before. The URL must be https on a public host name (no IP addresses or localhost), max 2048 characters. Saving the first URL creates the endpoint's signing secret, so you can set up your handler and send a test delivery before turning forwarding on. Send "forwardEnabled": false to stop; deliveries still waiting are marked failed, and so are those of a kind you switch off. Send "forwardUrl": null to remove the URL once forwarding is off.

The other settings, all optional:

FieldValues
forwardHttptrue to forward HTTP requests. Default false.
forwardEmailtrue to forward emails. Default true.
forwardFormatauto (default), as_received, json or chat.
forwardHeadersThe whole list of your headers, at most 10: [{ "name": "Authorization", "value": "Bearer ..." }]. A null value keeps the stored value of that name; [] removes them all.
forwardAppendPathfalse to send relayed requests to the URL exactly as written. Default true.
forwardRetrySeconds86400 (default), 3600, or 0 for a single try.
forwardKeepOrdertrue for one delivery at a time, in capture order. Default false.
forwardSentFieldA JSON body field with the sender's timestamp, such as publishedAt or data.publishedAt; null clears it.

Get the signing secret

curl https://webhooks.cc/api/endpoints/abc123/forwarding \
  -H "Authorization: Bearer whcc_..."

Returns { "secret": "whsec_..." }, or 404 before a URL has been saved.

Rotate the signing secret

curl -X POST https://webhooks.cc/api/endpoints/abc123/forwarding \
  -H "Authorization: Bearer whcc_..."

Returns { "secret": "whsec_..." } with the new secret. Every delivery from now on, retries included, is signed with it.

Send a test delivery

Sends the endpoint's newest captured request (or a sample marked as a test, if none has arrived yet) to the saved URL once, exactly as a delivery would go out, and reports what came back. It works with forwarding on or off, and nothing is retried or added to the delivery log. Send {"kind": "email"} or {"kind": "http"} to pick the kind; by default it is HTTP when HTTP requests are forwarded, otherwise email.

curl -X POST https://webhooks.cc/api/endpoints/abc123/forwarding/test \
  -H "Authorization: Bearer whcc_..."

Response:

{
  "status": 200,
  "durationMs": 182,
  "excerpt": "ok",
  "error": null,
  "delivered": true,
  "sample": false,
  "kind": "http",
  "format": "as_received",
  "url": "https://example.com/hooks/stripe/events",
  "target": "example.com/hooks/stripe/events",
  "request": { "method": "POST", "path": "/stripe/events", "subject": null, "receivedAt": 1791538201882 },
  "sender": { "at": 1791538201685, "source": "publishedAt", "wholeSeconds": false }
}

delivered is true when the URL answered 2xx. error says why no answer arrived (a timeout, a refused connection, a TLS problem), and excerpt is the start of the response body. sender is the sender's own timestamp when the request has one (Timing). Returns 400 when no URL is saved. Rate limited to 10 requests per minute.

List deliveries

curl "https://webhooks.cc/api/endpoints/abc123/deliveries?limit=50&status=failed" \
  -H "Authorization: Bearer whcc_..."

Returns the endpoint's deliveries, newest first. limit is 1 to 100 (default 5); status is all (default), pending (queued or retrying), failed or skipped (filtered out); destination takes a destination's id, for its deliveries only; before takes the cursor of the last row you have, for older rows (URL-encode it; it is opaque). Each has id, requestId, kind, destination ({ "id", "name", "url" }, or null once it is removed), status (pending, succeeded, failed or skipped), attempts, createdAt, finishedAt, nextAttemptAt, lastStatus, lastError, lastDurationMs (your server's time on the latest try), format, senderAt and senderSource (the sender's timestamp and where it was read), cursor, and the request's method, path, receivedAt and, for an email, subject.

Delivery counts

curl https://webhooks.cc/api/endpoints/abc123/deliveries/summary \
  -H "Authorization: Bearer whcc_..."

Returns { "last24h": { "delivered": 1284, "failed": 0 }, "pending": 3, "failed": 1, "total": 1312 }: what was delivered and failed among deliveries of the last 24 hours, what is waiting now, and the failed and total counts among the deliveries kept. Each count stops at 100,000.

Redeliver all failed

curl -X POST https://webhooks.cc/api/endpoints/abc123/deliveries/redeliver-failed \
  -H "Authorization: Bearer whcc_..."

Queues every request whose latest delivery to a destination failed again, oldest first, with that destination's current settings, and returns { "queued": 12 }. Add ?destination=<id> for one destination only. At most 1,000 deliveries wait for a destination at a time, so queued can be lower than the failed count; call it again once those are through. Returns 409 when no destination is on. Rate limited to 10 requests per minute.

List deliveries of a request

curl https://webhooks.cc/api/requests/REQUEST_ID/deliveries \
  -H "Authorization: Bearer whcc_..."

Response:

[
  {
    "id": "...",
    "requestId": "REQUEST_ID",
    "kind": "http",
    "status": "pending",
    "attempts": 2,
    "createdAt": 1791450602000,
    "finishedAt": null,
    "nextAttemptAt": 1791450752000,
    "lastStatus": 500,
    "lastError": "The URL answered 500.",
    "format": "as_received",
    "target": "example.com/hooks/stripe/events",
    "senderAt": null,
    "senderSource": null,
    "attemptLog": [
      {
        "attemptedAt": 1791450632000,
        "status": 500,
        "durationMs": 95,
        "error": "The URL answered 500.",
        "responseExcerpt": "Internal Server Error"
      },
      {
        "attemptedAt": 1791450602000,
        "status": 500,
        "durationMs": 104,
        "error": "The URL answered 500.",
        "responseExcerpt": "Internal Server Error"
      }
    ]
  }
]

Lists up to 20 deliveries of the request, newest first, each with every try (newest first). nextAttemptAt is set while a delivery is pending; attemptedAt is when a try ended. target is where it went (host and path; the host only for Slack and Discord, whose path is a secret, and for team members). Anyone who can read the request can list its deliveries, team members included. A request that was never forwarded returns [].

Redeliver a request

curl -X POST https://webhooks.cc/api/requests/REQUEST_ID/deliveries \
  -H "Authorization: Bearer whcc_..."

Queues another delivery of an HTTP request or an email to every destination that takes it, sent with their current settings, and returns { "id": "..." }. It also sends a request that was never forwarded. Send {"destinationId": "..."} to send it to that destination only, whatever its filter says. Owner only. Returns 409 when no destination takes it. Rate limited to 30 requests per minute.

Destinations

curl -X POST https://webhooks.cc/api/endpoints/abc123/destinations \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"url": "https://hooks.zapier.com/hooks/catch/123/abc", "email": false, "format": "template", "bodyTemplate": {"event": "{{ json.type }}"}, "filter": {"conditions": [{"field": "body_path", "op": "eq", "path": "type", "value": "invoice.paid"}]}}'
RouteWhat it does
GET /api/endpoints/:slug/destinationsThe destinations in order, header values masked; ?summary=true adds each one's last 24 hours
POST /api/endpoints/:slug/destinationsAdd one (up to 10); returns it with its id and creates its signing secret
GET /api/endpoints/:slug/destinations/:idOne destination
PATCH /api/endpoints/:slug/destinations/:idChange the fields sent; switching it off or taking a kind away fails what is waiting for it
DELETE /api/endpoints/:slug/destinations/:idRemove it; what is waiting is marked failed, the log stays
PUT /api/endpoints/:slug/destinations/order{"ids": [...]}, every id once: the new order
GET /api/endpoints/:slug/destinations/:id/secret{ "secret": "whsec_..." }; POST rotates it
POST /api/endpoints/:slug/destinations/:id/testSend one delivery now (kind, requestId), as the forwarding test above, plus filterPasses
POST /api/endpoints/:slug/destinations/previewDry run: destinationId and/or unsaved destination fields, optional requestId; nothing is sent

A destination's fields are url, name, enabled, http, email, filter ({ "logic": "and" | "or", "conditions": [...] } or null), format (auto, as_received, json, template, chat, or a mail provider's shape for emails: postmark, sendgrid, mailgun, cloudmailin), bodyTemplate (any JSON, for template), messageTemplate (for chat), mailgunSigningKey (for mailgun; write-only, the destination says hasMailgunSigningKey), headers, appendPath, retrySeconds (0, 3600 or 86400), keepOrder, sentField, throttleSeconds and delaySeconds (0 to 3,600), attachments (how an email's files travel: links, inline or none) and position. Google Sheets (sheets) and file drop (file_drop, Business) destinations keep their settings in config and are easiest to set up in the dashboard. A mistake in a template or a condition returns 400 naming the field. See the SDK reference for each field and the preview's answer.

Search captured requests across endpoints with optional filters.

curl "https://webhooks.cc/api/search/requests?slug=abc123&method=POST&q=stripe&limit=20" \
  -H "Authorization: Bearer whcc_..."

Query parameters:

ParamTypeDescription
slugstringFilter by endpoint slug
methodstringFilter by HTTP method (EMAIL for emails)
kindstringhttp or email
qstringFree-text search
fromnumberStart time (Unix timestamp in ms)
tonumberEnd time (Unix timestamp in ms)
limitnumberMax results (default: 50)
offsetnumberPagination offset
orderstring"asc" or "desc" (default)

Search count

Count matching requests without returning them.

curl "https://webhooks.cc/api/search/requests/count?slug=abc123&method=POST" \
  -H "Authorization: Bearer whcc_..."

Returns { "count": 42 }. Accepts the same query parameters as search (except limit, offset, order).

Real-time streaming (SSE)

Stream incoming requests as Server-Sent Events. Useful for building integrations that react to webhooks in real time.

curl -N "https://webhooks.cc/api/stream/abc123" \
  -H "Authorization: Bearer whcc_..." \
  -H "Accept: text/event-stream"

Each captured request arrives as an SSE message:

event: request
data: {"_id":"...","_creationTime":1234567890000,"endpointId":"...","method":"POST","path":"/","headers":{...},"body":"...","receivedAt":1234567890000,"kind":"http"}

The stream opens with a connected event ({ "slug", "endpointId" }). The server sends a keepalive comment (: keepalive) every 30 seconds to keep the connection alive. Maximum connection duration is 30 minutes: a timeout event comes before the stream closes, so reconnect then. endpoint_deleted means the endpoint is gone.

A stream message names the request id _id (not id) and has no email field. Emails arrive as request events too, with "kind": "email", "method": "EMAIL" and the recipient address as path; fetch the request by its _id for the parsed message.

Usage

Check your current request quota and usage.

curl https://webhooks.cc/api/usage \
  -H "Authorization: Bearer whcc_..."

Response:

{
  "used": 12,
  "limit": 50,
  "remaining": 38,
  "blockRequests": 0,
  "creditRequests": 0,
  "overage": { "active": false, "used": 0, "max": 0 },
  "plan": "free",
  "periodEnd": 1234567890000,
  "teamBilledEndpoints": 0,
  "emails": 3
}

limit is the period's quota including monthly blocks (blockRequests), and used counts against it. remaining is how many more requests are accepted this period: what is left of limit, plus one-time blocks (creditRequests), plus overage up to its maximum. See Extra requests. emails is how many of used were emails, and teamBilledEndpoints how many of your endpoints are shared with a subscribed team and bill its pooled quota instead.

Teams

Manage teams, share endpoints, and invite members. Any account can create a team; inviting members and sharing endpoints require the team to have an active Teams or Team Business subscription.

List teams

curl https://webhooks.cc/api/teams \
  -H "Authorization: Bearer whcc_..."

Returns an array of team objects that you own or belong to.

Create team

curl -X POST https://webhooks.cc/api/teams \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "My Team"}'

Response:

{
  "id": "TEAM_ID",
  "name": "My Team",
  "ownerId": "...",
  "createdAt": "2026-03-28T00:00:00.000Z"
}

Update team

Rename a team. Only the team owner can update.

curl -X PATCH https://webhooks.cc/api/teams/TEAM_ID \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"name": "Renamed Team"}'

Delete team

Delete a team and remove all memberships. Only the team owner can delete.

curl -X DELETE https://webhooks.cc/api/teams/TEAM_ID \
  -H "Authorization: Bearer whcc_..."

Returns 204 No Content on success.

Share endpoint with team

Share one of your endpoints so all team members can view its captured requests.

curl -X POST https://webhooks.cc/api/teams/TEAM_ID/endpoints \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"endpointId": "ENDPOINT_ID"}'

Unshare endpoint

Remove an endpoint from a team.

curl -X DELETE https://webhooks.cc/api/teams/TEAM_ID/endpoints/ENDPOINT_ID \
  -H "Authorization: Bearer whcc_..."

Returns 204 No Content on success.

Invite member

Invite a user to your team by email. Only the team owner can invite.

curl -X POST https://webhooks.cc/api/teams/TEAM_ID/invite \
  -H "Authorization: Bearer whcc_..." \
  -H "Content-Type: application/json" \
  -d '{"email": "teammate@example.com"}'

List members

List all members and pending invites for a team.

curl https://webhooks.cc/api/teams/TEAM_ID/members \
  -H "Authorization: Bearer whcc_..."

Response:

{
  "members": [
    { "userId": "...", "email": "owner@example.com", "role": "owner" },
    { "userId": "...", "email": "member@example.com", "role": "member" }
  ],
  "pendingInvites": [{ "id": "...", "email": "invited@example.com", "createdAt": "..." }]
}

Remove member

Remove a member from a team. Only the team owner can remove members.

curl -X DELETE https://webhooks.cc/api/teams/TEAM_ID/members/USER_ID \
  -H "Authorization: Bearer whcc_..."

Returns 204 No Content on success.

Leave team

Leave a team you are a member of. Owners cannot leave their own team; delete it instead.

curl -X POST https://webhooks.cc/api/teams/TEAM_ID/leave \
  -H "Authorization: Bearer whcc_..."

Invites

Manage team invitations sent to you.

List pending invites

curl https://webhooks.cc/api/invites \
  -H "Authorization: Bearer whcc_..."

Returns an array of pending invite objects addressed to your account.

Accept invite

curl -X POST https://webhooks.cc/api/invites/INVITE_ID/accept \
  -H "Authorization: Bearer whcc_..."

Decline invite

curl -X POST https://webhooks.cc/api/invites/INVITE_ID/decline \
  -H "Authorization: Bearer whcc_..."

Session-token-only routes

Some routes require a Supabase session token (from browser login) instead of an API key. These return 403 when called with an API key.

  • GET /api/api-keys: list your API keys
  • POST /api/api-keys: create a new API key
  • DELETE /api/api-keys?id=...: delete an API key
  • PATCH /api/account: change account settings
  • DELETE /api/account: delete your account and all data
  • /api/billing/* and /api/teams/:id/billing/*: plans, seats, extra capacity, checkout and cancellation
  • /api/requests/:id/shares and /api/request-shares: create, list and revoke share links
  • /api/endpoints/:slug/layouts/assist: AI setup for layouts and email fields
  • /api/agent/registrations: list and disconnect connected agents
  • /api/domains: add, verify and remove custom inbound domains

These routes are designed for the web dashboard, not programmatic access.

Error handling

All errors return a JSON body with an error field:

{ "error": "Invalid or missing API key" }
StatusMeaning
400Validation error: check request body and parameters
401Invalid or missing API key
403Not allowed, for example an owner-only setting
404Resource not found
409Conflict, for example redelivering with forwarding off
429Rate limited: check Retry-After header for wait time
500Server error

Rate limit headers

Rate-limited endpoints return these headers on every response (not just 429s):

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window
X-RateLimit-RemainingRemaining requests in the current window
X-RateLimit-ResetUnix timestamp (seconds) when the window resets
Retry-AfterSeconds to wait (only on 429 responses)

Language examples

Python

import requests
 
API_KEY = "whcc_..."
BASE = "https://webhooks.cc"
HEADERS = {"Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json"}
 
# Create endpoint
endpoint = requests.post(f"{BASE}/api/endpoints", headers=HEADERS, json={"name": "my-endpoint"}).json()
slug = endpoint["slug"]
print(f"Webhook URL: {endpoint['url']}")
 
# Send a test webhook
requests.post(f"{BASE}/api/send-test", headers=HEADERS, json={
    "slug": slug,
    "method": "POST",
    "headers": {"content-type": "application/json"},
    "body": '{"event": "test"}'
})
 
# List captured requests
captured = requests.get(f"{BASE}/api/endpoints/{slug}/requests", headers=HEADERS).json()
for req in captured:
    print(f"{req['method']} {req['path']} {req['receivedAt']}")

Go

package main
 
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)
 
const (
	base   = "https://webhooks.cc"
	apiKey = "whcc_..."
)
 
func main() {
	// Create endpoint
	body, _ := json.Marshal(map[string]string{"name": "my-endpoint"})
	req, _ := http.NewRequest("POST", base+"/api/endpoints", bytes.NewReader(body))
	req.Header.Set("Authorization", "Bearer "+apiKey)
	req.Header.Set("Content-Type", "application/json")
 
	resp, _ := http.DefaultClient.Do(req)
	defer resp.Body.Close()
	data, _ := io.ReadAll(resp.Body)
 
	var endpoint map[string]interface{}
	json.Unmarshal(data, &endpoint)
	fmt.Printf("Webhook URL: %s\n", endpoint["url"])
 
	// List requests
	slug := endpoint["slug"].(string)
	req, _ = http.NewRequest("GET", base+"/api/endpoints/"+slug+"/requests", nil)
	req.Header.Set("Authorization", "Bearer "+apiKey)
	resp, _ = http.DefaultClient.Do(req)
	defer resp.Body.Close()
	data, _ = io.ReadAll(resp.Body)
 
	var requests []map[string]interface{}
	json.Unmarshal(data, &requests)
	for _, r := range requests {
		fmt.Printf("%s %s\n", r["method"], r["path"])
	}
}

Ruby

require "net/http"
require "json"
require "uri"
 
API_KEY = "whcc_..."
BASE = "https://webhooks.cc"
 
def api_request(method, path, body = nil)
  uri = URI("#{BASE}#{path}")
  req = case method
        when :get    then Net::HTTP::Get.new(uri)
        when :post   then Net::HTTP::Post.new(uri)
        when :delete then Net::HTTP::Delete.new(uri)
        end
  req["Authorization"] = "Bearer #{API_KEY}"
  req["Content-Type"] = "application/json"
  req.body = body.to_json if body
 
  res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
  JSON.parse(res.body)
end
 
# Create endpoint
endpoint = api_request(:post, "/api/endpoints", { name: "my-endpoint" })
slug = endpoint["slug"]
puts "Webhook URL: #{endpoint['url']}"
 
# Send a test webhook
api_request(:post, "/api/send-test", {
  slug: slug,
  method: "POST",
  headers: { "content-type" => "application/json" },
  body: '{"event": "test"}'
})
 
# List captured requests
requests = api_request(:get, "/api/endpoints/#{slug}/requests")
requests.each do |req|
  puts "#{req['method']} #{req['path']} #{req['receivedAt']}"
end

Rust

use reqwest::header::{AUTHORIZATION, CONTENT_TYPE, HeaderMap, HeaderValue};
use serde_json::{json, Value};
 
const BASE: &str = "https://webhooks.cc";
const API_KEY: &str = "whcc_...";
 
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let client = reqwest::Client::new();
    let mut headers = HeaderMap::new();
    headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {API_KEY}"))?);
    headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json"));
 
    // Create endpoint
    let endpoint: Value = client
        .post(format!("{BASE}/api/endpoints"))
        .headers(headers.clone())
        .json(&json!({"name": "my-endpoint"}))
        .send()
        .await?
        .json()
        .await?;
 
    let slug = endpoint["slug"].as_str().unwrap();
    println!("Webhook URL: {}", endpoint["url"]);
 
    // Send a test webhook
    client
        .post(format!("{BASE}/api/send-test"))
        .headers(headers.clone())
        .json(&json!({
            "slug": slug,
            "method": "POST",
            "headers": {"content-type": "application/json"},
            "body": r#"{"event": "test"}"#
        }))
        .send()
        .await?;
 
    // List captured requests
    let requests: Vec<Value> = client
        .get(format!("{BASE}/api/endpoints/{slug}/requests"))
        .headers(headers)
        .send()
        .await?
        .json()
        .await?;
 
    for req in &requests {
        println!("{} {}", req["method"], req["path"]);
    }
 
    Ok(())
}

Next steps

TypeScript SDK reference

Full method reference for the @webhooks-cc/sdk package.

Email forwarding

The forwarded JSON payload, signature verification, and the retry schedule.

Plans & Limits

Request quotas, billing periods, and plan details.

Interactive API Explorer

Try the API directly from your browser with the interactive reference.