Webhooks let your server receive real-time HTTP callbacks when events happen in your AnySpend organization — payments completing, checkouts expiring, and more. Instead of polling the API, you register a URL and AnySpend pushes events to you.
Payer completes payment | vAnySpend processes transaction | vAnySpend sends POST to your webhook URL | vYour server verifies signature & processes event | vYour server responds with 200 OK
1
Register a webhook endpoint
Create a webhook via the Dashboard or the API, specifying the URL and which events to subscribe to.
2
AnySpend sends events
When a subscribed event occurs, AnySpend sends a POST request to your URL with a JSON payload and a signature header.
3
Verify and process
Your server verifies the HMAC-SHA256 signature, processes the event, and responds with a 200 status within 30 seconds.
4
Automatic retries
If your server does not respond with a 2xx status, AnySpend retries up to 3 times with exponential backoff.
Navigate to Settings > Webhooks > Add Endpoint in the AnySpend Dashboard. Enter your URL, select the events, and click Create. The signing secret will be displayed once — copy it immediately.
The payment.completed webhook always includes a checkoutSession block containing clientReferenceId, metadata, customerEmail, and customerName — everything you need to match payments to your internal records.
Pass your order or user ID when creating the checkout (via URL parameter or API):
Copy
Ask AI
function handlePaymentCompleted(data) { const { clientReferenceId, metadata } = data.checkoutSession; // Look up your internal order const order = await db.orders.findOne({ id: clientReferenceId }); if (!order) return; // Mark as paid await db.orders.update(order.id, { status: "paid", txHash: data.txHash, paidAt: new Date(), });}
function handlePaymentCompleted(data) { const { metadata, customerEmail } = data.checkoutSession; // metadata contains whatever you passed via URL or API const userId = metadata?.user_id; // e.g., Clerk user ID const plan = metadata?.plan; // e.g., "pro" await activateSubscription(userId, plan); if (customerEmail) { await sendReceipt(customerEmail, data.amount, data.txHash); }}
Both client_reference_id and metadata are always included in the webhook payload. You can set them via URL parameters for simple integrations or the Checkout Sessions API for server-side control.
Always verify the X-AnySpend-Signature header before processing a webhook. Without verification, an attacker could send forged events to your endpoint.
If your endpoint does not respond with a 2xx status code within 30 seconds, AnySpend marks the delivery as failed and retries.
Attempt
Delay after previous attempt
1st retry
1 minute
2nd retry
10 minutes
3rd retry
1 hour
After 3 failed retries, the delivery is marked as failed permanently. You can still manually retry it from the Dashboard or API.
If your endpoint consistently fails (10+ consecutive failed deliveries), the webhook will be automatically disabled and you will receive an email notification. Re-enable it from the Dashboard after fixing the issue.
// List recent deliveries for a webhookconst deliveries = await platform.webhooks.deliveries("wh_abc123", { limit: 20,});for (const d of deliveries.data) { console.log( d.id, d.event, d.status, // "success" | "failed" | "pending" d.response_code, // HTTP status code from your server d.created_at );}
Use the test endpoint to send a synthetic event to your webhook URL. This helps verify your endpoint is reachable and your signature verification logic is correct.
SDK
cURL
Copy
Ask AI
const testResult = await platform.webhooks.test("wh_abc123");console.log(testResult.delivery_id); // Delivery IDconsole.log(testResult.response_code); // Your server's HTTP statusconsole.log(testResult.success); // true if 2xx response
Copy
Ask AI
curl -X POST https://platform-api.anyspend.com/api/v1/webhooks/wh_abc123/test \ -H "Authorization: Bearer asp_your_api_key"
The test event uses the payment.completed event type with mock data. Your handler should process it like any other event, but you can check for the X-AnySpend-Test: true header if you want to skip side effects during testing.