Start free

API reference

Webhooks

Subscribe endpoints to receive HMAC-signed events, validate a URL before subscribing, pause or delete a subscription and rotate the secret at any time.

  • 7 operations
  • Base URLhttps://api.messages.ccxcompany.co
  • AuthenticationBearer with your API key

The technical text below comes from the public contract, openapi.json, in the language the contract was written in.

List webhook subscriptions

GET/webhooks

Returns paginated list. Filters: status, event, limit, skip. The secretCipher field is the encrypted blob — never the plaintext secret.

Parameters

FieldInTypeDescription
statusoptionalquerystringValues:activepausedfailingrevoked
eventoptionalquerystringValues:message.sentmessage.deliveredmessage.readmessage.failedmessage.receivedcampaign.completednumber.quality_changedtemplate.approvedtemplate.rejectedcontact.createdcontact.opted_outapikey.createdapikey.rotatedsms.deliveredsms.failedsms.undeliveredsms.receivedemail.deliveredemail.bouncedemail.complainedemail.openedemail.clickedrcs.deliveredrcs.readrcs.failedsms.clickedrcs.clickedwallet.low_balancewallet.depletedinstagram.message.receivedinstagram.message.sentinstagram.message.failed
limitoptionalqueryinteger
skipoptionalqueryinteger

Responses

  • 200Default Response

    Response fields and example
    FieldTypeDescription
    itemsrequiredobject[]
    items[].idrequiredstringBranded Webhook id (wbh_ prefix + 21 nano-id chars).
    items[].organizationIdrequiredstringBranded Organization id (org_ prefix + 21 nano-id chars).
    items[].urlrequiredstring · uri
    items[].secretCipherrequiredstringPII_CRITICAL — encrypted webhook signing secret (JSON-serialized EncryptedBlob, AAD bound to org+webhook). Never returned in plaintext after creation.
    items[].secretVersionrequiredinteger
    items[].headersoptionalobject[]
    items[].headers[].keyrequiredstring
    items[].headers[].valuerequiredstring
    items[].eventsrequiredstring[]
    items[].statusrequiredstringValues:activepausedfailingrevoked
    items[].descriptionoptionalstring
    items[].retryrequiredobject
    items[].retry.maxAttemptsrequiredinteger
    items[].retry.backoffSecondsrequiredinteger
    items[].createdByIdoptionalstringBranded Account id (acc_ prefix + 21 nano-id chars).
    items[].consecutiveFailuresoptionalinteger
    items[].lastSuccessAtoptionalstring · date-timeISO-8601 timestamp in UTC.
    items[].lastFailureAtoptionalstring · date-timeISO-8601 timestamp in UTC.
    items[].createdAtrequiredstring · date-timeISO-8601 timestamp in UTC.
    items[].updatedAtrequiredstring · date-timeISO-8601 timestamp in UTC.
    totalrequiredinteger
    JSON
    {  "items": [    {      "id": "wbh_uV2aLwzXMqNmKjHbPiRoT",      "organizationId": "org_pGgniw5L7CTDSZDwjDrQ2",      "url": "https://api.partner.test/ccx/webhook",      "secretCipher": "string",      "secretVersion": 1,      "headers": [        {          "key": "X-Api-Key",          "value": "sk_live_…"        }      ],      "events": [        "message.sent"      ],      "status": "active",      "description": "string",      "retry": {        "maxAttempts": 8,        "backoffSeconds": 60      },      "createdById": "acc_dEws32WQG1MgvOVa6wqXT",      "consecutiveFailures": 0,      "lastSuccessAt": "2026-04-07T17:30:00.000Z",      "lastFailureAt": "2026-04-07T17:30:00.000Z",      "createdAt": "2026-04-07T17:30:00.000Z",      "updatedAt": "2026-04-07T17:30:00.000Z"    }  ],  "total": 3}
  • 401RFC 7807 problem details for UNAUTHORIZED (401).

  • 403RFC 7807 problem details for FORBIDDEN (403).

Error responses follow RFC 7807 (application/problem+json), with a stable code in the code field. See the error codes

curl example

cURL
curl 'https://api.messages.ccxcompany.co/webhooks' \  -H "Authorization: Bearer $CCX_API_KEY"
Required fields only. Swap the sample values for your own.

Subscribe a webhook endpoint

POST/webhooks

