RaabtaHQ
Create a webhook endpoint

Create a webhook endpoint

POST/api/v1/webhooks

scope webhooks:manageIdempotent

Subscribes a URL to one or more event types. The response carries the signing secret ONCE — store it; it cannot be retrieved later (rotate to get a new one). The URL must be HTTPS, carry no credentials, and resolve to a public address (422 url_not_allowed says which rule failed). At most 10 endpoints per account (422 endpoint_limit_reached).

Body

application/json
NameTypeDescription
urlrequired
stringHTTPS URL to POST to. See the URL rules
eventsrequired
string[]Event types to subscribe to; at least one
descriptionoptional
string | nullA note for the team
enabledoptional
booleanDefaults to true
Request
curl -X POST "https://your-crm.example.com/api/v1/webhooks" \
  -H "Authorization: Bearer $RAABTA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 6f1c2a9e-3b4d-4c5e-8f7a-9b0c1d2e3f4a" \
  -d '{
    "url": "https://example.com/hooks/raabta",
    "events": [
      "contact.created",
      "contact.updated",
      "deal.stage_changed"
    ],
    "description": "Sync contacts and deals into our warehouse"
  }'
Response · 201 Created
{
  "data": {
    "endpoint": {
      "id": "3a4b5c6d-7e8f-4a9b-8c0d-1e2f3a4b5c6d",
      "url": "https://example.com/hooks/raabta",
      "description": "Sync contacts and deals into our warehouse",
      "events": [
        "contact.created",
        "contact.updated",
        "deal.stage_changed"
      ],
      "enabled": true,
      "api_version": "2026-09-01",
      "secret_last4": "a9Fq",
      "consecutive_failures": 0,
      "disabled_at": null,
      "disabled_reason": null,
      "last_success_at": "2026-09-02T10:06:00.000Z",
      "last_error": null,
      "last_error_at": null,
      "created_at": "2026-09-01T08:00:00.000Z",
      "updated_at": "2026-09-02T10:06:00.000Z"
    },
    "secret": "whsec_3Qw9xGf4Lk2mN8pRtV6yZbCdEfGhJkLmNoPqRsTuVwXa9Fq"
  }
}

Response

Wrapped in { data: … }
NameTypeDescription
endpointrequired
object
endpoint.idrequired
string (uuid)UUID
endpoint.urlrequired
stringWhere events are POSTed
endpoint.descriptionrequired
string | null
endpoint.eventsrequired
string[]Subscribed event types
endpoint.enabledrequired
booleanfalse = nothing is delivered; pending deliveries wait
endpoint.api_versionrequired
stringThe payload version this endpoint receives
endpoint.secret_last4required
stringThe last four characters of the signing secret
endpoint.consecutive_failuresrequired
integerUnbroken run of failed deliveries; reset to 0 on success
endpoint.disabled_atrequired
string (date-time) | null
endpoint.disabled_reasonrequired
string | nullWhy it is off — `auto: …` when the platform switched it off
endpoint.last_success_atrequired
string (date-time) | null
endpoint.last_errorrequired
string | null
endpoint.last_error_atrequired
string (date-time) | null
endpoint.created_atrequired
string (date-time)ISO 8601 timestamp
endpoint.updated_atrequired
string (date-time)ISO 8601 timestamp
secretrequired
stringThe signing secret — shown ONCE. Store it; it cannot be retrieved later

Errors

StatusCodeWhen
400bad_requestThe request could not be parsed: malformed JSON, an invalid cursor, or a query parameter of the wrong shape.
400validation_errorThe body or query failed validation. `details` lists each failing field with a `path` and a `message`.
401unauthorizedNo usable API key: the Authorization header is missing or malformed, or the key is unknown, revoked or expired. The three are deliberately indistinguishable.
403forbiddenThe key is valid but lacks the scope this endpoint requires, or the request came from an address outside the key’s IP allowlist. The message says which.
403account_suspendedThe account this key belongs to is suspended. Rotating the key will not help; contact support.
409conflictThe request conflicts with current state: a duplicate phone number on create, an invalid state transition, or an idempotent request that is still in flight.
422unprocessableThe request was well-formed but cannot be carried out. `reason` is a stable string saying why (for example `outside_window` or `stage_not_in_pipeline`).
429rate_limitedThe per-key budget, or the per-IP budget for failed authentication, is exhausted. Honour `Retry-After` before retrying.
500internalSomething failed on our side. Safe to retry with the same Idempotency-Key; quote `request_id` if it persists.