Deadsimple Email Webhooks API

The Webhooks API from Deadsimple Email — 10 operation(s) for webhooks.

Operations 16

POST /v1/webhooks Register webhook #
GET /v1/webhooks List webhooks #
GET /v1/webhooks/{webhook_id} Get webhook #
PATCH /v1/webhooks/{webhook_id} Update webhook #
DELETE /v1/webhooks/{webhook_id} Delete webhook #
GET /v1/webhooks/{webhook_id}/config Get webhook retry configuration #
PUT /v1/webhooks/{webhook_id}/config Update webhook retry configuration #
GET /v1/webhooks/{webhook_id}/headers Get webhook custom delivery headers #
PUT /v1/webhooks/{webhook_id}/headers Replace webhook custom delivery headers #
GET /v1/webhooks/{webhook_id}/deliveries List webhook deliveries #
GET /v1/webhooks/{webhook_id}/deliveries/{delivery_id} Get a webhook delivery #
POST /v1/webhooks/{webhook_id}/deliveries/{delivery_id}/replay Replay a webhook delivery #
POST /v1/event-subscriptions Create or refresh an MCP Events subscription #
GET /v1/event-subscriptions List MCP Events subscriptions #
POST /v1/event-subscriptions/unsubscribe Remove an MCP Events subscription by identity #
DELETE /v1/event-subscriptions/{subscription_id} Delete an MCP Events subscription #

Work with this as data

Every API 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 apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • 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 API
curl "https://apis.io/api/v1/apis/deadsimple-email:deadsimple-email-webhooks-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

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.

OpenAPI Specification

deadsimple-email-webhooks-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Dead Simple Email Webhooks API
  version: 0.1.0
  description: 'Email API for AI agents. Create inboxes, send and receive email,

    manage threads, and get real-time webhook notifications.


    ## Authentication

    All authenticated endpoints use Bearer token auth:

    ```

    Authorization: Bearer dse_your_api_key

    ```


    ## Agent self-signup

    Autonomous agents can provision an account, a key, and a live inbox in one

    unauthenticated call — no human, no verification email:

    ```

    POST /v1/auth/agent-signup

    ```

    That returns a `trial` account (1 inbox, 10 sends/hour, 25 sends/day) with a

    scoped key. `POST /v1/auth/claim` attaches a verified email and moves it to

    the Free plan, keeping the same key, inboxes and history.


    ## Sign in with Dead Simple (OpenID Connect)

    `https://id.deadsimple.email` is an OpenID Connect provider where the

    identity is an agent''s inbox: `sub` is the `inbox_id`, `email` is the inbox

    address. Discovery at `/.well-known/openid-configuration`, JWKS at

    `/.well-known/jwks.json`, ES256 id_tokens, authorization-code + PKCE S256,

    RFC 7591 registration at `/register`. Open clients (client_id = the app''s

    https origin) need no registration. An agent authenticates at `/authorize`

    with `Authorization: Bearer dse_...` plus `inbox_id=`, or with an ES256

    assertion signed by a key enrolled via `/v1/inboxes/{inbox_id}/identity/keys`;

    the one-call helper `POST /v1/inboxes/{inbox_id}/identity/sign-in` wraps

    this for SDKs and the MCP server. Endpoints on the `id.deadsimple.email`

    host are tagged **Identity Provider** below and carry their own `servers`

    entry.


    ## Pay per request (no API key)

    Twelve priced routes can be called with no credentials at all. They answer

    `402 Payment Required` carrying an x402 v2 `PAYMENT-REQUIRED` header (USDC

    on Base, Polygon or Solana, `exact` scheme, PayAI facilitator; the same

    JSON is repeated in the body for v1 clients) and, on `POST /v1/inboxes`,

    a `WWW-Authenticate: Payment` challenge for Stripe''s Machine Payments

    Protocol. Retry with `PAYMENT-SIGNATURE` (x402 v2), `X-PAYMENT` (v1) or

    `Authorization: Payment ` (MPP). x402 payments are verified

    before the work and settled after it; MPP charges first and refunds if

    the work fails. Prices: inbox $1.00, send/reply/reply-all/forward $0.01,

    reads $0.005. The paying wallet keys a `payg` account, and the first paid

    inbox returns `credentials` (an API key) in the response so later reads

    and sends within the plan are free. See the reusable `402` response and

    https://deadsimple.email/pay-per-request.html.


    ## Idempotency

    POST endpoints accept an `Idempotency-Key` header. Same key within 24h

    returns the cached response without re-executing.


    ## Rate Limits

    Every response includes rate limit headers:

    - `X-RateLimit-Limit` — requests per window

    - `X-RateLimit-Remaining` — remaining requests

    - `X-RateLimit-Reset` — window reset timestamp


    On `429`, a `Retry-After` header gives the seconds to wait. Per-account

    limits scale with plan (`rate_limit_rpm`); public/unauthenticated endpoints

    are additionally limited per IP.


    ## Webhook Reliability

    Every event is a durable delivery: the first attempt runs immediately and

    failures are retried on an exponential-backoff schedule (configurable per

    webhook via `/v1/webhooks/{id}/config`; default `[1, 30, 300, 1800, 7200,

    86400]` seconds). Deliveries are HMAC-SHA256 signed on **all plans**. The

    per-webhook delivery log (`/v1/webhooks/{id}/deliveries`) records every

    attempt with status and error, and any delivery can be replayed. Endpoints

    that fail repeatedly are auto-disabled.


    ## WebSocket Streaming

    Connect to `wss://ws.deadsimple.email?api_key=dse_...` for real-time events.


    After connecting, subscribe to specific inboxes/events:

    ```json

    {"action": "subscribe", "inbox_ids": ["..."], "events": ["message.received"]}

    ```


    Events are pushed as JSON with the same payload as webhooks. Send `{"action": "ping"}`

    to keep the connection alive (10-minute idle timeout).'
  contact:
    url: https://deadsimple.email
  license:
    name: MIT
