Webhooks
Receive signed callbacks when invoices change status — no polling required.
Delivery
Set a public https callback_url when creating an invoice (private, localhost, and cloud-metadata hosts are rejected — invalid URLs return 400 INVALID_CALLBACK at create time). On each status transition, swp.gg POSTs JSON with X-Swp-Signature, optional replay-safe X-Swp-Signature-V2 + X-Swp-Timestamp, and User-Agent: swp.gg-webhooks/1.0.
Signature
Verify with HMAC-SHA256(rawBody, secret) where secret is the full whsec_… string from the dashboard (API Keys → Webhook signing secret → Reveal). Use that value as the HMAC key — do not add another whsec_ prefix.
const crypto = require("crypto");
function verifyWebhook(rawBody, header, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(rawBody)
.digest("hex");
const received = header.replace("sha256=", "");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(received, "hex"),
);
}
// Recommended: also verify X-Swp-Signature-V2 over `${timestamp}.${rawBody}`
// and reject if |now - X-Swp-Timestamp| > 5 minutes.Events
invoice.confirming— payment seen on-chain; waiting for confirmationsinvoice.paid— confirmed within accuracy and creditedinvoice.partially_paid— confirmed but below the due amountinvoice.expired— lifetime ended without a full payment
Payload fields include track_id, status, order_id, amounts, pay_currency, address, tx_hash, and confirmation counts.
Reliability
Deliveries are queued durably and retried automatically (up to 10 attempts over ~24h) until your endpoint returns HTTP 2xx. Each POST includes X-Swp-Delivery-Id / Idempotency-Key — treat duplicate deliveries as no-ops. Still reconcile with GET /api/v1/invoice/{track_id}?refresh=1 if you miss a callback.
Full field reference: API documentation.