> ## Documentation Index
> Fetch the complete documentation index at: https://docs.daya.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks Overview

> Real-time event notifications for Daya Stocks activity

## What are Webhooks?

Webhooks let you receive real-time HTTP notifications when key events happen on your Daya Stocks account, so you never have to poll the API for order or transfer status.

<Note>
  Webhooks are the recommended way to track order execution, deposits, and withdrawals. They deliver updates in real time and cut down on API load.
</Note>

Webhook endpoints are managed from the [Daya Dashboard](https://dashboard.daya.co): create, pause, rotate, and delete them in your workspace's Webhooks page. Configuration is deliberately not exposed on the API-key surface, so a leaked key cannot redirect your event stream.

## Supported Events

| Event                  | Description             | When Triggered                                      |
| ---------------------- | ----------------------- | --------------------------------------------------- |
| `order.filled`         | Order completely filled | A trade reaches its terminal filled state           |
| `order.failed`         | Order failed            | An order could not be executed and is terminal      |
| `deposit.completed`    | Deposit settled         | A confirmed deposit was credited to your balance    |
| `withdrawal.completed` | Withdrawal settled      | A withdrawal reached its terminal completed state   |
| `withdrawal.failed`    | Withdrawal failed       | A withdrawal could not be completed and is terminal |

<Info>
  Events fire only on terminal transitions. The rail is carried inside `data.method` (`onchain` or `bank_transfer`) and the flow direction in `data.direction` (`in` or `out`), so event names are not split per rail.
</Info>

## Managing Webhooks

<Steps>
  <Step title="Create an endpoint">
    In the Dashboard, open your workspace's **Webhooks** page and add your HTTPS `url` with the `events` you want. The signing `secret` is shown **only once**.
  </Step>

  <Step title="Store the secret">
    Save the secret in a secure store. You use it to verify every delivery. It is not retrievable later.
  </Step>

  <Step title="Verify and acknowledge">
    Verify the `X-Webhook-Signature` on each request (see [Verification](/stocks/webhooks/verification)) and return a 2xx status.
  </Step>

  <Step title="Rotate when needed">
    Pause the webhook in the Dashboard, rotate it, copy the new secret, update your receiver, then re-enable delivery. Reconcile API state for the paused interval.
  </Step>
</Steps>

<Warning>
  Webhook URLs must use HTTPS in production. The secret is returned only on create and rotate. If you lose it, rotate to get a new one.
</Warning>

## HTTP Headers

Every webhook request includes the following headers:

| Header                | Description                                 | Example                                |
| --------------------- | ------------------------------------------- | -------------------------------------- |
| `Content-Type`        | Always `application/json`                   | `application/json`                     |
| `X-Webhook-Signature` | HMAC-SHA256 signature with `sha256=` prefix | `sha256=a8f5f167f44f...`               |
| `X-Webhook-Event`     | Event type that triggered this delivery     | `order.filled`                         |
| `X-Webhook-ID`        | Unique event identifier (UUID)              | `550e8400-e29b-41d4-a716-446655440000` |
| `X-Webhook-Timestamp` | When the event occurred (RFC3339)           | `2026-01-14T15:08:15Z`                 |
| `User-Agent`          | Identifies Daya as the sender               | `Daya-Webhook/1.0`                     |

## Webhook Payload

All webhook events share the same envelope:

```json theme={"dark"}
{
  "event_id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "order.filled",
  "timestamp": "2026-01-14T15:08:15Z",
  "data": {
    "id": "9f1c2d3e-...",
    "status": "filled",
    "side": "buy",
    "symbol": "AAPL",
    "notional_usd": "100.00",
    "fee_usd": "0.50",
    "executed_price": "195.00",
    "created_at": "2026-01-14T15:06:30Z",
    "completed_at": "2026-01-14T15:08:15Z"
  }
}
```

### Envelope Fields

<ParamField body="event_id" type="string">
  Unique identifier for this event (UUID). Use it to deduplicate deliveries.
</ParamField>

<ParamField body="type" type="string">
  Event type: `order.filled`, `order.failed`, `deposit.completed`, `withdrawal.completed`, or `withdrawal.failed`.
</ParamField>

<ParamField body="timestamp" type="string">
  When the event occurred (RFC3339).
</ParamField>

<ParamField body="data" type="object">
  Event-specific payload. Order events carry order fields; deposit and withdrawal events carry transaction fields.
</ParamField>

## Event Payloads

<Tabs>
  <Tab title="order.filled">
    **Sent when:** an order reaches its terminal filled state. `symbol` is the customer-facing ticker.

    ```json theme={"dark"}
    {
      "event_id": "550e8400-e29b-41d4-a716-446655440000",
      "type": "order.filled",
      "timestamp": "2026-01-14T15:08:15Z",
      "data": {
        "id": "9f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
        "status": "filled",
        "side": "buy",
        "symbol": "AAPL",
        "notional_usd": "100.00",
        "fee_usd": "0.50",
        "executed_price": "195.00",
        "created_at": "2026-01-14T15:06:30Z",
        "completed_at": "2026-01-14T15:08:15Z"
      }
    }
    ```
  </Tab>

  <Tab title="order.failed">
    **Sent when:** an order could not be executed and is terminal.

    ```json theme={"dark"}
    {
      "event_id": "550e8400-e29b-41d4-a716-446655440001",
      "type": "order.failed",
      "timestamp": "2026-01-14T15:06:31Z",
      "data": {
        "id": "9f1c2d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
        "status": "failed",
        "side": "buy",
        "symbol": "AAPL",
        "notional_usd": "100.00",
        "failure_reason": "market_closed",
        "created_at": "2026-01-14T15:06:30Z"
      }
    }
    ```
  </Tab>

  <Tab title="deposit.completed">
    **Sent when:** a confirmed deposit is credited to your balance. `method` is `onchain` or `bank_transfer`; `direction` is `in`.

    ```json theme={"dark"}
    {
      "event_id": "660e8400-e29b-41d4-a716-446655440000",
      "type": "deposit.completed",
      "timestamp": "2026-06-05T12:00:09Z",
      "data": {
        "id": "11111111-1111-1111-1111-111111111111",
        "type": "deposit",
        "method": "bank_transfer",
        "direction": "in",
        "status": "completed",
        "asset": "NGN",
        "amount": "160000.00",
        "amount_usd": "100.00",
        "provider_ref": "PSTK-REF-9001",
        "created_at": "2026-06-05T11:59:30Z",
        "completed_at": "2026-06-05T12:00:09Z"
      }
    }
    ```
  </Tab>

  <Tab title="withdrawal.completed">
    **Sent when:** a withdrawal reaches its terminal completed state. `direction` is `out`.

    ```json theme={"dark"}
    {
      "event_id": "770e8400-e29b-41d4-a716-446655440000",
      "type": "withdrawal.completed",
      "timestamp": "2026-06-05T13:00:09Z",
      "data": {
        "id": "22222222-2222-2222-2222-222222222222",
        "type": "withdrawal",
        "method": "bank_transfer",
        "direction": "out",
        "status": "completed",
        "asset": "NGN",
        "amount": "80000.00",
        "amount_usd": "50.00",
        "fee_amount": "500.00",
        "fee_currency": "NGN",
        "provider_ref": "PSTK-PAYOUT-7788",
        "created_at": "2026-06-05T12:59:30Z",
        "completed_at": "2026-06-05T13:00:09Z"
      }
    }
    ```
  </Tab>

  <Tab title="withdrawal.failed">
    **Sent when:** a withdrawal could not be completed and is terminal.

    ```json theme={"dark"}
    {
      "event_id": "880e8400-e29b-41d4-a716-446655440000",
      "type": "withdrawal.failed",
      "timestamp": "2026-06-05T13:05:09Z",
      "data": {
        "id": "22222222-2222-2222-2222-222222222222",
        "type": "withdrawal",
        "method": "bank_transfer",
        "direction": "out",
        "status": "failed",
        "asset": "NGN",
        "amount": "80000.00",
        "amount_usd": "50.00",
        "failure_reason": "bank_rejected",
        "created_at": "2026-06-05T13:04:30Z"
      }
    }
    ```
  </Tab>
</Tabs>

## Delivery Guarantees

<AccordionGroup>
  <Accordion title="At-least-once delivery">
    Webhooks may be delivered more than once. Deduplicate using `event_id`.
  </Accordion>

  <Accordion title="Order not guaranteed">
    Events may arrive out of order. Use `timestamp` and the resource status to reconcile client-side.
  </Accordion>

  <Accordion title="Retry behavior">
    If your endpoint returns a non-2xx status or times out, Daya retries with exponential backoff across 10 attempts:

    | Attempt | Delay After Previous |
    | ------- | -------------------- |
    | 1       | 10 seconds           |
    | 2       | 30 seconds           |
    | 3       | 1 minute             |
    | 4       | 5 minutes            |
    | 5       | 15 minutes           |
    | 6       | 30 minutes           |
    | 7       | 1 hour               |
    | 8       | 2 hours              |
    | 9       | 4 hours              |
    | 10      | 8 hours              |
  </Accordion>

  <Accordion title="Timeout">
    Your endpoint must respond within 30 seconds. Slower responses time out and trigger a retry.
  </Accordion>

  <Accordion title="Auto-disable">
    A webhook is disabled automatically after 10 consecutive delivery failures. Fix the endpoint, then re-enable it from the Dashboard's Webhooks page.
  </Accordion>
</AccordionGroup>

## Delivery Statuses

Each delivery moves through these statuses:

| Status      | Meaning                                   |
| ----------- | ----------------------------------------- |
| `pending`   | Queued, not yet attempted                 |
| `delivered` | Acknowledged with a 2xx response          |
| `retrying`  | Last attempt failed, another is scheduled |
| `failed`    | Exhausted all attempts                    |

Delivery logs — attempts, response codes, and retry state — are visible on the Dashboard's Webhooks page, where failed deliveries can also be re-sent.

## Implementing an Endpoint

Your endpoint must:

1. Verify the `X-Webhook-Signature` header (see [Verification](/stocks/webhooks/verification)).
2. Return a 2xx status to acknowledge receipt.
3. Process quickly, or queue heavy work for async handling.

## Best Practices

<Steps>
  <Step title="Verify signatures">
    Always verify `X-Webhook-Signature` before trusting a payload. Strip the `sha256=` prefix before comparing.
  </Step>

  <Step title="Handle idempotency">
    Deduplicate on `event_id`. Store processed IDs so repeat deliveries are no-ops.
  </Step>

  <Step title="Return 2xx quickly">
    Acknowledge immediately and queue heavy processing asynchronously.
  </Step>

  <Step title="Reconcile with the API">
    Periodically reconcile against [List Orders](/api-reference/stocks-trading/list-orders) and [List Transactions](/api-reference/stocks-transactions/list-transactions) in case a delivery was missed.
  </Step>
</Steps>

## Next Steps

<CardGroup cols={2}>
  <Card title="Signature Verification" icon="shield-check" href="/stocks/webhooks/verification">
    Implement HMAC verification
  </Card>

  <Card title="Manage Webhooks" icon="webhook" href="https://dashboard.daya.co">
    Create and manage webhook endpoints from your workspace's Webhooks page.
  </Card>
</CardGroup>