servers:
- url: https://api.deadsimple.email
  description: Production
security:
- BearerAuth: []
tags:
- name: Webhooks
paths:
  /v1/webhooks:
    post:
      summary: Register webhook
      tags:
      - Webhooks
      parameters:
      - $ref: '#/components/parameters/WorkspaceHeader'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - url
              - events
              properties:
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
                    enum:
                    - message.received
                    - message.sent
                    - message.delivered
                    - message.bounced
                    - message.complained
                    - message.rejected
                    - message.opened
                    - message.link_clicked
                    - inbox.created
                    - inbox.deleted
                    - domain.verified
                description:
                  type: string
                headers:
                  type: object
                  additionalProperties:
                    type: string
                  maxProperties: 10
                  description: 'Optional custom headers sent on every delivery attempt and every retry — for endpoints that enforce their own auth (Bearer tokens, gateway keys). Values are write-only and are masked in every response. Reserved names (Content-Type, Host, User-Agent, hop-by-hop headers, and any `X-DSE-*` header) are rejected, as are values with control characters or non-ASCII bytes. Max 10 headers, 1024 characters per value.

                    '
                endpoint_secret:
                  type: string
                  pattern: ^whsec_[A-Za-z0-9+/]+={0,2}$
                  description: 'The receiving service''s own Standard Webhooks secret (`whsec_...`), for endpoints that verify every request against it, such as Grok Automations. Each delivery then also carries `webhook-id` (the delivery id, stable across retries), `webhook-timestamp` and `webhook-signature` (`v1,<base64 HMAC-SHA256 of "id.timestamp.body">`) per standardwebhooks.com. Such receivers answer an unsigned request with HTTP 400. Write-only. A `whsec_` value supplied as `Authorization: Bearer whsec_...` in headers is treated the same way and not sent as a header.

                    '
                inbox_ids:
                  type: array
                  maxItems: 100
                  items:
                    type: string
                    format: uuid
                  description: 'Optional inbox scope. A webhook with inbox_ids fires only for events whose payload names one of those inboxes (so never for inbox-less events such as domain.verified). Every id must belong to the account. Omit or pass an empty list for an account-wide webhook. An inbox-scoped API key may only register webhooks for its own inboxes and gets them by default.

                    '
            example:
              url: https://your-app.com/webhooks/email
              events:
              - message.received
              description: Notify on new inbound email
              inbox_ids:
              - b1a7f2c0-3f44-4c2b-9d0e-6a1e2f3c4d5e
              headers:
                Authorization: Bearer eyJhbGciOi...
      responses:
        '201':
          description: Webhook created with signing secret (shown once)
      operationId: postV1Webhooks
      x-operation-id-source: derived
    get:
      summary: List webhooks
      tags:
      - Webhooks
      parameters:
      - $ref: '#/components/parameters/WorkspaceHeader'
      - name: workspace_id
        in: query
        required: false
        schema:
          type: string
          format: uuid
        description: Filter webhooks by workspace
      responses:
        '200':
          description: List of webhooks
      operationId: getV1Webhooks
      x-operation-id-source: derived
  /v1/webhooks/{webhook_id}:
    get:
      summary: Get webhook
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Webhook (signing secret never returned; header values masked)
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '404':
          description: No such webhook on this account
      operationId: getV1WebhooksByWebhookId
      x-operation-id-source: derived
    patch:
      summary: Update webhook
      description: 'Partial update. Any of url, events, description, inbox_ids, is_active and endpoint_secret may be supplied; unknown fields are ignored and at least one known field is required. `inbox_ids: []` clears the inbox scope. Setting `is_active` to true also resets the consecutive-failure counter, so an auto-disabled endpoint starts over with a full disable budget.'
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                url:
                  type: string
                  format: uri
                events:
                  type: array
                  items:
                    type: string
                description:
                  type: string
                inbox_ids:
                  type: array
                  maxItems: 100
                  items:
                    type: string
                    format: uuid
                is_active:
                  type: boolean
                endpoint_secret:
                  type: string
                  pattern: ^whsec_[A-Za-z0-9+/]+={0,2}$
                  description: 'The receiving service''s own Standard Webhooks secret (`whsec_...`), for endpoints that verify every request against it, such as Grok Automations. Each delivery then also carries `webhook-id` (the delivery id, stable across retries), `webhook-timestamp` and `webhook-signature` (`v1,<base64 HMAC-SHA256 of "id.timestamp.body">`) per standardwebhooks.com. Such receivers answer an unsigned request with HTTP 400. Write-only. A `whsec_` value supplied as `Authorization: Bearer whsec_...` in headers is treated the same way and not sent as a header. `null` removes it.

                    '
            example:
              inbox_ids:
              - b1a7f2c0-3f44-4c2b-9d0e-6a1e2f3c4d5e
              is_active: true
      responses:
        '200':
          description: Updated webhook
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Webhook'
        '400':
          description: Invalid field, unknown inbox, or nothing to update
      operationId: patchV1WebhooksByWebhookId
      x-operation-id-source: derived
    delete:
      summary: Delete webhook
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
      - $ref: '#/components/parameters/WorkspaceHeader'
      responses:
        '204':
          description: Webhook deleted
      operationId: deleteV1WebhooksByWebhookId
      x-operation-id-source: derived
  /v1/webhooks/{webhook_id}/config:
    get:
      summary: Get webhook retry configuration
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Retry/backoff configuration
      operationId: getV1WebhooksByWebhookIdConfig
      x-operation-id-source: derived
    put:
      summary: Update webhook retry configuration
      description: Tune delivery reliability. `retry_schedule` is the exponential-backoff delay (seconds) before each retry; failed deliveries are retried by the webhook-retry processor until `max_retries` is reached. Endpoints that fail `disable_on_failures` times in a row are auto-disabled.
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                max_retries:
                  type: integer
                  minimum: 0
                  maximum: 10
                retry_schedule:
                  type: array
                  items:
                    type: integer
                timeout_seconds:
                  type: integer
                  minimum: 5
                  maximum: 60
                disable_on_failures:
                  type: integer
                  minimum: 0
            example:
              max_retries: 6
              retry_schedule:
              - 1
              - 30
              - 300
              - 1800
              - 7200
              - 86400
              timeout_seconds: 30
              disable_on_failures: 50
      responses:
        '200':
          description: Updated configuration
      operationId: putV1WebhooksByWebhookIdConfig
      x-operation-id-source: derived
  /v1/webhooks/{webhook_id}/headers:
    get:
      summary: Get webhook custom delivery headers
      description: Header names installed on the webhook. Values are masked.
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Custom headers (values masked)
      operationId: getV1WebhooksByWebhookIdHeaders
      x-operation-id-source: derived
    put:
      summary: Replace webhook custom delivery headers
      description: 'Replaces the webhook''s custom headers wholesale; send `{"headers": {}}` to clear them. These headers are sent on every delivery attempt and every retry, so an endpoint that requires its own `Authorization` header can be used without polling. Values are write-only — responses mask them.'
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - headers
              properties:
                headers:
                  type: object
                  additionalProperties:
                    type: string
                  maxProperties: 10
            example:
              headers:
                Authorization: Bearer eyJhbGciOi...
      responses:
        '200':
          description: Updated headers (values masked)
      operationId: putV1WebhooksByWebhookIdHeaders
      x-operation-id-source: derived
  /v1/webhooks/{webhook_id}/deliveries:
    get:
      summary: List webhook deliveries
      description: Delivery log for a webhook (newest first). Each entry records the event, status (pending/delivered/failed), attempt count, last HTTP status and error, and next scheduled retry.
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
      - name: limit
        in: query
        required: false
        schema:
          type: integer
          default: 50
          maximum: 200
      responses:
        '200':
          description: Recent delivery attempts
      operationId: getV1WebhooksByWebhookIdDeliveries
      x-operation-id-source: derived
  /v1/webhooks/{webhook_id}/deliveries/{delivery_id}:
    get:
      summary: Get a webhook delivery
      description: A single delivery record, including the payload that was (or will be) sent.
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
      - name: delivery_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Delivery record
      operationId: getV1WebhooksByWebhookIdDeliveriesByDeliveryId
      x-operation-id-source: derived
  /v1/webhooks/{webhook_id}/deliveries/{delivery_id}/replay:
    post:
      summary: Replay a webhook delivery
      description: Re-attempt a delivery immediately, e.g. after fixing a broken endpoint. The retry budget is reset so the full backoff schedule is available again.
      tags:
      - Webhooks
      parameters:
      - name: webhook_id
        in: path
        required: true
        schema:
          type: string
      - name: delivery_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Delivery re-attempted
      operationId: postV1WebhooksByWebhookIdDeliveriesByDeliveryIdReplay
      x-operation-id-source: derived
  /v1/event-subscriptions:
    post:
      summary: Create or refresh an MCP Events subscription
      description: 'Backs `events/subscribe` on the hosted MCP server (OpenAI''s MCP Events, used by ChatGPT Work chats and dots). Idempotent: the subscription id is derived from the account, callback URL, event name and arguments, so the same request refreshes the same subscription. A new subscription, or one whose secret changed, is activated only after the callback answers a signed `{"type":"verification","challenge":...}` POST by echoing the challenge. Events are then POSTed as `{eventId, name, timestamp, data, cursor}` with Standard Webhooks headers (`webhook-id` = eventId, stable across retries) and `X-MCP-Subscription-Id`. 410 and 413 responses are not retried; a 410 deletes the subscription. Field names follow the MCP Events spec.'
      tags:
      - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              - delivery
              properties:
                name:
                  type: string
                  enum:
                  - message.received
                  - message.bounced
                arguments:
                  type: object
                  additionalProperties: false
                  description: 'Filters. `inbox_id` (both events; omitted = every inbox the credential can see, required for inbox-scoped keys), `from_contains` and `subject_contains` (message.received only, case-insensitive substrings).

                    '
                  properties:
                    inbox_id:
                      type: string
                    from_contains:
                      type: string
                      maxLength: 200
                    subject_contains:
                      type: string
                      maxLength: 200
                delivery:
                  type: object
                  required:
                  - url
                  - secret
                  properties:
                    mode:
                      type: string
                      enum:
                      - webhook
                    url:
                      type: string
                      format: uri
                      description: HTTPS callback; must resolve to public addresses.
                    secret:
                      type: string
                      description: Standard Webhooks secret, `whsec_` + base64 of 24-64 bytes.
                ttlMs:
                  type:
                  - number
                  - 'null'
                  description: 'Requested lifetime. Omitted = 30 days; capped at 90 days; null (no expiry) is granted as 90 days.

                    '
                cursor:
                  description: Accepted and ignored; these events are not replayable.
      responses:
        '201':
          description: Subscription active
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                    example: sub_3f0c2a9d4b1e8f7a6c5d4e3f2a1b0c9d
                  refreshBefore:
                    type: string
                    format: date-time
                  cursor:
                    type: 'null'
                  truncated:
                    type: boolean
        '400':
          description: Unknown event, unknown argument, bad secret or callback URL
        '422':
          description: '`callback_verification_failed`: the callback did not echo the challenge. `details[0].reason` is `challenge_failed` or `timeout`.

            '
      operationId: postV1EventSubscriptions
      x-operation-id-source: derived
    get:
      summary: List MCP Events subscriptions
      tags:
      - Webhooks
      responses:
        '200':
          description: The account's subscriptions (callback host only, never the secret)
      operationId: getV1EventSubscriptions
      x-operation-id-source: derived
  /v1/event-subscriptions/unsubscribe:
    post:
      summary: Remove an MCP Events subscription by identity
      description: Backs `events/unsubscribe`. Idempotent; removing one that does not exist succeeds.
      tags:
      - Webhooks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - name
              - delivery
              properties:
                name:
                  type: string
                arguments:
                  type: object
                delivery:
                  type: object
                  properties:
                    url:
                      type: string
                      format: uri
      responses:
        '201':
          description: Removed (empty object)
      operationId: postV1EventSubscriptionsUnsubscribe
      x-operation-id-source: derived
  /v1/event-subscriptions/{subscription_id}:
    delete:
      summary: Delete an MCP Events subscription
      tags:
      - Webhooks
      parameters:
      - name: subscription_id
        in: path
        required: true
        schema:
          type: string
      responses:
        '200':
          description: Deleted
        '404':
          description: No such subscription on this account
      operationId: deleteV1EventSubscriptionsBySubscriptionId
      x-operation-id-source: derived
