Webhooks

View as Markdown

Events sent to your endpoints, and the events log.

Verifying a delivery. Every delivery is a POST with three headers:

  • X-Dapi-Webhook-Signature: sha256=<hex>, the HMAC-SHA256 of <X-Dapi-Webhook-Timestamp>.<raw body> keyed with the endpoint secret (whsec_..., shown when the endpoint is created or its secret rotated).
  • X-Dapi-Webhook-Timestamp: the Unix time in seconds when this attempt was signed. Every retry is signed again with a new timestamp.
  • X-Dapi-Event-Id: the event id (evt_...), the same on every retry of one event.

To verify: compute the HMAC over the raw bytes you received (before parsing the JSON), compare it to the header in constant time, and refuse the request when the timestamp is more than 5 minutes away from your clock (replay tolerance). Answer with any 2xx within 10 seconds; failed deliveries are retried with backoff for 24 hours, so deduplicate on the event id.

This page covers the event object and the webhook endpoint object.

The event object

Something that happened in your account. Sent to your webhook endpoints and kept in the events log for 30 days.

Attributes

  • idstring

    Unique identifier of the event, prefixed with evt_. Treat it as an opaque string: store it and compare it, never parse it.

  • objectstring

    Names the type of this object. Always event.

  • typeEventType

    What happened. See the webhooks section for each event and its payload.

    Possible valuesorder.createdorder.paidorder.assignedorder.in_productionorder.shippedorder.completedorder.on_holdorder.rejectedorder.returnedorder.cancelledcheckout.expiredqc_ticket.needs_your_actionqc_ticket.resolvedproduct.approvedproduct.rejectedstock_batch.purchasedstock_batch.completedstock.lowquote.readyquote.failedquote.approvedquote.rejectedapproval_request.approvedapproval_request.changes_requested
  • created_attimestamp

    Point in time, ISO 8601 in UTC.

  • dataobject

    The object the event is about, as it is now, and what changed. Each event type documents its exact shape in the webhooks section.

The event object
{
  ,
  ,
  ,
  ,
  
}

List events

GET/v2/events

Credentials
An API key with the webhooks:read scope (full access includes every scope). A signed-in dashboard session works too.
For
Designers and Integrators
Operation id
list_events

Lists the events of the last 30 days, newest first, whether or not they were delivered. Use it to catch up after downtime or to reconcile what your endpoint received. Integrators see their own event types only (orders, checkouts, quotes and onboarding); get_event_deliveries shows where each event was sent.

Query parameters

  • typearray of EventType

    Only these event types. Repeat for several.

    Possible valuesorder.createdorder.paidorder.assignedorder.in_productionorder.shippedorder.completedorder.on_holdorder.rejectedorder.returnedorder.cancelledcheckout.expiredqc_ticket.needs_your_actionqc_ticket.resolvedproduct.approvedproduct.rejectedstock_batch.purchasedstock_batch.completedstock.lowquote.readyquote.failedquote.approvedquote.rejectedapproval_request.approvedapproval_request.changes_requested
  • objectstring

    Only events about this object, by its id, for example an ord_ id.

  • created_aftertimestamp

    Only events after this time.

  • created_beforetimestamp

    Only events before this time.

  • limitinteger

    How many results per page, from 1 to 100.

    Range 1 to 100. Defaults to 20.
  • cursorstring

    Where to continue from: the next_cursor of the previous page. Omit for the first page.

Returns200 OK

A page of events.

Responses

  • 200

    A page of events.

  • 400

    The request is invalid. details.errors lists each problem (validation_failed), or an expand[] path is unknown (invalid_expand).

  • 401

    Credentials are missing (missing_credentials), the API key is unknown or inactive (invalid_api_key), the session expired (invalid_session), or both a key and a session were sent (ambiguous_credentials).

  • 403

    The credentials are valid but not allowed to do this: a scope is missing (missing_scope), the endpoint is for another kind of account (actor_not_allowed) or another kind of credential (credential_not_allowed), the account may no longer use the API (account_blacklisted), or the account must change something first, in which case details.fix_url links to the dashboard page that fixes it.

  • 429

    Too many requests (rate_limited): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the Retry-After header, then retry; details has limit, window_seconds, reset_at and retry_after_seconds.

Errors use the error object.

GET/v2/events
curl "https://public-api.dapi.digital/v2/events" \
  -H "x-api-key: $DAPI_API_KEY"