Creates a webhook subscription with a fresh HMAC signing secret. The secret is encrypted at rest by the PII guardian (AES-256-GCM, AAD bound to org+webhook id) and returned exactly once in the reveal.secret field of the response. Caller MUST store it — it cannot be retrieved later.

Signing convention (Step 19 worker will use this):

  • Algorithm: HMAC-SHA256(secret, "${unix_ts}.${rawBody}")
  • Header: X-CCX-Signature: t=<unix>,v1=<hex>
  • Headers: X-CCX-Webhook-Id, X-CCX-Event, X-CCX-Delivery-Id
  • Secret format: whsec_<base64url>

Request bodyapplication/json

FieldTypeDescription
urlrequiredstring · uriHTTPS endpoint that will receive signed POST requests.
eventsrequiredstring[]Subscribed events. Closed enum from WEBHOOK_EVENTS.
descriptionoptionalstring
headersoptionalobject[]Custom headers forwarded on every delivery + the ping. Values are encrypted at rest; reserved (X-CCX-*, Host, …) names are rejected.
headers[].keyrequiredstring
headers[].valuerequiredstring
retryoptionalobject
retry.maxAttemptsrequiredinteger
retry.backoffSecondsrequiredinteger
Full body example
JSON
{  "url": "https://api.partner.test/ccx/webhook",  "events": [    "message.delivered",    "message.failed"  ],  "description": "Production CRM sync",  "headers": [    {      "key": "X-Api-Key",      "value": "sk_live_…"    }  ],  "retry": {    "maxAttempts": 8,    "backoffSeconds": 60  }}

Responses

  • 201Default Response

    Response fields and example
    FieldTypeDescription
    webhookrequiredobject
    webhook.idrequiredstringBranded Webhook id (wbh_ prefix + 21 nano-id chars).
    webhook.organizationIdrequiredstringBranded Organization id (org_ prefix + 21 nano-id chars).
    webhook.urlrequiredstring · uri
    webhook.secretCipherrequiredstringPII_CRITICAL — encrypted webhook signing secret (JSON-serialized EncryptedBlob, AAD bound to org+webhook). Never returned in plaintext after creation.
    webhook.secretVersionrequiredinteger
    webhook.headersoptionalobject[]
    webhook.headers[].keyrequiredstring
    webhook.headers[].valuerequiredstring
    webhook.eventsrequiredstring[]
    webhook.statusrequiredstringValues:activepausedfailingrevoked
    webhook.descriptionoptionalstring
    webhook.retryrequiredobject
    webhook.retry.maxAttemptsrequiredinteger
    webhook.retry.backoffSecondsrequiredinteger
    webhook.createdByIdoptionalstringBranded Account id (acc_ prefix + 21 nano-id chars).
    webhook.consecutiveFailuresoptionalinteger
    webhook.lastSuccessAtoptionalstring · date-timeISO-8601 timestamp in UTC.
    webhook.lastFailureAtoptionalstring · date-timeISO-8601 timestamp in UTC.
    webhook.createdAtrequiredstring · date-timeISO-8601 timestamp in UTC.
    webhook.updatedAtrequiredstring · date-timeISO-8601 timestamp in UTC.
    revealrequiredobject
    reveal.idrequiredstringBranded Webhook id (wbh_ prefix + 21 nano-id chars).
    reveal.secretrequiredstringPlaintext signing secret. Returned once by POST /webhooks and POST /webhooks/:id/rotate-secret. Caller MUST store it — it cannot be retrieved later.
    reveal.secretVersionrequiredinteger
    reveal.createdAtrequiredstring · date-timeISO-8601 timestamp in UTC.
    JSON
    {  "webhook": {    "id": "wbh_uV2aLwzXMqNmKjHbPiRoT",    "organizationId": "org_pGgniw5L7CTDSZDwjDrQ2",    "url": "https://api.partner.test/ccx/webhook",    "secretCipher": "string",    "secretVersion": 1,    "headers": [      {        "key": "X-Api-Key",        "value": "sk_live_…"      }    ],    "events": [      "message.sent"    ],    "status": "active",    "description": "string",    "retry": {      "maxAttempts": 8,      "backoffSeconds": 60    },    "createdById": "acc_dEws32WQG1MgvOVa6wqXT",    "consecutiveFailures": 0,    "lastSuccessAt": "2026-04-07T17:30:00.000Z",    "lastFailureAt": "2026-04-07T17:30:00.000Z",    "createdAt": "2026-04-07T17:30:00.000Z",    "updatedAt": "2026-04-07T17:30:00.000Z"  },  "reveal": {    "id": "wbh_uV2aLwzXMqNmKjHbPiRoT",    "secret": "whsec_aB3xY9zQ1pTkLmNoPqRsTuVwXyZ123456",    "secretVersion": 1,    "createdAt": "2026-04-07T17:30:00.000Z"  }}
  • 401RFC 7807 problem details for UNAUTHORIZED (401).

  • 403RFC 7807 problem details for FORBIDDEN (403).

  • 422RFC 7807 problem details for VALIDATION_ERROR (422).

