Skip to content
WaaruDocs

Webhook management

Read and save one number’s callback, rotate its signing secret, send a test and control delivery with revision-aware requests.

Reviewed

Each number has one callback configuration. webhooks:read returns settings and recent health; webhooks:manage permits mutations. A read with no configuration returns {"webhook": null}. Reads never return signing secrets or encrypted values.

Save with a revision

Create with revision: null, a public HTTPS callbackUrl and one to five selectedEvents. The URL must use port 443 and have no credentials, query or fragment. Private/reserved network destinations and redirects are blocked. Updates require the current revision; callbackUrl may be omitted to keep the URL. Saving resumes ACTIVE delivery. A new or changed URL, or a paused or disabled endpoint, must pass the unsigned activation challenge before saving. An unchanged active endpoint continues without a new check. Explicitly resuming through the state endpoint with ACTIVE also verifies the receiver. A failed challenge returns 502 webhook_verification_failed without changing the configuration. See the activation exchange. Callback URLs are limited to 2,048 characters.

Selected events use uppercase configuration enums: MESSAGE_RECEIVED, MESSAGE_SENT, MESSAGE_DELIVERED, MESSAGE_READ, MESSAGE_FAILED. Actual callbacks use lowercase event types. Follow signature verification and callback examples.

Choose observation coverage

Save optional boolean observeAllOrigins: true to observe supported fresh message origins while keeping Logic Flow handling. Omission writes false, so preserve the boolean explicitly on later saves. Read and save responses return it. False retains the existing external-admitted inbound/API outbound behavior. True additionally enables supported native inbound and canonical outbound observations; coverage and excluded origins are bounded. There is no automatic historical replay or reply authority. Selected events, revision checks, signature and receiver verification controls remain in force.

webhooks:read works in either mode and after disconnection subject to lifecycle checks. webhooks:manage works in either mode but requires a connected number. Configuration never grants messages:send, releases human ownership or changes the inbound handler.

Handle health and secrets

The read's webhook object has id, revision, state, selectedEvents, observeAllOrigins, callbackUrl, consecutiveFailures, nullable lastSuccessAt, lastFailureAt, lastFailureCode, and deliveries. Up to 20 deliveries from the seven-day window expose id, eventType, status, attempts, nullable responseStatus, lastErrorCode, deliveredAt, terminalAt, and createdAt. Endpoint state is ACTIVE, DEGRADED, PAUSED or DISABLED; delivery state is PENDING, CLAIMED, DELIVERED or TERMINAL.

Only creation and rotation return signingSecret. Store it immediately in your server secret manager. Rotation returns the new revision and previousSecretExpiresAt; old and new signatures overlap for 24 hours. Never log the response. After a lost creation/rotation response, read settings and deliberately rotate once with the current revision if necessary. Do not retry rotation automatically.

Rotation, test and state requests require a positive revision. State accepts only ACTIVE or DISABLED. A revision conflict returns 409; re-read and reconcile. A test sends a real webhook.test callback and returns 200 only when the receiver accepts it. A receiver rejection or transport failure returns safe 502 webhook_test_failed. This documentation never sends a callback itself.

Public APIs do not provide multiple callback destinations or arbitrary historical replay. Resume is not replay; reconcile messages after a delivery gap.

Endpoint details

Every request uses Authorization: Bearer $WAARU_API_KEY and a connected number in Developer API mode. The scope below is required. IDs and payloads are illustrative; replace them with your own permitted resources. See authentication and common errors, size limits and retries.

GET /v1/webhook

Required scope: webhooks:read

Requires webhooks:read.

No path or query parameters.

Request

curl --fail-with-body --request GET 'https://api.waaru.app/v1/webhook' \
  --header "Authorization: Bearer $WAARU_API_KEY"

Send no request body.

Response: HTTP 200

{
  "webhook": {
    "id": "webhook_example",
    "revision": 1,
    "state": "ACTIVE",
    "selectedEvents": [
      "MESSAGE_RECEIVED"
    ],
    "callbackUrl": "https://example.com/waaru/events",
    "consecutiveFailures": 0,
    "lastSuccessAt": null,
    "lastFailureAt": null,
    "lastFailureCode": null,
    "deliveries": []
  }
}
Response body fields
FieldRequiredType and constraints
webhookYes{"type":["object","null"],"description":"Callback URL, selected events, revision and delivery health. Never contains signing secrets or encrypted values."}
Response body: complete JSON schema

Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.

{
  "type": "object",
  "properties": {
    "webhook": {
      "type": [
        "object",
        "null"
      ],
      "description": "Callback URL, selected events, revision and delivery health. Never contains signing secrets or encrypted values."
    }
  },
  "required": [
    "webhook"
  ],
  "additionalProperties": false
}