Response
{
  "object": "list",
  "data": [
    {
      "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
      "object": "event",
      "type": "order.shipped",
      "created_at": "2026-10-14T10:02:00Z",
      "data": {
        "object": {
          "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
          "object": "order",
          "number": "PV-SCN-1701A372",
          "channel": "storefront",
          "status": "shipped",
          "customer": {
            "name": "Ana Popescu",
            "email": "ana@example.com",
            "phone": null,
            "company": null
          },
          "shipping_address": {
            "formatted": "Str. Test 1, Cluj, RO",
            "city": "Cluj",
            "country": "Romania",
            "country_code": "RO"
          },
          "charges": {
            "paid_by": "buyer",
            "items": {
              "amount": 1800,
              "currency": "usd"
            },
            "shipping": {
              "amount": 600,
              "currency": "usd"
            },
            "vat": {
              "amount": {
                "amount": 0,
                "currency": "usd"
              },
              "rate": 0,
              "included": false,
              "country": null
            },
            "sample_credit": null,
            "total": {
              "amount": 2400,
              "currency": "usd"
            }
          },
          "earnings": {
            "manufacturing": {
              "amount": 900,
              "currency": "usd"
            },
            "platform_fee": {
              "amount": 300,
              "currency": "usd"
            },
            "payment_processing": {
              "amount": 102,
              "currency": "usd"
            },
            "refunded": {
              "amount": 0,
              "currency": "usd"
            },
            "net": {
              "amount": 498,
              "currency": "usd"
            }
          },
          "shipping": {
            "tier": "small",
            "carrier": null,
            "tracking_number": null,
            "shipped_at": null
          },
          "external": null,
          "customer_notes": null,
          "qc": {
            "open_tickets": 1,
            "blocks_shipping": true
          },
          "created_at": "2026-10-09T08:39:29Z",
          "updated_at": "2026-10-09T08:40:02Z",
          "item_count": 1,
          "items": [
            {
              "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
              "object": "order_item",
              "product": {
                "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "product",
                "name": "Clicker"
              },
              "variant": {
                "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
                "object": "variant",
                "name": "Clicker Blue"
              },
              "sku": "CLICK-BLUE",
              "quantity": 1,
              "unit_price": {
                "amount": 1800,
                "currency": "usd"
              },
              "total": {
                "amount": 1800,
                "currency": "usd"
              },
              "stock_used": [],
              "custom_file": null,
              "fulfilled_from_stock": false
            }
          ],
          "refunds": []
        },
        "previous": {
          "status": "paid"
        },
        "region": null
      }
    }
  ],
  "has_more": false,
  "next_cursor": null
}

Get an event

GET/v2/events/{id}

Credentials
An API key with the webhooks:read scope (full access includes every scope). A signed-in dashboard session works too.
For
Designers and Integrators
Operation id
get_event

Returns one event from the last 30 days, as it was sent. Integrators read their own event types only.

Path parameters

  • idstringRequired

    Unique identifier of the event, prefixed with evt_. Treat it as an opaque string: store it and compare it, never parse it.

Returns200 OK

The event. Returns the event object.

Responses

  • 200

    The event.

  • 401

    Credentials are missing (missing_credentials), the API key is unknown or inactive (invalid_api_key), the session expired (invalid_session), or both a key and a session were sent (ambiguous_credentials).

  • 403

    The credentials are valid but not allowed to do this: a scope is missing (missing_scope), the endpoint is for another kind of account (actor_not_allowed) or another kind of credential (credential_not_allowed), the account may no longer use the API (account_blacklisted), or the account must change something first, in which case details.fix_url links to the dashboard page that fixes it.

  • 404

    No event with this id in the last 30 days (event_not_found).

  • 429

    Too many requests (rate_limited): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the Retry-After header, then retry; details has limit, window_seconds, reset_at and retry_after_seconds.

Errors use the error object.

GET/v2/events/{id}
curl "https://public-api.dapi.digital/v2/events/evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu" \
  -H "x-api-key: $DAPI_API_KEY"
Response
{
  "id": "evt_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
  "object": "event",
  "type": "order.shipped",
  "created_at": "2026-10-14T10:02:00Z",
  "data": {
    "object": {
      "id": "ord_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
      "object": "order",
      "number": "PV-SCN-1701A372",
      "channel": "storefront",
      "status": "shipped",
      "customer": {
        "name": "Ana Popescu",
        "email": "ana@example.com",
        "phone": null,
        "company": null
      },
      "shipping_address": {
        "formatted": "Str. Test 1, Cluj, RO",
        "city": "Cluj",
        "country": "Romania",
        "country_code": "RO"
      },
      "charges": {
        "paid_by": "buyer",
        "items": {
          "amount": 1800,
          "currency": "usd"
        },
        "shipping": {
          "amount": 600,
          "currency": "usd"
        },
        "vat": {
          "amount": {
            "amount": 0,
            "currency": "usd"
          },
          "rate": 0,
          "included": false,
          "country": null
        },
        "sample_credit": null,
        "total": {
          "amount": 2400,
          "currency": "usd"
        }
      },
      "earnings": {
        "manufacturing": {
          "amount": 900,
          "currency": "usd"
        },
        "platform_fee": {
          "amount": 300,
          "currency": "usd"
        },
        "payment_processing": {
          "amount": 102,
          "currency": "usd"
        },
        "refunded": {
          "amount": 0,
          "currency": "usd"
        },
        "net": {
          "amount": 498,
          "currency": "usd"
        }
      },
      "shipping": {
        "tier": "small",
        "carrier": null,
        "tracking_number": null,
        "shipped_at": null
      },
      "external": null,
      "customer_notes": null,
      "qc": {
        "open_tickets": 1,
        "blocks_shipping": true
      },
      "created_at": "2026-10-09T08:39:29Z",
      "updated_at": "2026-10-09T08:40:02Z",
      "item_count": 1,
      "items": [
        {
          "id": "itm_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
          "object": "order_item",
          "product": {
            "id": "prd_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
            "object": "product",
            "name": "Clicker"
          },
          "variant": {
            "id": "var_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
            "object": "variant",
            "name": "Clicker Blue"
          },
          "sku": "CLICK-BLUE",
          "quantity": 1,
          "unit_price": {
            "amount": 1800,
            "currency": "usd"
          },
          "total": {
            "amount": 1800,
            "currency": "usd"
          },
          "stock_used": [],
          "custom_file": null,
          "fulfilled_from_stock": false
        }
      ],
      "refunds": []
    },
    "previous": {
      "status": "paid"
    },
    "region": null
  }
}

