OpenFX uses an event-driven architecture to notify your application when something changes —
a payment completes, a customer passes KYB, a conversion
settles, or a collection is returned. Instead of polling every endpoint to check for updates, you register a webhook
subscription and OpenFX pushes events to your URL in real time.
How it works
Your Application OpenFX Platform
================= ===============
1. Register webhook subscription
POST /webhooks/subscriptions ---> Subscription created (wsub_...)
<--- Signing secret returned (store securely)
2. Something happens on the platform
(e.g., payment completes) Event generated (evt_...)
3. OpenFX delivers webhook
POST to your URL <--- Signed payload with X-OpenFX-Signature
4. Verify signature (Your code verifies HMAC-SHA256)
5. Process event (Your code handles the event)
6. Return 2xx response ---> Delivery marked as "delivered"
Every webhook delivery is signed with HMAC-SHA256 using a secret unique to your subscription.
Always verify the X-OpenFX-Signature header before processing any webhook payload.
See Signature Verification for implementation details.
Webhooks vs polling
OpenFX provides two complementary mechanisms for tracking resource changes:
| Webhooks | Events API (GET /events) |
|---|
| Delivery model | Push — OpenFX sends to your URL | Pull — you poll the endpoint |
| Latency | Near real-time (seconds) | Depends on your poll interval |
| Best for | Primary event processing | Recovery, backfill, audit |
| Ordering | Delivery order not guaranteed | Chronologically ordered |
| Reliability | 3 retries with exponential backoff | Always available |
Use webhooks as your primary integration mechanism and the Events API as a fallback
for recovery. If your webhook endpoint goes down, you can poll missed events from
GET /events once it recovers.
Event domains
OpenFX generates events across 10 resource domains in the MVP. Events follow a
consistent naming convention: resource.action (e.g., payment.completed,
customer.kyb_status_changed).
| Domain | Events | Key use cases |
|---|
| Customer | 3 | KYB status changes, customer activation |
| Account | 2 | Account creation and status transitions |
| AccountNumber | 2 | Virtual account number provisioning |
| BlockchainAddress | 1 | Crypto deposit address creation |
| Counterparty | 3 | Counterparty lifecycle (create, activate, archive) |
| PaymentMethod | 3 | Payment method validation results |
| Payment | 10 | Full payment lifecycle from creation to completion |
| Conversion | 4 | FX conversion lifecycle |
| Transfer | 3 | Internal transfer completion |
| Transaction | 1 | Ledger entry creation |
| Onboarding | 3 | Orchestrated onboarding completion |
| Collection | 7 | ACH debit collection lifecycle |
See Event Types for the complete list with payload details.
Webhook payload structure
Every webhook delivery uses a consistent envelope:
{
"id": "whd_01953e1a5f4b7100",
"type": "payment.completed",
"sequence": 42,
"createdAt": "2026-02-23T12:05:00Z",
"data": {
"resourceType": "payment",
"resourceId": "pmt_01953e1a5f4b7005",
"snapshot": {
"id": "pmt_01953e1a5f4b7005",
"status": "completed",
"rail": "ach",
"sendAmount": {
"currency": "USD",
"exponent": 2,
"amount": "150000",
"displayValue": "1500.00"
}
}
}
}
The sequence field is a monotonically increasing integer. Use it for ordering events and
detecting gaps when polling via GET /events.
For .updated and .status_changed events, the payload also includes previousAttributes
showing the fields that changed:
{
"id": "whd_01953e1a5f4b7101",
"type": "customer.status_changed",
"createdAt": "2026-02-23T12:03:00Z",
"data": {
"resourceType": "customer",
"resourceId": "cus_01953e1a5f4b7000",
"snapshot": {
"id": "cus_01953e1a5f4b7000",
"status": "active",
"kybStatus": "approved"
}
},
"previousAttributes": {
"status": "submitted",
"kybStatus": "pending"
}
}
Delivery guarantees
OpenFX provides at-least-once delivery with bounded retries:
- Retry policy: Failed deliveries are retried up to 3 times with exponential backoff.
- Success criteria: Your endpoint must return a
2xx HTTP status code within 30 seconds.
- Failure handling: After 3 failed attempts, the delivery is marked as
failed.
Persistent failures may cause subscription suspension.
- Ordering: Event delivery order is not strictly guaranteed. Design your handlers to be
idempotent and use the event
id for deduplication.
Because webhooks provide at-least-once delivery, your endpoint may receive the same event
more than once. Always use the webhook delivery id field to deduplicate events in your
processing logic.
Quick start
Get webhooks working in three steps:
- Create a subscription — Register your endpoint URL and select event types.
- Verify signatures — Implement HMAC-SHA256 verification on your endpoint.
- Process events — Handle each event type relevant to your integration.
See Setup & Configuration to begin.
Next steps