Failures use a stable code and safe message. They never expose internal exception messages, traces or provider diagnostics. Check the error reference before retrying. An inaccessible resource can return 404 even if its identifier exists elsewhere.

PUT /v1/webhook

Required scope: webhooks:manage

Requires webhooks:manage. Use null revision to create. Updates require current revision and resume the endpoint. A new signing secret is returned only upon creation.

No path or query parameters.

Request

curl --fail-with-body --request PUT 'https://api.waaru.app/v1/webhook' \
  --header "Authorization: Bearer $WAARU_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-binary @request.json

Save this illustrative JSON as request.json. The body is required.

{
  "revision": null,
  "callbackUrl": "https://example.com/waaru/events",
  "selectedEvents": [
    "MESSAGE_RECEIVED"
  ]
}
Request body fields
FieldRequiredType and constraints
revisionYes{"type":["integer","null"],"minimum":0}
callbackUrlNo{"type":"string","format":"uri","maxLength":2048,"description":"Public HTTPS URL, port 443, no credentials/query/fragment. Required for creation. DNS validation is enforced."}
selectedEventsYes{"type":"array","minItems":1,"maxItems":5} Nested fields are in the complete schema below.
observeAllOriginsNo{"type":"boolean","default":false,"description":"Explicitly observe supported native/API message origins without reply authority. Send true on every replacement PUT to retain all-origin observation; omission restores legacy API-handler events. No historical replay."}
Request body: complete JSON schema

Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.

{
  "type": "object",
  "properties": {
    "revision": {
      "type": [
        "integer",
        "null"
      ],
      "minimum": 0
    },
    "callbackUrl": {
      "type": "string",
      "format": "uri",
      "maxLength": 2048,
      "description": "Public HTTPS URL, port 443, no credentials/query/fragment. Required for creation. DNS validation is enforced."
    },
    "selectedEvents": {
      "type": "array",
      "items": {
        "enum": [
          "MESSAGE_RECEIVED",
          "MESSAGE_SENT",
          "MESSAGE_DELIVERED",
          "MESSAGE_READ",
          "MESSAGE_FAILED"
        ]
      },
      "minItems": 1,
      "maxItems": 5
    },
    "observeAllOrigins": {
      "type": "boolean",
      "default": false,
      "description": "Explicitly observe supported native/API message origins without reply authority. Send true on every replacement PUT to retain all-origin observation; omission restores legacy API-handler events. No historical replay."
    }
  },
  "required": [
    "revision",
    "selectedEvents"
  ],
  "additionalProperties": false
}

Response: HTTP 200

{
  "webhook": {
    "id": "webhook_example",
    "revision": 1,
    "state": "ACTIVE",
    "selectedEvents": [
      "MESSAGE_RECEIVED"
    ],
    "observeAllOrigins": false
  },
  "signingSecret": "illustrative-secret-store-once"
}
Response body fields
FieldRequiredType and constraints
webhookYes{"type":"object","required":["id","revision","state","selectedEvents","observeAllOrigins"],"additionalProperties":false} Nested fields are in the complete schema below.
signingSecretNo{"type":"string"}
Response body: complete JSON schema

Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.

{
  "type": "object",
  "properties": {
    "webhook": {
      "type": "object",
      "properties": {
        "id": {
          "type": "string",
          "minLength": 1,
          "maxLength": 191
        },
        "revision": {
          "type": "integer",
          "minimum": 1
        },
        "state": {
          "type": "string"
        },
        "selectedEvents": {
          "type": "array",
          "items": {
            "enum": [
              "MESSAGE_RECEIVED",
              "MESSAGE_SENT",
              "MESSAGE_DELIVERED",
              "MESSAGE_READ",
              "MESSAGE_FAILED"
            ]
          }
        },
        "observeAllOrigins": {
          "type": "boolean"
        }
      },
      "required": [
        "id",
        "revision",
        "state",
        "selectedEvents",
        "observeAllOrigins"
      ],
      "additionalProperties": false
    },
    "signingSecret": {
      "type": "string"
    }
  },
  "required": [
    "webhook"
  ],
  "additionalProperties": false
}

Failures use a stable code and safe message. They never expose internal exception messages, traces or provider diagnostics. Check the error reference before retrying. An inaccessible resource can return 404 even if its identifier exists elsewhere.

POST /v1/webhook/rotate

Required scope: webhooks:manage

Requires webhooks:manage.

No path or query parameters.

Request

curl --fail-with-body --request POST 'https://api.waaru.app/v1/webhook/rotate' \
  --header "Authorization: Bearer $WAARU_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-binary @request.json

Save this illustrative JSON as request.json. The body is required.

