AhaSend · AsyncAPI Specification

Ahasend Webhooks

Version

View Spec View on GitHub EmailTransactional EmailDeveloper ToolsSMTPWebhookAsyncAPIEvents

AsyncAPI Specification

Raw ↑
generated: '2026-08-30'
method: searched
source: https://ahasend.com/docs/webhooks.yaml
sources:
  - https://ahasend.com/docs/webhooks.yaml
  - https://ahasend.com/docs/api-reference/webhooks/index.md
  - https://ahasend.com/docs/api-reference/webhooks/security.md
  - https://ahasend.com/docs/api-reference/webhooks/retry-policy.md
  - https://ahasend.com/docs/integrations/routing.md
provider: AhaSend
providerId: ahasend
description: >-
  AhaSend's event surface. AhaSend publishes NO AsyncAPI document; it publishes a second OpenAPI
  3.1 document dedicated to its webhooks, using the 3.1 top-level `webhooks` object. That document
  is saved verbatim beside this file as asyncapi/ahasend-webhooks-openapi.yaml, and this manifest
  is the readable catalog of what it contains.
spec:
  type: OpenAPI 3.1 webhooks document
  asyncapi: false
  title: AhaSend Webhooks
  version: 2.0.0
  file: asyncapi/ahasend-webhooks-openapi.yaml
  remote: https://ahasend.com/docs/webhooks.yaml
  event_count: 11
transport: HTTPS POST to a subscriber-configured URL
content_type: application/json
management_api: openapi/ahasend-webhooks-api-openapi.yml
security:
  standard: Standard Webhooks
  spec_url: https://github.com/standard-webhooks/standard-webhooks/blob/main/spec/standard-webhooks.md
  headers:
    webhook-id: Unique event identifier; AhaSend recommends using it as the consumer's idempotency key.
    webhook-timestamp: Unix timestamp of the send; reject deliveries outside your tolerance window in either direction.
    webhook-signature: HMAC-SHA256 over `id.timestamp.body`.
  key_handling_caveat: >-
    The HMAC key is the LITERAL UTF-8 bytes of the webhook secret, including its prefix. Standard
    Webhooks libraries that Base64-decode the secret in their default constructor derive a
    different key and reject valid AhaSend deliveries — use raw-key mode (Go `NewWebhookRaw`,
    JavaScript `new Webhook(secret, { format: "raw" })`).
  verify_against: raw request body bytes, before JSON parsing
delivery:
  success_criteria: any 2xx from the subscriber endpoint
  retries: 6 attempts over 16+ minutes
  backoff: 'unsuccessful attempts ^ 4, with randomized ±10% jitter'
  auto_disable: >-
    After 100 consecutive failures the webhook is disabled automatically and every account member
    is notified by email.
  duplicate_semantics: >-
    Events can be retried, so handlers must be idempotent — AhaSend publishes a guide on exactly
    this failure mode (https://ahasend.com/blog/why-your-webhook-fired-twice-handling-duplicate-events/).
scoping:
  scope_values: [global, scoped]
  note: >-
    A scoped webhook requires a non-empty `domains` array and write permission on every listed
    domain; a global webhook requires webhooks:write:all and returns `domains: []`.
events:
  - name: message.reception
    group: Message Events
    summary: Message Reception Event
    payload_schema: MessageReceptionWebhookPayload
    flag: on_reception
  - name: message.delivered
    group: Message Events
    summary: Message Delivered Event
    payload_schema: MessageDeliveredWebhookPayload
    flag: on_delivered
  - name: message.transient_error
    group: Message Events
    summary: Message Transient Error Event (deferred)
    payload_schema: MessageTransientErrorWebhookPayload
    flag: on_transient_error
  - name: message.failed
    group: Message Events
    summary: Message Failed Event
    payload_schema: MessageFailedWebhookPayload
    flag: on_failed
  - name: message.bounced
    group: Message Events
    summary: Message Bounced Event
    payload_schema: MessageBouncedWebhookPayload
    flag: on_bounced
  - name: message.suppressed
    group: Message Events
    summary: Message Suppressed Event
    payload_schema: MessageSuppressedWebhookPayload
    flag: on_suppressed
  - name: message.opened
    group: Message Events
    summary: Message Opened Event
    payload_schema: MessageOpenedWebhookPayload
    flag: on_opened
  - name: message.clicked
    group: Message Events
    summary: Message Clicked Event
    payload_schema: MessageClickedWebhookPayload
    flag: on_clicked
  - name: suppression.created
    group: Suppression Events
    summary: Suppression Created Event
    payload_schema: SuppressionWebhookPayload
    flag: on_suppression_created
  - name: domain.dns_error
    group: Domain Events
    summary: Domain DNS Error Event
    payload_schema: DomainWebhookPayload
    flag: on_dns_error
  - name: message.routing
    group: Route Events
    summary: Inbound Message Route Event
    payload_schema: RouteWebhookPayload
    note: >-
      Inbound routing rather than outbound delivery — the parsed inbound message (latest reply
      separated from quoted text and signature, attachments and headers included) POSTed to the
      route's URL. Signed the same way; its own retry policy is documented separately. Attachments
      carry a `disposition` field distinguishing a real attachment from an inline embedded image.
testing:
  sandbox: >-
    Sandbox mode fires every event normally, and `sandbox_result` (deliver/bounce/defer/fail/
    suppress) selects which ones fire — a complete event pipeline can be exercised without
    sending mail.
  local: >-
    The CLI streams live webhook and inbound-route events to localhost over WebSocket, so no
    public endpoint is needed during development.
gap: >-
  No AsyncAPI document. The webhook contract is machine-readable, but as an OpenAPI 3.1 `webhooks`
  object, which most event-oriented tooling does not read.
maintainers:
  - FN: Kin Lane
    email: kin@apievangelist.com

Work with this as data

Every AsyncAPI spec here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for asyncapi

4 MCP tools reach this
  • find_asyncapisBrowse and filter every AsyncAPI spec in the catalog.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This AsyncAPI spec
curl "https://apis.io/api/v1/asyncapis/ahasend-webhooks"
All asyncapi
curl "https://apis.io/api/v1/asyncapis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.