Live order status
Get order and checkout updates the moment they happen with Server-Sent Events. Stream GET /v1/orders/{quoteId}/events or GET /v1/checkout/sessions/{id}/events from a browser, a server or curl, or use lh.streamOrder and the useOrderStream React hook.
LayerHop pushes order and checkout updates to you as they happen, over Server-Sent Events (SSE): one long-lived HTTP response that sends a new event on every change. You get the current state right away, then each change within about a second of LayerHop seeing it, and the stream closes by itself when the order is final. No polling loop, no webhook server needed.
| Stream | Events | Ends when |
|---|---|---|
GET /v1/orders/{quoteId}/events |
order with an OrderSummary |
filled, refunded, failed or expired |
GET /v1/checkout/sessions/{id}/events |
session with a CheckoutSession |
paid, expired or needs_review |
GET /v1/webhooks/stream (API key) |
event with a webhook payload |
Never (see the CLI) |
Orders are tracked once their transaction is reported with POST /v1/orders/{quoteId}/submitted (the SDK's reportSubmitted). While a transaction is in flight LayerHop checks it every 5 seconds for the first half minute, then backs off.
Try it with curl#
curl -N https://api.layerhop.com/v1/orders/q_6f1c2d0e-8a51-4c1e-9d0b-2f7a9e3c4b10/eventsevent: ready
data: {"retryMs":3000}
event: order
data: {"quoteId":"q_6f1c…","status":"submitted","provider":"relay",…}
event: order
data: {"quoteId":"q_6f1c…","status":"pending",…}
event: order
data: {"quoteId":"q_6f1c…","status":"filled","dstTxHash":"5VER…",…}Lines starting with : are keep-alive pings, sent every 15 seconds.
TypeScript SDK#
import { LayerHop } from "@layerhop/sdk";
const lh = new LayerHop();
await lh.reportSubmitted(quoteId, txHash);
for await (const order of lh.streamOrder(quoteId)) {
console.log(order.status); // submitted → pending → filled
}
// Checkout sessions
for await (const session of lh.checkout.stream(sessionId)) {
if (session.status === "paid") fulfil(session);
}Pass { signal } (an AbortSignal) to stop early. Errors are LayerHopApiErrors, like every other SDK call.
React#
import { useOrderStream } from "@layerhop/react";
function OrderProgress({ quoteId }: { quoteId: string }) {
const { order, isFinal, connected, error } = useOrderStream(quoteId);
if (!order) return <p>Connecting…</p>;
return <p>{order.status}{!isFinal && connected ? " (live)" : ""}</p>;
}useOrderStream reconnects after network errors (1 s, 2 s, 4 s, up to 30 s) and stops once the order is final. It needs @layerhop/sdk 0.3 or later.
In the browser without the SDK#
EventSource works directly:
const es = new EventSource(`https://api.layerhop.com/v1/orders/${quoteId}/events`);
es.addEventListener("order", (e) => {
const order = JSON.parse(e.data);
render(order);
if (["filled", "refunded", "failed", "expired"].includes(order.status)) es.close();
});EventSource cannot send headers, so it is anonymous; order and session streams don't need a key. Browsers reconnect it automatically: close it yourself once the order is final.
Limits#
- A stream lasts at most 30 minutes; reconnect if you need longer. You always get the current state first, so nothing is missed.
- At most 20 open streams per API key, or per IP without a key (
RATE_LIMITEDbeyond that). - Anyone who knows a
quoteIdor session id can watch it. The streams show the same fields as the publicGETendpoints (no account ids, no merchantreferenceormetadata).
Webhooks or streams?#
Use streams to show progress to a user while they wait (a swap screen, a checkout page). Use webhooks for your backend: they are signed, retried for 24 hours and arrive even when nobody is watching. Many apps use both.
Test webhooks locally#
GET /v1/webhooks/stream streams every webhook event of your account, in your API key's mode. The CLI uses it to forward events to your laptop, so you can develop a webhook handler without a public URL:
LAYERHOP_API_KEY=lh_test_… npx @layerhop/cli listen --forward-to http://localhost:3000/webhooksLast updated