Lynkist Developers

Webhook Management

Manage webhook endpoints, inspect deliveries, rotate signing secrets, replay failures.

This page covers the management side of webhooks: creating endpoints, listing them, rotating their signing secrets, inspecting delivery attempts, and replaying failures.

For the receive-side contract (event catalog, signature verification, retry curve), see Webhooks.

ScopeEndpoints
webhooks:readGET /webhooks, GET /webhooks/{id}, GET /webhooks/deliveries, GET /webhooks/deliveries/metrics, GET /webhooks/{id}/deliveries
webhooks:writePOST /webhooks, PUT /webhooks/{id}, DELETE /webhooks/{id}, POST /webhooks/{id}/rotate-secret, POST /webhooks/{id}/test, POST /webhooks/deliveries/{id}/retry

Base path: /api/v1/public/webhooks

Endpoint object

FieldTypeNotes
idUUID
urlstringHTTPS URL.
eventsstring[]Event types the endpoint is subscribed to.
descriptionstring | nullFree-form, ≤ 500 chars.
is_activebooleanfalse if auto-disabled after 20 consecutive failures.
last_success_atISO-8601 | null
last_failure_atISO-8601 | null
consecutive_failuresintegerResets to 0 on the next successful delivery.
created_atISO-8601
updated_atISO-8601 | null

POST /webhooks — Create an endpoint

curl -X POST https://lynkist.io/api/v1/public/webhooks \
  -H "Authorization: Bearer $LYNKIST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/lynkist/webhook",
    "events": ["message.sent", "message.delivered", "message.failed"],
    "description": "Primary message-status sink",
    "active": true,
    "api_version": "1"
  }'
FieldRequiredNotes
urlYesMust be HTTPS.
eventsYesNon-empty. Each must be a known event from the event catalog.
descriptionNo≤ 500 chars.
activeNo (true)Disable on create if you want to register the secret before going live.
api_versionNo (latest major)The major version of the payloads this endpoint gets, for example "1". An unknown version answers 400. See Versioning.

201 Created

{
  "data": {
    "id": "wh-…",
    "url": "https://example.com/lynkist/webhook",
    "events": ["message.sent", "message.delivered", "message.failed"],
    "description": "Primary message-status sink",
    "is_active": true,
    "api_version": "1.0",
    "last_success_at": null,
    "last_failure_at": null,
    "consecutive_failures": 0,
    "created_at": "2026-05-31T08:00:00Z",
    "updated_at": null
  },
  "signing_secret": "whsec_MfKQ9r8GKYqrTwjUPD8ILPZIo2LaLaSw"
}

signing_secret is shown once. Store it securely; you cannot retrieve it again. If you lose it, rotate it.

GET /webhooks — List endpoints

curl https://lynkist.io/api/v1/public/webhooks \
  -H "Authorization: Bearer $LYNKIST_API_KEY"

200 OK — { "data": [endpoint, …], "pagination": { "next_cursor", "has_more", "limit" } }. Uses cursor pagination.

GET /webhooks/{id} — Get an endpoint

curl https://lynkist.io/api/v1/public/webhooks/wh-… \
  -H "Authorization: Bearer $LYNKIST_API_KEY"

200 OK — the endpoint object.

PUT /webhooks/{id} — Update an endpoint

All fields are optional — send only what changes.

curl -X PUT https://lynkist.io/api/v1/public/webhooks/wh-… \
  -H "Authorization: Bearer $LYNKIST_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": ["message.sent", "message.delivered", "message.read", "message.failed"],
    "active": true
  }'

Setting active: true is how you re-enable an endpoint that was auto-disabled. Send api_version to move the endpoint to another major version.

200 OK — the updated endpoint object.

DELETE /webhooks/{id} — Delete an endpoint

curl -X DELETE https://lynkist.io/api/v1/public/webhooks/wh-… \
  -H "Authorization: Bearer $LYNKIST_API_KEY"

200 OK — { "id": "wh-…", "deleted": true }. Delivery history for the endpoint is retained for audit, but no further events will be queued.

POST /webhooks/{id}/rotate-secret — Rotate signing secret

Generates a new secret and returns it once. The previous secret stays valid for 24 hours: in that time every delivery is signed with the old secret and the new one, so you can change the secret in your receiver with no failed delivery.

curl -X POST https://lynkist.io/api/v1/public/webhooks/wh-…/rotate-secret \
  -H "Authorization: Bearer $LYNKIST_API_KEY"

200 OK

{
  "message": "Signing secret rotated",
  "signing_secret": "whsec_C2FVsBQIhrscChlQIMV+b5sSYspob7oD",
  "previous_secret_expires_at": "2026-11-15T10:00:00Z"
}

POST /webhooks/{id}/test — Send a test event

Sends a signed webhook.test event to this endpoint. The body is a deterministic envelope you can use to validate signature verification end-to-end without waiting for real traffic.

