Create a subscription
Subscribe a URL to one or more events. From then on, each matching change in this sub-account is POSTed to the URL as JSON. A secret token is generated and returned in this response only — store it now; it is sent with every delivery as Authorization: Bearer <token> and X-Webhook-Token.
POST
https://api.centerfy.ai/webhooks/inbound/outbound-webhooks Headers
| Header | Required | Value | Description |
|---|---|---|---|
x-api-key | Yes | your sub-account API key (cfy_…) | Authenticates the request; or use Authorization: Bearer <key>. |
Body parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
url | string | Yes | — | Public http(s) URL (max 2048 characters). Private, loopback and internal hosts and URLs with credentials are rejected. |
events | string[] | Yes | — | At least one, without repeats. Any of contact.created, contact.updated, contact.deleted, contact.tag_added, contact.tag_removed, contact.unsubscribed, contact.resubscribed, contact.assigned, contact.lead_score_changed, contact.custom_fields_updated, appointment.created, appointment.updated, appointment.deleted, appointment.confirmed, appointment.cancelled, appointment.no_show, appointment.completed, opportunity.created, opportunity.updated, opportunity.stage_changed, opportunity.status_changed, opportunity.deleted, message.received, message.sent, note.created, note.updated, note.deleted, conversation.created, contract.created, contract.sent, contract.viewed, contract.signed, contract.completed, contract.voided, invoice.created, invoice.sent, invoice.paid, invoice.voided, quote.created, quote.sent, quote.accepted, quote.declined, user.invited, user.joined, user.removed (see Part 4 for what each one sends). |
name | string | No | — | Label (max 200 characters). Defaults to “Contact webhook”. |
is_active | boolean | No | — | Defaults to true. false creates the subscription paused. |
source | string | null | No | — | Your system’s label (max 100 characters). Changes you make with the X-Webhook-Source header set to this value are not sent back to this URL. |
generate_secret | boolean | No | — | Defaults to true. false creates the subscription without a secret: secret_token is null and deliveries carry no token headers. |
Request body
{
"name": "CRM sync",
"url": "https://hooks.example.com/centerfy",
"events": [
"contact.created",
"contact.updated"
],
"source": "my-crm"
}Example
curl -X POST "https://api.centerfy.ai/webhooks/inbound/outbound-webhooks" \
-H "x-api-key: $CENTERFY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "CRM sync",
"url": "https://hooks.example.com/centerfy",
"events": [
"contact.created",
"contact.updated"
],
"source": "my-crm"
}' import { CenterfyClient } from "@centerfy/sdk";
const centerfy = new CenterfyClient({ apiKey: process.env.CENTERFY_API_KEY! });
const result = await centerfy.outboundWebhooks.create({
"name": "CRM sync",
"url": "https://hooks.example.com/centerfy",
"events": [
"contact.created",
"contact.updated"
],
"source": "my-crm"
});
console.log(result); centerfy outboundWebhooks create '{"name":"CRM sync","url":"https://hooks.example.com/centerfy","events":["contact.created","contact.updated"],"source":"my-crm"}' MCP tool: centerfy_outbound_webhooks_create (see MCP server)
Response
{
"status": "success",
"webhook": {
"id": "6f7a8b9c-0d1e-4f2a-8b3c-4d5e6f7a8b9c",
"name": "CRM sync",
"url": "https://hooks.example.com/centerfy",
"events": [
"contact.created",
"contact.updated"
],
"is_active": true,
"source": "my-crm",
"has_secret": true,
"created_at": "2026-10-01T10:00:00.000Z",
"updated_at": "2026-10-01T10:00:00.000Z"
},
"secret_token": "0000000000000000000000000000000000000000000000000000000000000000"
}Errors
400— if the url is not a public http(s) URL or an event is not supported.
Notes
Returns 201. secret_token is a 64-character hex string, shown only here and by rotate-secret. Subscriptions with more than one event are delivered normally but don’t appear in Settings → Webhooks.