Getting started with webhooks
Management authentication
Seção intitulada “Management authentication”Webhook management routes use the same bearer token and JSON:API conventions as the rest of the API. They require a token belonging to a human user who is a full administrator of the current account.
Obtain that bearer through the normal interactive célula.in login flow. Authentication shows the credential fields and token response, but its POST /authenticate request is part of that human login flow; it is not a server-side token-acquisition flow. The bearer-only, no-Origin access path applies only to webhook-management routes. Do not call /authenticate from your integration server without an Origin header.
After the administrator signs in, make the webhook-management calls from your secure server without sending Origin. Fabricating an allowed Origin value or sending the proprietary X-Cin-Api-Token header is unsupported. There is no service-account or client-credentials flow, so do not store a person’s username, password, or bearer token for unattended management automation. Use the bearer only for an administrator-initiated management session.
1. Prepare an HTTPS receiver
Seção intitulada “1. Prepare an HTTPS receiver”Your receiver must:
- be reachable through a public HTTPS URL;
- accept
POSTrequests withContent-Type: application/vnd.api+json; - preserve the exact request body bytes for signature verification;
- return a
2xxresponse within 10 seconds; - process duplicate events safely.
For event and test deliveries, any 2xx response is successful. Verification deliveries have an additional challenge-response requirement described below.
2. Create an endpoint
Seção intitulada “2. Create an endpoint”Choose one or more values from the event catalog and create the endpoint:
curl --request POST 'https://api.celula.in/v1/webhook-endpoints' \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ --header 'Accept: application/vnd.api+json' \ --header 'Content-Type: application/vnd.api+json' \ --data '{ "data": { "type": "webhook-endpoints", "attributes": { "name": "Production integration", "url": "https://integrations.example.com/webhooks/celulain", "events": [ "person.created", "person.updated", "cell.updated" ] } } }'The response has status 201 Created, includes an ETag header such as "webhook-endpoint-1", and returns the signing secret in data.meta.secret. An abbreviated response looks like this:
{ "data": { "type": "webhook-endpoints", "id": "66a000000000000000000001", "attributes": { "name": "Production integration", "url": "https://integrations.example.com/webhooks/celulain", "events": ["person.created", "person.updated", "cell.updated"], "status": "pending-verification", "configuration-version": 1, "destination-version": 1, "secret-generation": 1 }, "meta": { "secret": "whsec_BASE64_SECRET" } }}Save both the endpoint ID and the complete whsec_... value in your secret manager before discarding the response. The secret cannot be retrieved with a later GET.
Treat every response containing data.meta.secret as secret material. This includes endpoint creation, PATCH requests containing url, secret rotations, and valid idempotent replays of URL updates or rotations. Configure HTTP clients, reverse proxies, APM tracing, analytics, and error reporting to redact the secret or the entire response body. Never write these responses to application or request logs.
Recover from a lost creation response
Seção intitulada “Recover from a lost creation response”Endpoint creation is not idempotently replayable. If the request succeeded but its response was lost, repeating the same normalized URL returns 409 duplicate-url without the endpoint ID or secret.
Instead:
-
List the account’s endpoints:
Terminal window curl --globoff 'https://api.celula.in/v1/webhook-endpoints?page[number]=1&page[size]=100' \--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \--header 'Accept: application/vnd.api+json' -
Find the unique resource in
datawhoseattributes.urlmatches your destination, then record that resource’sidand currentattributes.secret-generation. -
Use that endpoint ID and generation to rotate the secret. Store the new
data.meta.secretbefore discarding the rotation response.
3. Request verification
Seção intitulada “3. Request verification”Request verification using the endpoint’s current configuration-version. Idempotency-Key is required, must be non-empty, and can contain at most 128 characters.
curl --request POST 'https://api.celula.in/v1/webhook-endpoint-verifications' \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ --header 'Accept: application/vnd.api+json' \ --header 'Content-Type: application/vnd.api+json' \ --header 'Idempotency-Key: verify-production-endpoint-v1' \ --data '{ "data": { "type": "webhook-endpoint-verifications", "attributes": { "endpoint-id": "66a000000000000000000001", "configuration-version": 1 } } }'The API returns 202 Accepted with a webhook-deliveries resource. célula.in then sends a signed control message to your endpoint:
{ "jsonapi": { "version": "1.0" }, "data": { "type": "webhook-controls", "id": "66b000000000000000000001", "attributes": { "control-type": "webhook.verification", "created-at": "2026-08-16T15:00:00.000Z", "expires-at": "2026-08-16T15:15:00.000Z", "destination-version": 1, "verification-generation": 1, "challenge": "THE_RANDOM_CHALLENGE" } }, "meta": { "delivery-id": "66b000000000000000000001", "delivery-kind": "verification", "attempt": 1 }}After verifying the signature, return the exact challenge in a JSON response with a 2xx status:
{ "meta": { "challenge": "THE_RANDOM_CHALLENGE" }}The challenge expires after 15 minutes. Verification can make up to three attempts: immediately, after about 1 minute, and after about 5 additional minutes. Every retry has the same Webhook-Id and challenge. Verify the signature on every attempt and return the matching challenge again, even when that ID has already been seen. Do not pass verification controls through a generic duplicate-event shortcut that returns an empty 2xx response.
A 2xx response with a missing or different challenge permanently fails that verification delivery.
When verification succeeds, the endpoint becomes active. Confirm its current status with:
curl 'https://api.celula.in/v1/webhook-endpoints/66a000000000000000000001' \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ --header 'Accept: application/vnd.api+json'4. Send a test delivery
Seção intitulada “4. Send a test delivery”Test deliveries are available only for active, verified endpoints:
curl --request POST 'https://api.celula.in/v1/webhook-test-deliveries' \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ --header 'Accept: application/vnd.api+json' \ --header 'Content-Type: application/vnd.api+json' \ --header 'Idempotency-Key: first-production-test' \ --data '{ "data": { "type": "webhook-test-deliveries", "attributes": { "endpoint-id": "66a000000000000000000001" } } }'The signed request contains control-type: webhook.test and the marker Celulain webhook test delivery. A test delivery has one attempt and expires after 15 minutes; it is not automatically retried.
5. Process production events
Seção intitulada “5. Process production events”Verify every request before parsing or processing it. Queue accepted work internally, return 2xx quickly, and deduplicate it using Webhook-Id. See Event types and envelope for the production request format and Deliveries and retries for response classification and retry timing.