The webhook endpoint object

A URL that receives the events it subscribes to. Endpoints and their secrets are managed in the dashboard; the secret is never returned by the API.

Attributes

  • idstring

    Unique identifier of the webhook endpoint, prefixed with whe_. Treat it as an opaque string: store it and compare it, never parse it.

  • objectstring

    Names the type of this object. Always webhook_endpoint.

  • urlstring

    Where events are posted.

  • eventsarray of EventType

    The event types this endpoint receives.

    Possible valuesorder.createdorder.paidorder.assignedorder.in_productionorder.shippedorder.completedorder.on_holdorder.rejectedorder.returnedorder.cancelledcheckout.expiredqc_ticket.needs_your_actionqc_ticket.resolvedproduct.approvedproduct.rejectedstock_batch.purchasedstock_batch.completedstock.lowquote.readyquote.failedquote.approvedquote.rejectedapproval_request.approvedapproval_request.changes_requested
  • payload_versionstring

    v2 sends the event envelope documented here. v1 keeps the payload of the previous API, for integrations built on it.

    Possible valuesv1v2
  • statusstring

    Disabled endpoints receive nothing.

    Possible valuesenableddisabled
  • last_deliveryobject or null

    How the latest delivery went. Null before the first one.

  • created_attimestamp

    Point in time, ISO 8601 in UTC.

The webhook endpoint object
{
  ,
  ,
  ,
  ,
  ,
  ,
  ,
  
}

List webhook endpoints

GET/v2/webhook-endpoints

Credentials
An API key with the webhooks:read scope (full access includes every scope). A signed-in dashboard session works too.
For
Designers and Integrators
Operation id
list_webhook_endpoints

Lists your webhook endpoints, the events each receives and how its last delivery went. Designers create and edit endpoints in the dashboard; integrators in the dashboard too, through the session-only endpoint operations.

Query parameters

  • limitinteger

    How many results per page, from 1 to 100.

    Range 1 to 100. Defaults to 20.
  • cursorstring

    Where to continue from: the next_cursor of the previous page. Omit for the first page.

Returns200 OK

A page of endpoints.

Responses

  • 200

    A page of endpoints.

  • 400

    The request is invalid. details.errors lists each problem (validation_failed), or an expand[] path is unknown (invalid_expand).

  • 401

    Credentials are missing (missing_credentials), the API key is unknown or inactive (invalid_api_key), the session expired (invalid_session), or both a key and a session were sent (ambiguous_credentials).

  • 403

    The credentials are valid but not allowed to do this: a scope is missing (missing_scope), the endpoint is for another kind of account (actor_not_allowed) or another kind of credential (credential_not_allowed), the account may no longer use the API (account_blacklisted), or the account must change something first, in which case details.fix_url links to the dashboard page that fixes it.

  • 429

    Too many requests (rate_limited): the limit is 500 per minute per API key (or signed-in user). Wait for the number of seconds in the Retry-After header, then retry; details has limit, window_seconds, reset_at and retry_after_seconds.

Errors use the error object.

GET/v2/webhook-endpoints
curl "https://public-api.dapi.digital/v2/webhook-endpoints" \
  -H "x-api-key: $DAPI_API_KEY"
Response
{
  "object": "list",
  "data": [
    {
      "id": "whe_bfP0GSqKoZFrD72H5Os0rc7HV9XP6eQJu",
      "object": "webhook_endpoint",
      "url": "https://example.com/webhooks/dapi",
      "events": [
        "order.shipped",
        "order.completed",
        "qc_ticket.needs_your_action"
      ],
      "payload_version": "v2",
      "status": "enabled",
      "last_delivery": {
        "succeeded": true,
        "status_code": 200,
        "at": "2026-10-14T10:02:01Z"
      },
      "created_at": "2026-10-01T12:00:00Z"
    }
  ],
  "has_more": false,
  "next_cursor": null
}