Webhooks
Event delivery, retries, and verifying webhook signatures.
Webhooks push real-time notifications when consents, payments, or mandates change state. Use them in production instead of polling, they reduce latency, API load, and missed edge cases.
In this guide
- How outbound webhooks are delivered to your server
- Which events are available today
- Signature verification and idempotent handling
- How webhooks fit with polling and Spare Link
Why webhooks matter
After payer authorization, settlement can take seconds or minutes. Polling in a tight loop wastes API quota and still misses events if your server restarts.
Webhooks let Spare notify you when:
- Consent moves from
PendingtoAuthorisedorRejected - Payment settlement completes or fails
- Mandate transactions execute
Your handler should enqueue work and re-fetch the resource, webhook bodies are notifications, not the sole source of truth.
Delivery model
Transport
- HTTPS POST to URLs you register in the Spare dashboard
- JSON body with event type, resource IDs, and timestamps
- HMAC signature header for verification (algorithm in your dashboard docs)
Retries
If your endpoint returns non-2xx or times out, Spare retries with exponential backoff. Design handlers to be idempotent, the same event may arrive more than once.
Event catalog
Consent events
| Event | When fired |
|---|---|
consent.created | Consent record created |
consent.authorized | Payer approved at bank |
consent.revoked | Consent cancelled |
More events such as consent.rejected and consent.expired may be added, so handle unknown event types gracefully.
Payment events
| Event | When fired |
|---|---|
payment.created | Payment record created after execution starts |
Settlement completion events may be published as the platform evolves, always confirm final state via GET /payments/{id}.
Spare Link payment status
For Link-initiated payments, status updates may follow:
INITIATED β EXECUTED β SETTLED (or FAILED / REJECTED)
Wire these into the same handler pattern as standard payment events.
Recommended handler pattern
app.post("/webhooks/spare", async (req, res) => {
// 1. Verify signature
if (!verifySpareSignature(req)) {
return res.status(401).send("invalid signature");
}
const event = req.body;
// 2. Acknowledge quickly
res.status(200).send("ok");
// 3. Enqueue async processing
await queue.add("spare-event", event);
});
async function processEvent(event: SpareEvent) {
// 4. Re-fetch authoritative state
const consent = await client.paymentConsents.get({
consentId: event.data.consentId,
});
// 5. Idempotent business logic
if (consent.data.status === "Authorised") {
await fulfillOrder(event.data.paymentRequestId);
}
}from flask import Flask, request, abort
import json
app = Flask(__name__)
@app.route("/webhooks/spare", methods=["POST"])
def handle_webhook():
# 1. Verify signature
if not verify_spare_signature(request):
abort(401, "invalid signature")
event = request.get_json()
event_type = event.get("type")
if event_type == "payment.confirmed":
# fulfill order
pass
elif event_type == "consent.authorised":
# record authorization
pass
return "", 200Rules of thumb
- Verify the signature before parsing untrusted payloads
- Respond 200 within a few seconds, do heavy work asynchronously
- Re-fetch resources via API before irreversible actions
- Deduplicate using event ID or
(type, resourceId, timestamp)keys - Log raw events for audit, redact PII in application logs
Webhooks vs polling
| Webhooks | Polling | |
|---|---|---|
| Latency | Low (push) | Depends on interval |
| API load | Minimal | Grows with traffic |
| Complexity | Endpoint + verification | Simple loops |
| Best for | Production | Sandbox, debugging |
Use polling as a fallback if a webhook is delayed, not as the primary production strategy.
Registering endpoints
Configure webhook URLs in your Spare merchant dashboard (sandbox and production separately). Requirements:
- Public HTTPS URL
- Valid TLS certificate
- No authentication via query string secrets alone, use signature verification
Testing in sandbox
- Register a tunnel URL (e.g. ngrok) pointing to your local handler
- Complete a Quickstart payment
- Confirm
consent.authorizedarrives - Verify your handler re-fetches consent before updating state
Related topics
- Consent lifecycle, statuses to act on
- Payment flow, when events fire in the sequence
- Payments, polling alternative for sandbox
Key takeaways
- Webhooks notify; the API GET confirms, never fulfill solely on webhook payload.
- Respond fast, process async, verify signatures, deduplicate events.
- Register separate sandbox and production endpoints.