Overview
SwiftPay posts a signed HTTP POST request to your configured endpoint whenever a payment event
occurs. Every delivery includes an HMAC-SHA256 signature so you can verify the request originated
from SwiftPay and has not been tampered with.
Dashboard setup
- Open the Webhooks page in the SwiftPay dashboard.
- Enter an HTTPS URL in the Endpoint URL field and click Save.
- A
whsec_* signing secret is generated. The full value is always accessible from the
Webhooks page in the dashboard.
- The endpoint shows Active once saved.
To update the URL later, click the pencil icon next to the URL field, make your change, and save
again.
To remove the endpoint, click Remove. A confirmation prompt appears before the endpoint is
deleted.
Secret rotation
- On the Webhooks page, click Rotate Secret.
- A confirmation dialog is shown — confirm to generate a new
whsec_* secret.
- The old secret is invalidated immediately. Copy the new secret and update your server environment
as soon as possible — any webhook delivery verified against the old secret will fail.
Every webhook delivery includes the following HTTP headers:
| Header | Description |
|---|
Content-Type | application/json |
User-Agent | swiftpay-client/v0.1.0 |
X-SwiftPay-EventId | UUID of this delivery attempt |
X-SwiftPay-Timestamp | Unix timestamp (seconds) when the delivery was sent |
X-SwiftPay-Signature | HMAC-SHA256 hex signature (see Signature verification) |
Signature verification
Each delivery is signed so you can confirm it came from SwiftPay.
Signed message format
{X-SwiftPay-Timestamp}.{raw request body}
Algorithm: HMAC-SHA256, keyed with your whsec_* signing secret
Output: lowercase hex string
Replay protection: reject deliveries where |now − X-SwiftPay-Timestamp| > 300 seconds (5
minutes).
const crypto = require('crypto');
function verifySignature(secret, timestamp, rawBody, signature) {
const msg = `${timestamp}.${rawBody}`;
const expected = crypto.createHmac('sha256', secret).update(msg).digest('hex');
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}
Always use a constant-time comparison function (timingSafeEqual / hmac.compare_digest) to
prevent timing attacks.
Retry behaviour
- SwiftPay attempts delivery up to 5 times per event.
- If your endpoint does not return an HTTP
2xx response the delivery is retried on the following
backoff schedule:
| Attempt | Delay before retry |
|---|
| 2nd | 5 seconds |
| 3rd | 30 seconds |
| 4th | 2 minutes |
| 5th | 10 minutes |
- After 5 failed attempts the delivery is marked failed and will not be retried.
- Delivery history — including attempt count, HTTP status codes, and timestamps — is visible in the
dashboard under Webhooks → Delivery log.
Event catalogue
API Reference
payment.detected
payment.received
payment.confirmed
payment.flagged
All event payloads share the following envelope fields. Additional fields specific to each event
type are documented in the corresponding tab.| Field | Type | Description |
|---|
event | string | Event type identifier (e.g. payment.detected) |
invoiceId | string | UUID of the invoice |
reference | string | SwiftPay-generated short reference for the invoice |
externalRef | string | null | Your own reference passed when the invoice was created |
status | string | Current invoice status (pending, partial, paid, completed) |
token.symbol | string | Token symbol (e.g. USDC) |
token.network | string | Network ID the payment was received on (e.g. base) |
amounts | object | Amount details — fields vary by event type (see individual tabs) |
metadata | object | Key-value pairs you attached to the invoice at creation time |
Fired when an on-chain payment transaction is detected and confirmed for an invoice that is not
yet fully paid. Use this event to show a “payment in progress” state to your customer.Invoice status at this point: partial (amount still outstanding) or pending (first
detection before status transition)amounts field | Type | Description |
|---|
expected | string | null | Total amount due (null for open/flexible invoices) |
received | string | Cumulative amount received so far |
balance | string | Remaining amount still owed |
{
"event": "payment.detected",
"invoiceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference": "SP-20240101-XKQZ",
"externalRef": "order_9876",
"status": "partial",
"token": {
"symbol": "USDC",
"network": "base"
},
"amounts": {
"expected": "100.000000000000000000",
"received": "60.000000000000000000",
"balance": "40.000000000000000000"
},
"metadata": {
"customerId": "cus_abc123"
}
}
Fired when the full invoice amount has been received and the invoice moves to paid. This is
the primary event to use as a payment confirmation signal in your fulfilment flow.Invoice status at this point: paidamounts field | Type | Description |
|---|
expected | string | Total amount that was due |
received | string | Total amount received |
overpaid | string | null | Amount received above expected (omitted if zero) |
{
"event": "payment.received",
"invoiceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference": "SP-20240101-XKQZ",
"externalRef": "order_9876",
"status": "paid",
"token": {
"symbol": "USDC",
"network": "base"
},
"amounts": {
"expected": "100.000000000000000000",
"received": "100.000000000000000000",
"overpaid": "0.000000000000000000"
},
"paidAt": "2024-01-01T12:34:56Z",
"metadata": {
"customerId": "cus_abc123"
}
}
Fired when SwiftPay has forwarded the collected funds to your treasury address and the invoice
moves to completed. The forwardTxHash can be used to verify the on-chain settlement
transaction.Invoice status at this point: completedamounts field | Type | Description |
|---|
expected | string | Total amount that was due |
received | string | Total amount received from payer |
fee | string | SwiftPay platform fee deducted (1%) |
remitted | string | Net amount forwarded to your treasury |
overpaid | string | null | Amount received above expected (omitted if zero) |
{
"event": "payment.confirmed",
"invoiceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"reference": "SP-20240101-XKQZ",
"externalRef": "order_9876",
"status": "completed",
"token": {
"symbol": "USDC",
"network": "base"
},
"amounts": {
"expected": "100.000000000000000000",
"received": "100.000000000000000000",
"fee": "1.000000000000000000",
"remitted": "99.000000000000000000",
"overpaid": "0.000000000000000000"
},
"forwardTxHash": "0xabc123def456...",
"completedAt": "2024-01-01T12:35:10Z",
"metadata": {
"customerId": "cus_abc123"
}
}
Fired when an inbound transaction is flagged by SwiftPay’s compliance/AML screening. The
invoice is not automatically voided — review the flag details and take appropriate action (e.g.
pause fulfilment, contact the payer, or escalate to your compliance team).| Field | Type | Description |
|---|
event | string | payment.flagged |
invoiceId | string | UUID of the invoice associated with the flagged transaction |
txHash | string | On-chain transaction hash that was flagged |
complianceFlag | boolean | true when a compliance issue was detected |
riskLevel | string | Risk level assigned by the screening provider (e.g. high) |
flags | string[] | List of specific flag identifiers raised (e.g. ["SANCTIONS", "MIXER"]) |
{
"event": "payment.flagged",
"invoiceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"txHash": "0xdeadbeef...",
"complianceFlag": true,
"riskLevel": "high",
"flags": ["SANCTIONS", "MIXER"]
}