Execute API

Webhooks

Get a signed POST every time an address you created changes.

Setup

Set your endpoint in the dashboard. It must be a public https URL. Every address your account creates, with its API key or its client ID, reports there.

Events

TypeSent when
address.detectedA payment to the address was seen onchain. It counts once confirmed.
address.fundedThe address holds the amount, and Execute is executing it.
address.failingA call reverts. Execute retries until expiry; status.message says why.
address.settledEvery call ran.
address.refundingPast expiry with a balance, which is on its way to recovery.
address.refundedThe balance went to recovery.
address.expiredExpired with nothing to return.

Payload

POST your endpoint
{
  "id": "01925f3c-7d2e-7b41-9a8c-2f5e8d1b4c60",
  "type": "address.settled",
  "createdAt": "2026-09-29T07:12:31Z",
  "sequence": 7,
  "address": "0x3a9012C8bbDA720CE9C94458f8753965dFB6455a",
  "chainId": 8453,
  "status": {
    "address": "0x3a9012C8bbDA720CE9C94458f8753965dFB6455a",
    "state": "settled",
    "transactionHash": "0x5e0c…",
    "blockNumber": 36120455,
    "updatedAt": "2026-09-29T07:12:31Z"
  },
  "data": {}
}
idstring · UUID
The event’s id. Deliveries can repeat: skip an id you’ve already handled.
typestring
One of the events above.
createdAtstring · RFC 3339
When the event happened.
sequenceinteger
Grows with every change to the address. Deliveries can arrive out of order: keep the one with the highest sequence.
addressaddress
The executable address.
chainIdinteger
Its chain.
statusobject
The address’s status as of the event, exactly as Get an address’s status returns it.
dataobject
Details of the event, for diagnostics. They vary by event: rely on status.

Headers

HeaderValue
X-Execute-Signaturet=<unix seconds>,v1=<hex>: see below.
X-Execute-Event-IdThe event’s id.
X-Execute-Event-TypeThe event’s type.
X-Execute-Delivery-Attempt1 on the first delivery, then 2, 3…

Verifying signatures

v1 is the HMAC-SHA256 of <t>.<raw body>, keyed with your signing secret from the dashboard, as hex. Check it against the body exactly as received, before parsing it, and reject timestamps more than five minutes old so a captured delivery can’t be replayed:

verify-webhook.ts
import { createHmac, timingSafeEqual } from "node:crypto";

/** Whether a webhook is from Execute: the signature covers "<t>.<body>", and t is recent. */
export function verifyWebhook(rawBody: string, signature: string, secret: string, toleranceSecs = 300): boolean {
  const parts = new Map(signature.split(",").map((part) => part.trim().split("=", 2) as [string, string]));
  const t = Number(parts.get("t"));
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > toleranceSecs) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  const given = Buffer.from(parts.get("v1") ?? "", "hex");
  return given.length === expected.length && timingSafeEqual(given, expected);
}

Delivery and retries

Answer with any 2xx within 10 seconds. Anything else is retried with exponential backoff, from a second up to an hour between attempts, for 24 hours. Handle events idempotently: the same id can arrive more than once.