Unlike real events, which are queued and retried in the background, a test event is delivered synchronously — the call blocks until your endpoint answers (or up to 10s) and the response tells you what it said. Two more differences from normal delivery:

  • It runs even when the endpoint is disabled, so you can confirm a fix before re-enabling it. Check endpoint_disabled in the response — while it is true, real events are not delivered.
  • A failed test never counts toward the 20-consecutive-failure auto-disable threshold, and it is never retried. A successful one clears the failure count.
curl -X POST https://lynkist.io/api/v1/public/webhooks/wh-…/test \
  -H "Authorization: Bearer $LYNKIST_API_KEY"

200 OK

{
  "delivered": true,
  "delivery_id": "del-…",
  "message": "Endpoint responded HTTP 200 in 412ms",
  "status_code": 200,
  "response_time_ms": 412,
  "response_body": "ok",
  "error": null,
  "endpoint_disabled": false
}
FieldNotes
deliveredtrue only when your endpoint answered 2xx within 10s.
delivery_idThe delivery-log row for this attempt.
status_codeHTTP status your endpoint returned; null if it never replied.
response_bodyFirst 500 characters of your endpoint's response, if any.
errorWhy the send failed, when delivered is false.
endpoint_disabledtrue when the endpoint is inactive and real events are not flowing.

A 200 OK here means the test call completed, not that your endpoint accepted it — branch on delivered. The attempt is also written to the delivery log, so GET /webhooks/{id}/deliveries shows it alongside real traffic.

Delivery inspection

GET /webhooks/{id}/deliveries — Per-endpoint deliveries

curl "https://lynkist.io/api/v1/public/webhooks/wh-…/deliveries?status=failed&limit=20" \
  -H "Authorization: Bearer $LYNKIST_API_KEY"
QueryNotes
statusOne of pending, success, failed, retrying, abandoned.
cursorThe next_cursor of the previous page.
limit1-100 (default 20).

200 OK

{
  "data": [
    {
      "id": "del-…",
      "webhook_endpoint_id": "wh-…",
      "event_id": "evt_…",
      "event_type": "message.delivered",
      "request_url": "https://example.com/lynkist/webhook",
      "status": "success",
      "attempt_number": 1,
      "response_status_code": 200,
      "response_time_ms": 142,
      "error_message": null,
      "next_retry_at": null,
      "created_at": "2026-05-31T08:00:00Z",
      "completed_at": "2026-05-31T08:00:00Z"
    }
  ],
  "pagination": { /* … */ }
}

GET /webhooks/deliveries — All deliveries (cross-endpoint)

Same filters and shape as the per-endpoint version, but spans every endpoint the tenant owns.

GET /webhooks/deliveries/metrics — Delivery metrics

curl https://lynkist.io/api/v1/public/webhooks/deliveries/metrics \
  -H "Authorization: Bearer $LYNKIST_API_KEY"

200 OK

{
  "total": 12_443,
  "success": 12_201,
  "retrying": 14,
  "abandoned": 86,
  "pending": 142,
  "success_rate": 0.9805
}

Retrying a failed delivery

POST /webhooks/deliveries/{id}/retry — Replay a delivery

Replays the original payload to the endpoint. The replay creates a new delivery row — look at the returned delivery_id for the new attempt; the original is untouched.

curl -X POST https://lynkist.io/api/v1/public/webhooks/deliveries/del-…/retry \
  -H "Authorization: Bearer $LYNKIST_API_KEY"

200 OK

{
  "message": "Delivery retry queued",
  "delivery_id": "del-new-…"
}

Use this for an abandoned delivery you want to attempt again after fixing your receiver, or for debugging signature verification on an arbitrary historical event.

Errors

Statuserror.codeWhen
400VALIDATION_ERRORA malformed body, an unknown event type in events, or an unknown api_version.
400INVALID_WEBHOOK_URLThe URL is not HTTPS, or points to an address that is not allowed.
400INVALID_CURSORThe cursor is not one that this API returned.
401INVALID_API_KEY, API_KEY_EXPIREDThe API key is missing, wrong, revoked or expired.
403INSUFFICIENT_SCOPEThe key lacks webhooks:read or webhooks:write.
403UPGRADE_REQUIREDYour plan does not include webhooks.
404WEBHOOK_NOT_FOUND, WEBHOOK_DELIVERY_NOT_FOUNDThe endpoint or the delivery does not exist.
429RATE_LIMIT_EXCEEDEDRate limit. Wait for Retry-After.

Notes

  • Route ordering. /webhooks/deliveries/* literals are matched before /{webhook_id}, so the order you write requests in your client does not matter — deliveries is never confused with a UUID.
  • Endpoint URLs must be HTTPS. Lynkist rejects http:// URLs at the schema level.
  • webhook.test envelopes carry the same signature as any other event — use them to validate signature verification on first integration.
Webhook Management API | Lynkist Developers