POST /v1/webhooks. Apocor POSTs a signed JSON envelope for each matching domain event. Events are also available via GET /v1/events?starting_after= (same ids as webhooks) for reconciliation.
Example event
Event catalog
Full machine-readable list:
GET /v1/webhooks/event-types.
Events fire only on actual state changes. Freezing an already-frozen card
returns
200 but emits no second card.frozen. Likewise
applicant.status_changed fires only when the tenant-visible status
changes — internal verification stages that don’t move the collapsed status
stay silent, and concurrent processing paths are deduped so each transition
is emitted once.Verifying signatures
Header:X-Webhook-Signature: t=<unix_ts>,v1=<hex>Signed payload:
{timestamp}.{raw_body} with HMAC-SHA256 using your endpoint secret.
POST /v1/webhooks/{id}/rotate-secret), verify against the new secret or the previous secret until previous_secret_expires_at (48h overlap).
Also sent: X-Webhook-Id (immutable event id — dedupe on this), X-Webhook-Event.
Latency
First delivery attempt is typically 1–2 seconds after Apocor persists the event (target under 5 seconds). This is after our commit, not a synchronous hook on the card-network authorization path. We retry up to 6 times with backoff, then dead-letter. Return2xx immediately and process asynchronously.
Managing subscriptions
Redelivered events carry the same event
id — receivers that dedupe on X-Webhook-Id handle them for free.
Reconcile missed events
- Primary:
GET /v1/events?starting_after={last_event_id}&limit=100 - Resource GETs: KYC status, card, issuance, transactions, funding balances
- Delivery log:
GET /v1/webhooks/{id}/deliveries+ redeliver
Best practices
Respond fast
Return
2xx immediately and process asynchronously. Slow handlers risk timeouts and retries.Be idempotent
Deliveries may repeat. De-duplicate on the event
id.Verify every time
Check the signature on every request, not just the first.
Use HTTPS
Only register TLS endpoints so payloads stay encrypted in transit.
Issuance notes
POST /v1/cardsrequiresIdempotency-Key(72h window). Optionalreference_id— unique per program + environment; query withGET /v1/cards?reference_id=.- Async:
202+issuance_id; pollGET /v1/cards/issuances/{id}. - In-flight same key →
409withissuance_id. POST /v1/cards/{id}/fund,fund-from-wallet, anddefundalso requireIdempotency-Key.