Webhooks
Receive status changes and verify signatures.
UPP sends a POST to each enabled webhook endpoint subscribed to an event. Add endpoints under Settings → Webhook endpoints; each endpoint has its own signing secret and can subscribe to specific event types (all by default). Past deliveries are under Settings → Webhook deliveries.
Payload
{
"id": "evt_...",
"type": "card_funding.status_changed",
"created_at": "2026-10-29T08:00:00Z",
"data": { "id": "cfd_...", "status": "succeeded" }
}typeis<resource>.status_changedfor status changes, sent only when the status visible to you (data.status) changes. Other events are<resource>.<past tense>, such aswithdrawal.rfi_requested.datahas the same shape asGET /<resource>/{id}, withstatusset to the status that triggered the event.- Events can arrive out of order. When you need the current state, call the
GETendpoint.
All event types and their payloads are listed in the API reference.
Verify the signature
Each request has X-UPP-Signature: t=<unix seconds>,v1=<signature>, where the signature is hex(HMAC-SHA256(secret, "<t>.<raw body>")).
Verify against the raw request body, before parsing JSON, and reject requests whose t is too far from your clock.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyUppSignature(rawBody: string, header: string, secret: string, toleranceSeconds = 300): boolean {
const parts = Object.fromEntries(header.split(',').map((part) => part.split('=', 2)));
const timestamp = Number(parts.t);
if (!parts.v1 || Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) {
return false;
}
const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
return expected.length === parts.v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}Deduplicate
X-UPP-Event-Id equals the body id and stays the same across retries. Store processed IDs and skip repeats.
Retries
Any 2xx response is a success. Other responses, timeouts (10 seconds) and network errors are retried, up to 6 attempts in total, after about 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours.
Respond quickly with 2xx and process the event in the background.