Skip to content
CredenShare
Webhooks

Delivery and retries

The request CredenShare sends, what counts as success, the retry schedule, and how to recover a delivery that failed.

Events are queued when they happen and sent by a background worker, so delivery is not synchronous with the action that caused it. Emission is fire-and-forget on CredenShare's side: if your endpoint is unreachable, the share, request or membership change that triggered the event still succeeds.

The request

CredenShare sends a POST to the URL you registered, with a JSON body.

HeaderValue
Content-Typeapplication/json
User-AgentCredenShare-Webhooks/1
X-CredenShare-EventThe event code, for example share.created
X-CredenShare-DeliveryThe delivery's id, a UUID
X-CredenShare-Signaturet=<unix-seconds>,v1=<hex> — see Verifying signatures

Standard HTTP headers (Host, Content-Length, Accept-Encoding) are set by the HTTP client as usual. You cannot add headers of your own, change the method, or change the content type; an endpoint's URL is also fixed once created.

A delivery on the wire takes this shape:

POST /your-webhook-path HTTP/1.1
Host: hooks.example.com
Content-Type: application/json
User-Agent: CredenShare-Webhooks/1
X-CredenShare-Event: share.deleted
X-CredenShare-Delivery: 3f1b9c74-5e2a-4b6d-9c88-1a0e7d452fb1
X-CredenShare-Signature: t=<unix-seconds>,v1=<hex>

<flat, metadata-only JSON carrying an "event" key>

Do not assume a particular key order or spacing in the body. Read it as JSON, and if you are verifying the signature, hash the bytes you received rather than anything you re-serialize.

A few consequences worth designing around:

  • Redirects are not followed. A 3xx is treated as a failed attempt, not as a pointer to somewhere else. Register the final URL.
  • HTTPS only, and the hostname must resolve to a public address. This is re-checked before every delivery, not only at registration.
  • Each attempt times out after 10 seconds — both connecting and the request as a whole.
Ten seconds is not much room. Verify the signature, write the event somewhere durable, respond, and do the real work afterwards. A receiver that processes inline will start timing out as soon as its own dependencies slow down, and every timeout costs you a retry.

What counts as success

Any 2xx status. The response body is ignored.

Anything else is a failed attempt, and what happens next depends on the status:

ResponseWhat CredenShare does
2xxMarks the delivery delivered. Done.
408 or 429Retries on the schedule below.
Any other 4xxMarks the delivery dead immediately. No further attempts, however many were left.
5xxRetries on the schedule below.
3xxNot followed. Counts as a failed attempt and is retried.
Timeout, connection refused, TLS failureRetried.

The reasoning behind the 4xx rule: a 4xx says the request itself is unacceptable, and CredenShare will send a byte-identical request on the next attempt, so repeating it cannot help. 408 and 429 are the two 4xx codes that mean "the request was fine, try it again later", so they stay on the schedule.

This makes 400 an expensive default for rejecting a delivery. If your receiver returns 400 for a signature mismatch, a temporary misconfiguration on your side turns every event that arrives during it into a permanent loss. Return 500 (or 429) for anything you expect to be able to fix, and reserve 4xx for a request you genuinely never want again.

The retry schedule

A delivery gets at most 6 HTTP attempts. Each wait is five times the last, capped at six hours:

After attemptWaitElapsed since the first attempt
11 minute1m
25 minutes6m
325 minutes31m
42 hours 5 minutes2h 36m
56 hours8h 36m

So the sixth and final attempt lands roughly 8 hours 36 minutes after the first. After it fails, the delivery is marked dead and left alone.

Two things affect when the first attempt arrives. The worker sweeps the queue once a minute, so expect up to about a minute between the event and the first attempt. Each sweep takes a bounded batch (currently up to 50 deliveries) and works through it one at a time, so a burst of events can take a few minutes to clear.

There is no ordering guarantee. Retries alone break ordering — a delivery that fails and succeeds an hour later arrives after events that happened long afterwards. If order matters to your logic, derive it from the state you fetch, not from arrival sequence.

Duplicates

Deduplicate on X-CredenShare-Delivery. Retries of the same delivery reuse the same id, so a receiver that acknowledged after its own timeout can recognise the repeat. A replay is a new delivery and gets a new id, which is deliberate: a replay is a request for the event to be handled again.

When an endpoint is disabled

Repeated failures do not disable your endpoint. There is no failure threshold, no automatic pause and no cool-off. Deliveries die individually, and new events keep being queued and attempted.

CredenShare disables an endpoint by itself in exactly two situations:

TriggerWhat happens
The endpoint's hostname resolves to a private, loopback or otherwise reserved address at delivery timeThat delivery is marked dead and the endpoint is disabled with a reason recorded against it
The endpoint's signing secret can no longer produce verifiable signatures, because the underlying signing key changedDeliveries are marked dead and the endpoint is disabled with a reason telling you to re-create it

Both cases record a reason against the endpoint, reported alongside its disabled state. For the first, make the hostname resolve to a public address again and re-enable the endpoint from the app; because an endpoint's URL cannot be changed after creation, an endpoint pointing at a name you cannot fix has to be replaced. For the second, rotating the secret will not help — create a new endpoint and configure your receiver with its new secret.

You can also disable an endpoint yourself, from the Webhooks card in your account's Security settings. A disabled endpoint stops receiving immediately: events that occur while it is disabled are not queued for it and cannot be replayed later, because no delivery was ever created.

A disabled endpoint still counts against your plan's endpoint limit. Disabling one does not free a slot — only removing it does.

Dead deliveries, the delivery log, and replay

There is no dead-letter queue and no separate failure notification. A delivery that exhausts its attempts, or that a 4xx killed outright, simply stays in the endpoint's delivery log with the status dead. Recovery is a deliberate act: you look at the log and replay what you want.

A delivery moves through these states:

StatusMeaning
pendingQueued, first attempt not yet made
deliveringAn attempt is in flight
deliveredA 2xx was received
failedAn attempt failed and another is scheduled
deadNo further attempts will be made

Reading the log

Open your account page, go to the Security tab, find the Webhooks card, and choose Deliveries on the endpoint you care about. The table shows the event code, the status, the number of attempts, the HTTP status code your endpoint last returned, when the delivery was created, and a Replay action. It lists the 50 most recent deliveries, newest first.

The delivery record also retains the error text of the last failure and the payload that was sent, which is what makes a failure diagnosable after the fact: you can see whether your endpoint answered 502, timed out, or rejected the request outright.

Webhook management — creating, editing, disabling and removing endpoints, plus the delivery log and replay — lives in the app and is authenticated by your session, not by an API key. That is deliberate: a leaked API key that could re-point a webhook would turn one stolen credential into an ongoing feed of event metadata. Through the API surface, the hosted MCP server can list your endpoints and rotate a secret, and nothing else.

Replaying

Replay creates a new delivery carrying the original payload, verbatim. Your endpoint receives what it missed, not the current state of the object — which is the point, but it means the payload can describe something that has since changed or been deleted. Treat a replayed event as a record of what happened, and fetch current state if you need it.

The original delivery's attempt history is left intact, so the log keeps showing what actually failed. The replay is a fresh delivery: it gets its own X-CredenShare-Delivery id, its own signature and timestamp, and its own six attempts.

Removing an endpoint hard-deletes its entire delivery log along with it. There is no recovery and nothing left to replay. If you are replacing an endpoint, replay anything you still need before removing the old one.

Retry windows and delivery history are the same on every paid plan. Compare plans, or ask about a stricter SLA.