Skip to Content
APIWebhooks

Webhooks

Sweep POSTs signed lifecycle events to the callback URL configured for the partner. Delivery is at-least-once and ordered per attempt. Webhooks are wake-up signals; GET /status remains canonical.

Events

wallet_submitted provider_pending settled failed

There is intentionally no expired event. Keep status polling as the source-of-truth fallback.

Delivery

Headers:

X-Partner-Id: partner-example X-Timestamp: 1893456030000 X-Signature: <lowercase HMAC-SHA256 hex> X-Sweep-Event-Id: evt_00000000000000000001 X-Sweep-Api-Version: 1 Content-Type: application/json

Body — an attempt snapshot plus event metadata (synthetic example):

{ "eventId": "evt_00000000000000000001", "event": "settled", "occurredAt": "2030-01-01T00:05:00.000Z", "attemptId": "attempt_00000000000000000001", "intentSessionId": "intent_00000000000000000001", "partnerId": "partner-example", "status": "settled", "currentStatus": "settled", "walletAddress": "0x1111111111111111111111111111111111111111", "recipient": "0x1111111111111111111111111111111111111111", "submittedAt": 1893456030000, "perChainStatus": [], "settledOutputs": [ { "chainId": 42161, "symbol": "USDC", "amount": "9.950000000000000000", "amountBasis": "estimated", "txHash": "0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd" } ] }

Signature

Webhooks intentionally use the frozen exact-body signature even when partner requests use auth v2:

X-Signature = HMAC_SHA256(PARTNER_SECRET, "<X-Timestamp>.<exact raw JSON body>")

Processing rules

  1. Capture the exact raw request bytes before any JSON parsing.
  2. Verify partner id, timestamp (±5 minutes) and signature against the raw bytes.
  3. Verify X-Sweep-Event-Id equals payload eventId.
  4. Durably deduplicate eventId; commit the event before acknowledging.
  5. Return 2xx only after verification and persistence succeed; non-2xx triggers redelivery.
  6. Process a duplicate delivery as a no-op after the dedupe check.

Code

import express from 'express'; import { verifyWebhook } from 'sweep-external-sdk/server'; const app = express(); app.post('/webhooks/sweep', express.raw({ type: '*/*' }), async (req, res) => { const result = verifyWebhook( { partnerId: process.env.SWEEP_PARTNER_ID, secret: process.env.SWEEP_PARTNER_SECRET }, { rawBody: req.body, headers: req.headers } ); if (!result.ok) return res.status(result.status).json({ error: result.error }); const isNew = await store.recordEventOnce(result.payload.eventId, result.payload); if (isNew) await onAttemptEvent(result.payload); // e.g. trigger a /status poll return res.status(200).end(); });

The full verifyWebhook implementation is in Reference code.

Last updated on