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

# Workflow status changed

> This is the HTTP `POST` request **Halliday sends to your registered `url`** when a workflow
reaches a terminal state — it is not an endpoint you call. Respond with any `2xx` within the
10-second timeout to acknowledge; anything else (or a timeout) is treated as a failed delivery
and retried.

## Verifying the signature

Each delivery carries an `X-Halliday-Signature` header: a comma-separated list of `v1=0x<hex>`
values, where each `<hex>` is `HMAC-SHA256(raw_body)` computed with an active signing secret
(used as a UTF-8 string), prefixed with `0x`. During a secret rotation more than one signature
is present — treat the request as valid if **any** entry matches. Always verify against the
**raw request body**, before JSON parsing.

If you configured an `auth_header` when registering the webhook, Halliday also includes that
custom header on every delivery — check it alongside the signature.

```js
const crypto = require("crypto");

function verifyHallidaySignature(rawBody, headerValue, signingSecret) {
  const expected = crypto
    .createHmac("sha256", signingSecret) // secret used as UTF-8 string
    .update(rawBody)
    .digest("hex");

  return headerValue
    .split(",")
    .map((s) => s.trim().replace(/^v1=/, "").replace(/^0x/, ""))
    .some((sig) =>
      sig.length === expected.length &&
      crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))
    );
}
```

## Delivery & retry semantics

- **Acknowledgement:** any `2xx` response within the 10-second timeout.
- **Retries:** up to 12 attempts within a 24-hour window, after which the delivery is marked `FAILED`.
- **Backoff:** exponential with jitter — roughly 1m, 5m, 15m, 1h, 2h, 4h, then 8h between attempts.
- **Idempotency:** a given `(workflow_id, webhook, event_type)` is enqueued once. De-duplicate on
  the delivery `id`, since a retried delivery reuses the same `id`.




## OpenAPI

````yaml /public/openapi.yaml webhook workflowStatusChanged
openapi: 3.1.0
info:
  title: Halliday API V2
  description: >
    API V2 for Halliday's payment infrastructure, supporting onramps, swaps, and
    offramps.


    This API provides a unified interface for cryptocurrency payments, allowing
    developers to:

    - Quote payments across multiple providers

    - Execute payments with onramps, swaps, and offramps

    - Track payment status and history


    ## Authentication


    API key authentication is required for all endpoints.
  version: 2.0.0
  contact:
    name: Contact Halliday
    url: https://halliday.xyz
    email: support@halliday.xyz
servers:
  - url: https://v2.prod.halliday.xyz
    description: Base domain
security:
  - ApiKeyAuth: []
tags:
  - name: Chains
    description: Blockchain network information and configuration
  - name: Assets
    description: Asset information, discovery, and supported asset pairs
  - name: Payments
    description: >-
      Core payment operations including quotes, confirmation, and status
      tracking
  - name: Webhooks
    description: >
      Register HTTPS endpoints to receive signed notifications when a workflow
      reaches a terminal

      state, instead of polling for status. You subscribe to one or more event
      types per webhook.


      | Event type | Fires when a workflow's status becomes |

      | --- | --- |

      | `WORKFLOW_COMPLETED` | `COMPLETE` |

      | `WORKFLOW_FAILED` | `FAILED` |


      All management endpoints live under `/orgs/webhooks` and authenticate with
      a secret API key

      (passed as a bearer token) that has webhook access. Publishable keys
      cannot manage webhooks.


      **Integration checklist**


      - Receiver is a public HTTPS endpoint (no private IPs).

      - Save the `signing_secret` when you create the webhook — it is shown only
      once.

      - Verify `X-Halliday-Signature` against the raw body, accepting any of its
      comma-separated signatures.

      - Respond `2xx` quickly and do the real work afterward.

      - Skip deliveries whose `id` you have already handled.
paths: {}
components:
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: API_KEY

````