Hoppa till innehållet

Create a webhook

POST/v1/webhooks

TypeScript sendora.webhooks.create({ url, events })

Python sendora.webhooks.create(url=..., events=...)

Key sk_… a server key

Registers a URL to receive events. The answer carries the signing secret once; every delivery is signed with it (HMAC-SHA256 over the body) so the receiver can verify Sendora sent it. A second secret can be created later to roll the first one.

Request body

  • urlstringrequired

    An https URL on a public host, without credentials or a fragment.

  • eventsarray of "delivered" | "bounced" | "deferred" | "spam_complaint" | "unsubscribed" | "cap_warning" | "cap_reached" | "inbound", at least 1

    The events to deliver; every event when left out.

  • streamIdstring (uuid)

    Limits the message events to one stream of the server; every stream when left out. Cap events concern the server and arrive either way.

  • inboundContent"full" | "reference"

    How the inbound event carries a received message: full sends the parsed message with the event; reference sends ids and sizes only, for you to fetch. full when left out.

{
  "url": "https://example.se/hooks/sendora",
  "events": [
    "delivered",
    "bounced",
    "spam_complaint"
  ]
}

Responses

201The new webhook with its signing secret, shown this once.

  • webhookIdstring (uuid)required
  • urlstringrequired

    Where the events are posted.

  • eventsarray of "delivered" | "bounced" | "deferred" | "spam_complaint" | "unsubscribed" | "cap_warning" | "cap_reached" | "inbound"required
  • streamIdstring (uuid) or nullrequired

    The one stream whose message events it receives; null for every stream.

  • enabledbooleanrequired

    False while switched off in the dashboard; nothing is queued for it then.

  • inboundContent"full" | "reference"required

    How the inbound event carries a received message: the parsed message, or a reference.

  • createdAtstring (date-time)required
  • secretsarray of objectrequired

    The live secrets, oldest first; every delivery is signed with each of them, the newest first in Sendora-Signature.

    • secretIdstring (uuid)required
    • createdAtstring (date-time)required
  • secretstringrequired

    The signing secret, shown this once.

Example (20 lines)
{
  "webhookId": "7c1e4d2a-0b9f-4a3e-8d6c-5e2f1a9b8c70",
  "url": "https://example.se/hooks/sendora",
  "events": [
    "delivered",
    "bounced",
    "spam_complaint"
  ],
  "streamId": null,
  "enabled": true,
  "inboundContent": "full",
  "createdAt": "2026-09-15T12:00:00.000Z",
  "secrets": [
    {
      "secretId": "4a5b6c7d-8e9f-4a0b-8c1d-2e3f4a5b6c7d",
      "createdAt": "2026-09-15T12:00:00.000Z"
    }
  ],
  "secret": "whsec_9f8e7d6c5b4a3f2e1d0c9b8a7f6e5d4c"
}

Errors

Every error answers error, the code, and message, a sentence for a person. A code that adds fields is shown in full below the table.

CodeStatusMeaning
invalid_request400The body, the query or a header does not match what the route takes.
unauthorized401The key is missing, malformed or revoked.
wrong_token_kind403The key is of the other kind: a server key (sk_) where an account key (ak_) is needed, or the reverse. The message names the kind the operation takes.
webhook_exists409The server already has a webhook for that URL.
stream_not_found422The streamId names no stream of this server.

invalid_request

  • issuesarray of object

    One entry per invalid field; absent when a header is wrong.

    • pathstringrequired

      The field, dotted, such as to.0.email; empty when the whole body is wrong.

    • messagestringrequired

Example

{
  "error": "invalid_request",
  "message": "The request is invalid: to.0: Invalid email address",
  "issues": [
    {
      "path": "to.0",
      "message": "Invalid email address"
    }
  ]
}

webhook_exists

  • webhookIdstring (uuid)required

    The id of the webhook the server already has for that URL.

Example

{
  "error": "webhook_exists",
  "message": "This server already has a webhook for that URL.",
  "webhookId": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}