Error responses follow RFC 7807 (application/problem+json), with a stable code in the code field. See the error codes

curl example

cURL
curl -X POST 'https://api.messages.ccxcompany.co/webhooks' \  -H "Authorization: Bearer $CCX_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "url": "https://api.partner.test/ccx/webhook",    "events": [      "message.delivered",      "message.failed"    ]  }'
Required fields only. Swap the sample values for your own.

Validate an endpoint before subscribing

POST/webhooks/validate

Pre-create check for the dashboard's Validar button. Runs the same SSRF guardian the dispatcher uses, then fires a single signed webhook.ping POST (ephemeral secret) and reports reachability.

Always 200; the body's ok flag carries the verdict and reason/message describe a failure (invalid URL, non-HTTPS, SSRF-blocked, non-2xx, timeout, unreachable) so the modal can render it inline.

Request bodyapplication/json

FieldTypeDescription
urlrequiredstringEndpoint to validate. Checked for HTTPS + SSRF + a live signed webhook.ping.
headersoptionalobject[]Custom headers to include on the ping (as they would be on delivery).
headers[].keyrequiredstring
headers[].valuerequiredstring
Full body example
JSON
{  "url": "https://api.partner.test/ccx/webhook",  "headers": [    {      "key": "X-Api-Key",      "value": "sk_live_…"    }  ]}

Responses

  • 200Result of the pre-create ping. ok:true ⇒ endpoint returned 2xx; ok:false carries a reason the modal renders inline.

    Response fields and example
    FieldTypeDescription
    okrequiredboolean
    statusCodeoptionalinteger
    latencyMsoptionalinteger
    reasonoptionalstringValues:invalid_urlinsecure_schemessrf_blockednon_2xxtimeoutunreachable
    messageoptionalstring
    JSON
    {  "ok": true,  "statusCode": 200,  "latencyMs": 142,  "reason": "invalid_url",  "message": "string"}
  • 401RFC 7807 problem details for UNAUTHORIZED (401).

  • 403RFC 7807 problem details for FORBIDDEN (403).

  • 422RFC 7807 problem details for VALIDATION_ERROR (422).

Error responses follow RFC 7807 (application/problem+json), with a stable code in the code field. See the error codes

curl example

cURL
curl -X POST 'https://api.messages.ccxcompany.co/webhooks/validate' \  -H "Authorization: Bearer $CCX_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "url": "https://api.partner.test/ccx/webhook"  }'
Required fields only. Swap the sample values for your own.

Get a single webhook subscription

GET/webhooks/{id}

Returns the full record. Cross-tenant requests get 404 (Mongo Guardian filter).

Parameters

FieldInTypeDescription
idrequiredpathstringBranded Webhook id (wbh_ prefix + 21 nano-id chars).

