Webhooks
Get transfer updates pushed to your endpoint instead of polling.
Webhooks push transfer updates to your server as they happen. Across sends an
HTTPS POST to your endpoint every time a transfer made with your
integration's keys changes, so you don't have to poll
GET /v1/transfers/{orderId}.
Set up a webhook
Add your endpoint
In the integrator portal, open Webhooks and choose Add webhook. Enter:
- Endpoint URL: must be HTTPS, and must answer with a
2xxwithin 10 seconds. - Label (optional).
- Events:
transfer.status,transfer.data, or both.
Save the signing secret
Choose Create webhook. The portal shows the signing secret, which
starts with whsec_, once.
Copy the secret now. It cannot be retrieved or rotated. To replace it, delete the webhook and create a new one.
Verify and handle deliveries
Check the signature on every delivery, then process it as described below.
A webhook belongs to one integration. You can pause, resume, edit or delete it from the same page. Pausing stops deliveries, and changes made while it was paused are not sent when you resume.
Events
| Event | data carries | Sent when |
|---|---|---|
transfer.status | orderId, externalId (if you set one) and status | Any field of status changes: state, reason, description, isTerminal or finalized |
transfer.data | The full transfer, as returned by GET /v1/transfers/{orderId} | Any field of the transfer changes |
transfer.status is enough for most integrations. Use transfer.data if
you want amounts, fees and transaction hashes without a follow-up call.
Nothing is sent when you create a webhook, and there are no test events. You only receive changes made after the webhook exists.
What a delivery looks like
{
"version": 1,
"type": "transfer.status",
"payload": {
"id": "6f1c2b7e-3a5d-5f0e-9b8a-2d4c6e8f0a1b",
"timestamp": "2026-10-20T23:04:52.000Z",
"data": {
"orderId": "0x5673646f0e06e918b48d3d76dcc4f1dc5c9ea7553c50cd6919c5c3b0016fad13",
"status": {
"state": "completed",
"reason": "filled",
"description": "The transfer is complete and the funds were delivered to the recipient.",
"isTerminal": true,
"finalized": false
}
}
}
}Every delivery carries these headers:
| Header | What it is |
|---|---|
webhook-id | Unique id of this event, the same as payload.id. Use it to deduplicate |
webhook-timestamp | When this attempt was sent, in Unix seconds |
webhook-signature | v1, followed by the base64 HMAC-SHA256 signature |
The event type is only in the body's type, not in a header.
Verify the signature
Deliveries are signed to the Standard Webhooks spec, so any Standard Webhooks library can verify them. Verify against the raw request body, before parsing it.
import express from "express";
import { Webhook } from "standardwebhooks";
const wh = new Webhook(process.env.ACROSS_WEBHOOK_SECRET); // whsec_…
const app = express();
app.post(
"/webhooks/across",
express.raw({ type: "application/json" }), // keep the raw bytes for verification
(req, res) => {
let event;
try {
event = wh.verify(req.body, {
"webhook-id": req.header("webhook-id"),
"webhook-timestamp": req.header("webhook-timestamp"),
"webhook-signature": req.header("webhook-signature"),
});
} catch {
return res.sendStatus(400);
}
res.sendStatus(204); // acknowledge first, then do the work
void handleEvent(req.header("webhook-id"), event);
},
);The library also rejects deliveries whose webhook-timestamp is more than
five minutes old, which protects you against replays.
Handle deliveries safely
- Answer fast. Return a
2xxwithin 10 seconds and do the work afterwards. A timeout, a non-2xxresponse, a redirect or a network error counts as a failure, and failed deliveries are retried with backoff. - Deduplicate on
webhook-id. Delivery is at least once, so the same event can arrive more than once. - Don't rely on order. Events can arrive out of order. Read
statusfrom each event rather than inferring it from the sequence, or fetch the transfer when you need its current state. - Wait for
finalized. As with polling,isTerminalcan flip back untilfinalizedis true. Show progress fromisTerminal, and act on the outcome in ways you cannot undo only once an event carriesfinalized: true.
The webhook subscription endpoints under Deprecated APIs belong to the older deposit-address API. Across API webhooks are managed in the integrator portal.