Across Docs

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 2xx within 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

Eventdata carriesSent when
transfer.statusorderId, externalId (if you set one) and statusAny field of status changes: state, reason, description, isTerminal or finalized
transfer.dataThe 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

transfer.status
{
  "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:

HeaderWhat it is
webhook-idUnique id of this event, the same as payload.id. Use it to deduplicate
webhook-timestampWhen this attempt was sent, in Unix seconds
webhook-signaturev1, 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.

webhook-handler.ts
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 2xx within 10 seconds and do the work afterwards. A timeout, a non-2xx response, 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 status from 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, isTerminal can flip back until finalized is true. Show progress from isTerminal, and act on the outcome in ways you cannot undo only once an event carries finalized: true.

The webhook subscription endpoints under Deprecated APIs belong to the older deposit-address API. Across API webhooks are managed in the integrator portal.

On this page