{
  "revision": 1
}
Request body fields
FieldRequiredType and constraints
revisionYes{"type":"integer","minimum":1}
Request body: complete JSON schema

Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.

{
  "type": "object",
  "properties": {
    "revision": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "revision"
  ],
  "additionalProperties": false
}

Response: HTTP 200

{
  "signingSecret": "illustrative-new-secret-store-once",
  "revision": 2,
  "previousSecretExpiresAt": "2026-10-02T09:00:00.000Z"
}
Response body fields
FieldRequiredType and constraints
signingSecretYes{"type":"string"}
revisionYes{"type":"integer","minimum":1}
previousSecretExpiresAtYes{"type":"string","format":"date-time"}
Response body: complete JSON schema

Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.

{
  "type": "object",
  "properties": {
    "signingSecret": {
      "type": "string"
    },
    "revision": {
      "type": "integer",
      "minimum": 1
    },
    "previousSecretExpiresAt": {
      "type": "string",
      "format": "date-time"
    }
  },
  "required": [
    "signingSecret",
    "revision",
    "previousSecretExpiresAt"
  ],
  "additionalProperties": false
}

Failures use a stable code and safe message. They never expose internal exception messages, traces or provider diagnostics. Check the error reference before retrying. An inaccessible resource can return 404 even if its identifier exists elsewhere.

POST /v1/webhook/test

Required scope: webhooks:manage

Requires webhooks:manage.

No path or query parameters.

Request

curl --fail-with-body --request POST 'https://api.waaru.app/v1/webhook/test' \
  --header "Authorization: Bearer $WAARU_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-binary @request.json

Save this illustrative JSON as request.json. The body is required.

{
  "revision": 1
}
Request body fields
FieldRequiredType and constraints
revisionYes{"type":"integer","minimum":1}
Request body: complete JSON schema

Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.

{
  "type": "object",
  "properties": {
    "revision": {
      "type": "integer",
      "minimum": 1
    }
  },
  "required": [
    "revision"
  ],
  "additionalProperties": false
}

Response: HTTP 200

{
  "delivered": true,
  "eventId": "event_example"
}
Response body fields
FieldRequiredType and constraints
deliveredYes{"const":true}
eventIdYes{"type":"string","minLength":1,"maxLength":191}
Response body: complete JSON schema

Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.

{
  "type": "object",
  "properties": {
    "delivered": {
      "const": true
    },
    "eventId": {
      "type": "string",
      "minLength": 1,
      "maxLength": 191
    }
  },
  "required": [
    "delivered",
    "eventId"
  ],
  "additionalProperties": false
}

Failures use a stable code and safe message. They never expose internal exception messages, traces or provider diagnostics. Check the error reference before retrying. An inaccessible resource can return 404 even if its identifier exists elsewhere.

POST /v1/webhook/state

Required scope: webhooks:manage

Requires webhooks:manage.

No path or query parameters.

Request

curl --fail-with-body --request POST 'https://api.waaru.app/v1/webhook/state' \
  --header "Authorization: Bearer $WAARU_API_KEY" \
  --header 'Content-Type: application/json' \
  --data-binary @request.json

Save this illustrative JSON as request.json. The body is required.

{
  "revision": 1,
  "state": "DISABLED"
}
Request body fields
FieldRequiredType and constraints
revisionYes{"type":"integer","minimum":1}
stateYes{"enum":["ACTIVE","DISABLED"]}
Request body: complete JSON schema

Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.

{
  "type": "object",
  "properties": {
    "revision": {
      "type": "integer",
      "minimum": 1
    },
    "state": {
      "enum": [
        "ACTIVE",
        "DISABLED"
      ]
    }
  },
  "required": [
    "revision",
    "state"
  ],
  "additionalProperties": false
}

Response: HTTP 200

{
  "revision": 2,
  "state": "DISABLED"
}
Response body fields
FieldRequiredType and constraints
revisionYes{"type":"integer","minimum":1}
stateYes{"enum":["ACTIVE","DISABLED"]}
Response body: complete JSON schema

Required fields appear in required. Omit optional fields unless needed. Null is accepted only where the schema includes it. Arrays describe their elements in items; variants appear in anyOf or oneOf.

{
  "type": "object",
  "properties": {
    "revision": {
      "type": "integer",
      "minimum": 1
    },
    "state": {
      "enum": [
        "ACTIVE",
        "DISABLED"
      ]
    }
  },
  "required": [
    "revision",
    "state"
  ],
  "additionalProperties": false
}

Failures use a stable code and safe message. They never expose internal exception messages, traces or provider diagnostics. Check the error reference before retrying. An inaccessible resource can return 404 even if its identifier exists elsewhere.

Next: All public API operations or authentication and scopes.

On this page