Webhook

The Webhook object represents an endpoint configured to receive Event notifications.

A Webhook object represents an endpoint you've configured to receive Event notifications — payment successes, refunds, subscription changes, and more. Each webhook is subscribed to one or more event types and receives an HTTP POST for every matching event, signed with its secret.

A workspace can have up to 5 webhooks configured at once; create returns an error once that limit is reached. Archiving a webhook frees a slot; setting one to inactive does not.

A webhook URL must be a publicly reachable http:// or https:// address. Anything else is rejected, and the error tells you why.

A domain that isn't live yet — or a tunnel that has already expired — counts as unreachable, so save the webhook once the endpoint is actually serving.

Endpoints

MethodEndpointDescription
GET/v0/webhooks/listList all configured webhooks
POST/v0/webhooks/createCreate a new webhook
POST/v0/webhooks/sendTestEventSend a synthetic test event to subscribed webhooks
GET/v0/webhooks/{webhookId}Retrieve a webhook
PATCH/v0/webhooks/{webhookId}/updateUpdate a webhook
PATCH/v0/webhooks/{webhookId}/archiveArchive a webhook
GET/v0/webhooks/{webhookId}/attemptsList a webhook's delivery attempts

The Webhook object

objectstring

Object type identifier. Always "webhook" for Webhook objects.

idstringrequired

Unique identifier (UUID) for this webhook.

urlstringrequired

The endpoint that receives event deliveries.

statusstringrequired

Current webhook status. Possible values: active, inactive

secretstringrequired

Signing secret used to verify the x-fngs-signature header on each delivery.

list and get return the secret masked (••••••••cdef, last four characters only) — they only need the read-tier public key. create and update return it in full; both require the secret key. If you lose a secret, set a new one by sending secret on update.

eventsarrayrequired

Event types this webhook is subscribed to.

consecutiveFailuresinteger

How many events in a row have failed to reach this endpoint. Resets to 0 on the next success. See Automatic disabling. Defaults to 0.

failingSinceinteger | null

Unix timestamp (milliseconds) of the first failure in the current streak. null while the webhook is healthy.

lastBlockReasonstring | null

Set when the configured url cannot currently be used for delivery — it must be an http:// or https:// address that resolves publicly. null otherwise. Possible values: malformed_url, disallowed_scheme, blocked_host, unresolvable_host, blocked_resolved_address.

lastBlockedAtinteger | null

Unix timestamp (milliseconds) of the most recent delivery skipped for the reason above. null if that has never happened.

createdAtinteger

Unix timestamp (milliseconds) when the webhook was created.

deletedAtinteger | null

Unix timestamp (milliseconds) when the webhook was archived. null if not archived.


Delivery and retries

Each event is delivered on its own. Fungies waits up to 25 seconds for your endpoint to respond, and treats anything outside 2xx — or a timeout, or a connection error — as a failure. A 307 or 308 redirect is followed, up to 3 hops. Any other redirect, or one that cannot be followed, is a failed delivery recorded with its status code. A failed delivery is retried up to 5 times, roughly 5 minutes apart.

consecutiveFailures counts events, not attempts: it only increments once a delivery has used up all its retries.

Automatic disabling

When consecutiveFailures reaches 10, the webhook's status is set to inactive and it stops receiving events. It does not re-enable itself — set it back to active with update once your endpoint is healthy.

You are emailed twice: once when consecutiveFailures reaches 3, and again when the webhook is disabled at 10. There is no email when a failing endpoint recovers — a streak resetting to 0 is silent, so do not wait for an all-clear that never arrives.

Poll consecutiveFailures and failingSince on list or get to spot a degrading endpoint before it is disabled, and use GET /v0/webhooks/{webhookId}/attempts to see the status code and response body for each attempt.

Deliveries skipped because the URL itself cannot be used are tracked separately — see lastBlockReason above. Those do email you when they start and again when they clear.


The Webhook Attempt object

Each delivery attempt — success or failure — is recorded and available via the attempts endpoint.

objectstring

Object type identifier. Always "webhook_attempt" for Webhook Attempt objects.

idstringrequired

Unique identifier (UUID) for this attempt.

webhookIdstringrequired

The Webhook this attempt belongs to.

eventTypestring | null

The Event type that was delivered.

sequenceintegerrequired

Retry sequence number for this delivery. 0 is the first try.

statusCodeinteger | null

HTTP status code returned by the receiving endpoint. null if the request itself failed (timeout, connection error, blocked URL).

requestobject | null

The request body that was sent.

responseobject | null

The parsed JSON response body, if any.

createdAtinteger

Unix timestamp (milliseconds) when the attempt was made.


Example response

{
  "status": "success",
  "data": {
    "webhook": {
      "object": "webhook",
      "id": "660e8400-e29b-41d4-a716-446655440001",
      "url": "https://example.com/webhooks/fungies",
      "status": "active",
      "secret": "your-webhook-signing-secret",
      "events": ["payment_success", "subscription_cancelled"],
      "consecutiveFailures": 0,
      "failingSince": null,
      "lastBlockReason": null,
      "lastBlockedAt": null,
      "createdAt": 1717200000000,
      "deletedAt": null
    }
  }
}

Last updated