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

# REST API (raw HTTP)

> A stopgap raw-HTTP reference for non-JavaScript backends until the OpenAPI spec is published.

For backends not using `@pulsebyshiga/node` (PHP, Python, Java, Go, …), call the engine
directly over HTTPS. This is a stopgap until the OpenAPI spec is published.

<Info>
  This surface reflects the `@pulsebyshiga/node` types — the working view of the API
  until the canonical Pulse OpenAPI contract is published. Wire shapes
  (snake\_case JSON, decimal-string amounts) are stable; a few fields are still
  being reconciled and are flagged where they appear.
</Info>

**Base URL:** `https://engine-api.shiga.io` (override via the SDK `baseUrl` when a sandbox
host is issued).

## Authentication

| Call type                      | Headers                                                          |
| ------------------------------ | ---------------------------------------------------------------- |
| API key (backend)              | `X-API-Key: <sk_ key>` **and** `Authorization: Bearer <sk_ key>` |
| Client session (browser/embed) | `Authorization: Bearer <cs_ token>`                              |

All requests also send `Accept: application/json`. Every `POST` includes an
`Idempotency-Key: <uuid>` and `Content-Type: application/json`. Responses carry a
`request-id` header — include it when contacting support.

## Endpoints — live (engine)

| Method & path                 | Purpose                                     |
| ----------------------------- | ------------------------------------------- |
| `POST /v1/quotes`             | Create a quote (also backs `rates.preview`) |
| `POST /v1/offramp/orders`     | Create an offramp order                     |
| `GET /v1/offramp/orders/{id}` | Retrieve an offramp order                   |
| `POST /v1/onramp/orders`      | Create an onramp order                      |
| `GET /v1/onramp/orders/{id}`  | Retrieve an onramp order                    |
| `GET /v1/banks`               | List supported banks                        |
| `POST /v1/banks/resolve`      | Resolve an account number to a name         |

## Endpoints — preview (collection product)

These are part of the preview surface — see [Contract status](/reference/contract-status).

| Method & path                                   | Purpose                                                                                          |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `POST /v1/collection-sessions`                  | Mint a single-order collection session                                                           |
| `GET /v1/collection-orders/{id}`                | Retrieve a collection order                                                                      |
| `POST /v1/collection-orders/{id}/refresh_quote` | Re-lock an expired quote                                                                         |
| `GET /v1/collection-orders`                     | List orders (`gate`, `status`, `user_ref`, `created_after`, `created_before`, `limit`, `cursor`) |

The client-session routes the embed uses (`GET /v1/client/session`,
`POST /v1/client/session/refresh_quote`, `POST /v1/client/session/select`,
`POST /v1/client/telemetry`) belong to the same preview surface.

## Webhook signatures (`Pulse-Signature`)

Each delivery carries a `Pulse-Signature` header:

```
Pulse-Signature: t=<unix-seconds>,v1=<hex-hmac>
```

Verify it before trusting the payload:

1. Split the header on `,` into `k=v` pairs; read `t` and one or more `v1` values
   (multiple `v1` values support key rotation).
2. Compute `HMAC_SHA256(secret, "<t>.<raw-request-body>")` as a lowercase hex digest —
   sign the **raw** bytes, not a re-serialized JSON object.
3. Constant-time compare your digest against each `v1`; accept if any matches.
4. Reject if `t` is outside a 300-second tolerance (replay protection).
5. De-duplicate on the event `id` — delivery is at-least-once.

`@pulsebyshiga/node` does all of this for you in `webhooks.verifyOnce(rawBody, header)`.