components:
  parameters:
    WorkspaceHeader:
      name: X-Workspace-Id
      in: header
      required: false
      schema:
        type: string
        format: uuid
      description: Target a specific workspace. Required for account-wide API keys that want workspace-scoped results.
  schemas:
    Webhook:
      type: object
      properties:
        webhook_id:
          type: string
          format: uuid
        workspace_id:
          type: string
          format: uuid
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
            enum:
            - message.received
            - message.sent
            - message.delivered
            - message.bounced
            - message.complained
            - message.rejected
            - message.opened
            - message.link_clicked
            - inbox.created
            - inbox.deleted
            - domain.verified
        is_active:
          type: boolean
        headers:
          type: object
          additionalProperties:
            type: string
          description: Custom delivery headers, values masked (write-only).
        endpoint_secret_set:
          type: boolean
          description: 'True when deliveries are also signed with the receiver''s own Standard Webhooks secret (see endpoint_secret on create). The secret itself is write-only and never returned.

            '
        inbox_ids:
          type: array
          items:
            type: string
            format: uuid
          description: Inboxes this webhook is restricted to. Empty means every inbox.
        created_at:
          type: string
          format: date-time
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: API key (dse_...), session token (JWT), or OAuth 2.1 access token (dse_oat_..., obtained via the authorization-code + PKCE flow below)
    OAuth2:
      type: oauth2
      description: OAuth 2.1 for MCP clients. Public clients only (PKCE S256 required, no client secret). Supports RFC 7591 dynamic client registration at /oauth/register and discovery via /.well-known/oauth-authorization-server. Access tokens carry inbox/message permissions only (no billing, team, or API-key administration).
      flows:
        authorizationCode:
          authorizationUrl: https://api.deadsimple.email/oauth/authorize
          tokenUrl: https://api.deadsimple.email/oauth/token
          refreshUrl: https://api.deadsimple.email/oauth/token
          scopes:
            email: Send, receive, and manage email inboxes
    OpenIDConnect:
      type: openIdConnect
      openIdConnectUrl: https://id.deadsimple.email/.well-known/openid-configuration
      description: 'Sign in with Dead Simple. Relying parties use this to authenticate an agent by its inbox; it is not used to call the Dead Simple API itself. Scopes: openid, email, profile (open clients); owner_email, org, offline_access (registered clients only).'