Responses

  • 200Default Response

    Response fields and example
    FieldTypeDescription
    idrequiredstringBranded Webhook id (wbh_ prefix + 21 nano-id chars).
    organizationIdrequiredstringBranded Organization id (org_ prefix + 21 nano-id chars).
    urlrequiredstring · uri
    secretCipherrequiredstringPII_CRITICAL — encrypted webhook signing secret (JSON-serialized EncryptedBlob, AAD bound to org+webhook). Never returned in plaintext after creation.
    secretVersionrequiredinteger
    headersoptionalobject[]
    headers[].keyrequiredstring
    headers[].valuerequiredstring
    eventsrequiredstring[]
    statusrequiredstringValues:activepausedfailingrevoked
    descriptionoptionalstring
    retryrequiredobject
    retry.maxAttemptsrequiredinteger
    retry.backoffSecondsrequiredinteger
    createdByIdoptionalstringBranded Account id (acc_ prefix + 21 nano-id chars).
    consecutiveFailuresoptionalinteger
    lastSuccessAtoptionalstring · date-timeISO-8601 timestamp in UTC.
    lastFailureAtoptionalstring · date-timeISO-8601 timestamp in UTC.
    createdAtrequiredstring · date-timeISO-8601 timestamp in UTC.
    updatedAtrequiredstring · date-timeISO-8601 timestamp in UTC.
    JSON
    {  "id": "wbh_uV2aLwzXMqNmKjHbPiRoT",  "organizationId": "org_pGgniw5L7CTDSZDwjDrQ2",  "url": "https://api.partner.test/ccx/webhook",  "secretCipher": "string",  "secretVersion": 1,  "headers": [    {      "key": "X-Api-Key",      "value": "sk_live_…"    }  ],  "events": [    "message.sent"  ],  "status": "active",  "description": "string",  "retry": {    "maxAttempts": 8,    "backoffSeconds": 60  },  "createdById": "acc_dEws32WQG1MgvOVa6wqXT",  "consecutiveFailures": 0,  "lastSuccessAt": "2026-04-07T17:30:00.000Z",  "lastFailureAt": "2026-04-07T17:30:00.000Z",  "createdAt": "2026-04-07T17:30:00.000Z",  "updatedAt": "2026-04-07T17:30:00.000Z"}
  • 401RFC 7807 problem details for UNAUTHORIZED (401).

  • 403RFC 7807 problem details for FORBIDDEN (403).

  • 404RFC 7807 problem details for NOT_FOUND (404).

Error responses follow RFC 7807 (application/problem+json), with a stable code in the code field. See the error codes

curl example

cURL
curl 'https://api.messages.ccxcompany.co/webhooks/wbh_uV2aLwzXMqNmKjHbPiRoT' \  -H "Authorization: Bearer $CCX_API_KEY"
Required fields only. Swap the sample values for your own.

Patch webhook config

PATCH/webhooks/{id}

Partial update. URL is IMMUTABLE — to change the endpoint, delete + recreate (which forces a new secret). status accepts active / paused only; failing / revoked are managed by the Step 19 worker.

Parameters

FieldInTypeDescription
idrequiredpathstringBranded Webhook id (wbh_ prefix + 21 nano-id chars).

Request bodyapplication/json

FieldTypeDescription
eventsoptionalstring[]
statusoptionalstringValues:activepaused
descriptionoptionalstring
headersoptionalobject[]Replaces the full custom-header set. [] clears all.
headers[].keyrequiredstring
headers[].valuerequiredstring
retryoptionalobject
retry.maxAttemptsrequiredinteger
retry.backoffSecondsrequiredinteger
Full body example
JSON
{  "events": [    "message.sent"  ],  "status": "active",  "description": "string",  "headers": [    {      "key": "X-Api-Key",      "value": "sk_live_…"    }  ],  "retry": {    "maxAttempts": 8,    "backoffSeconds": 60  }}

Responses

  • 200Default Response

    Response fields and example
    FieldTypeDescription
    idrequiredstringBranded Webhook id (wbh_ prefix + 21 nano-id chars).
    organizationIdrequiredstringBranded Organization id (org_ prefix + 21 nano-id chars).
    urlrequiredstring · uri
    secretCipherrequiredstringPII_CRITICAL — encrypted webhook signing secret (JSON-serialized EncryptedBlob, AAD bound to org+webhook). Never returned in plaintext after creation.
    secretVersionrequiredinteger
    headersoptionalobject[]
    headers[].keyrequiredstring
    headers[].valuerequiredstring
    eventsrequiredstring[]
    statusrequiredstringValues:activepausedfailingrevoked
    descriptionoptionalstring
    retryrequiredobject
    retry.maxAttemptsrequiredinteger
    retry.backoffSecondsrequiredinteger
    createdByIdoptionalstringBranded Account id (acc_ prefix + 21 nano-id chars).
    consecutiveFailuresoptionalinteger
    lastSuccessAtoptionalstring · date-timeISO-8601 timestamp in UTC.
    lastFailureAtoptionalstring · date-timeISO-8601 timestamp in UTC.
    createdAtrequiredstring · date-timeISO-8601 timestamp in UTC.
    updatedAtrequiredstring · date-timeISO-8601 timestamp in UTC.
    JSON
    {  "id": "wbh_uV2aLwzXMqNmKjHbPiRoT",  "organizationId": "org_pGgniw5L7CTDSZDwjDrQ2",  "url": "https://api.partner.test/ccx/webhook",  "secretCipher": "string",  "secretVersion": 1,  "headers": [    {      "key": "X-Api-Key",      "value": "sk_live_…"    }  ],  "events": [    "message.sent"  ],  "status": "active",  "description": "string",  "retry": {    "maxAttempts": 8,    "backoffSeconds": 60  },  "createdById": "acc_dEws32WQG1MgvOVa6wqXT",  "consecutiveFailures": 0,  "lastSuccessAt": "2026-04-07T17:30:00.000Z",  "lastFailureAt": "2026-04-07T17:30:00.000Z",  "createdAt": "2026-04-07T17:30:00.000Z",  "updatedAt": "2026-04-07T17:30:00.000Z"}
  • 401RFC 7807 problem details for UNAUTHORIZED (401).

  • 403RFC 7807 problem details for FORBIDDEN (403).

  • 404RFC 7807 problem details for NOT_FOUND (404).

  • 422RFC 7807 problem details for VALIDATION_ERROR (422).

Error responses follow RFC 7807 (application/problem+json), with a stable code in the code field. See the error codes

curl example

cURL
curl -X PATCH 'https://api.messages.ccxcompany.co/webhooks/wbh_uV2aLwzXMqNmKjHbPiRoT' \  -H "Authorization: Bearer $CCX_API_KEY" \  -H "Content-Type: application/json" \  -d '{}'
Required fields only. Swap the sample values for your own.

Delete a webhook subscription

DELETE/webhooks/{id}

Hard delete. The encrypted secret is removed with the row.

Parameters

FieldInTypeDescription
idrequiredpathstringBranded Webhook id (wbh_ prefix + 21 nano-id chars).

Responses

  • 204Webhook removed.

  • 401RFC 7807 problem details for UNAUTHORIZED (401).

  • 403RFC 7807 problem details for FORBIDDEN (403).

  • 404RFC 7807 problem details for NOT_FOUND (404).

Error responses follow RFC 7807 (application/problem+json), with a stable code in the code field. See the error codes

curl example

cURL
curl -X DELETE 'https://api.messages.ccxcompany.co/webhooks/wbh_uV2aLwzXMqNmKjHbPiRoT' \  -H "Authorization: Bearer $CCX_API_KEY"
Required fields only. Swap the sample values for your own.

Rotate the HMAC signing secret

POST/webhooks/{id}/rotate-secret

Generates a new whsec_… secret, encrypts it with a fresh IV/tag (same AAD binding), bumps secretVersion, and returns the plaintext once. The previous secret is invalid immediately — there is no grace window in Step 13 (Step 19 may add an overlap window for zero-downtime rotation).

Parameters

FieldInTypeDescription
idrequiredpathstringBranded Webhook id (wbh_ prefix + 21 nano-id chars).

Responses

  • 200Default Response

    Response fields and example
    FieldTypeDescription
    idrequiredstringBranded Webhook id (wbh_ prefix + 21 nano-id chars).
    secretrequiredstringPlaintext signing secret. Returned once by POST /webhooks and POST /webhooks/:id/rotate-secret. Caller MUST store it — it cannot be retrieved later.
    secretVersionrequiredinteger
    createdAtrequiredstring · date-timeISO-8601 timestamp in UTC.
    JSON
    {  "id": "wbh_uV2aLwzXMqNmKjHbPiRoT",  "secret": "whsec_aB3xY9zQ1pTkLmNoPqRsTuVwXyZ123456",  "secretVersion": 1,  "createdAt": "2026-04-07T17:30:00.000Z"}
  • 401RFC 7807 problem details for UNAUTHORIZED (401).

  • 403RFC 7807 problem details for FORBIDDEN (403).

  • 404RFC 7807 problem details for NOT_FOUND (404).

Error responses follow RFC 7807 (application/problem+json), with a stable code in the code field. See the error codes

curl example

cURL
curl -X POST 'https://api.messages.ccxcompany.co/webhooks/wbh_uV2aLwzXMqNmKjHbPiRoT/rotate-secret' \  -H "Authorization: Bearer $CCX_API_KEY"
Required fields only. Swap the sample values for your own.

Stuck on a route? Talk to the people who run the API.

Sign up through the site and get a US$5 test credit.