Skip to main content
Webhooks turn OpenFX resource lifecycles from a polling problem into a push-driven one. OpenFX delivers signed HTTP POSTs to an HTTPS endpoint you control the moment an event reaches a meaningful state. This page walks you from “no endpoint” to “events are verified and processed”. If you only need the signature-verification reference, jump to Webhook authentication. This page is the operational setup around that.

Prerequisites

  • A publicly reachable HTTPS endpoint (no plain HTTP: signatures protect integrity, TLS protects in-transit confidentiality)
  • An admin user in the OpenFX dashboard
  • A handler that can hold a small amount of state (deduplication of replayed events)

Step 1: Register your endpoint

1

Open the dashboard

Sign in to app.openfx.com/webhooks, your organization’s Webhooks settings.
2

Add a webhook URL

Enter your HTTPS URL. The dashboard supports one webhook URL per environment (Live and Sandbox have separate registrations; see Environments).
3

Subscribe to event types

Every event type name follows a dot-namespaced, lowercase {product}.{resource}.{action} convention (fx. for FX), so you can route on a whole family by prefix (e.g. fx.withdrawal.*) or on individual event types (e.g. fx.deposit.completed). See Deposit webhooks, Withdrawal webhooks, and Trade webhooks for the exact event types each resource emits.Subscribe to whichever events your integration needs; if in doubt, subscribe to everything.
4

Copy the signing key

The dashboard generates a per-org signing key on first registration. Store it as OPENFX_WEBHOOK_SIGNING_KEY in your secrets manager. Treat it like a database password: anyone with it can mint events that pass signature verification.
Sandbox and Live signing keys are different. A handler that uses the Live signing key to validate Sandbox events will reject every one. Wire the key from environment-aware config, not a hard-coded constant.

Step 2: Build the handler

Minimum viable handler: read the raw body, verify the signature, deduplicate by event id, dispatch.
Node / Express
Python / Flask
The signature-verification half of this is covered in full in Webhook authentication, including additional language samples.

Step 3: Acknowledge fast, process async

Return 2xx within a few seconds. Long-running processing (database writes, downstream API calls, sending the user an email) belongs on a queue:
OpenFX retries deliveries that don’t ack with 2xx. The combination of “ack fast” + “process via queue” + “deduplicate by event id” makes retries safe.

Step 4: Test in Sandbox

The fastest path to a working integration is to fire events from Sandbox before going to Live:
  1. Use your Sandbox API key (sandbox_-prefixed), which is bound to the Sandbox environment. If you send X-App-Mode, set it to SANDBOX; omitting it also selects Sandbox.
  2. Trigger a Sandbox deposit or withdrawal yourself from Sandbox mode in the dashboard. Sandbox doesn’t move real money, so there’s no amount to worry about.
  3. Verify your handler receives the signed event and returns 2xx.
  4. Check the dashboard’s delivery log for the event.
Trade webhooks (fx.trade.ready_to_settle, fx.trade.settled, fx.trade.reversed) are delivered only to Live webhooks. Sandbox trades never emit them, so you can’t exercise a trade handler in Sandbox. See Environments for the Sandbox-vs-Live split.

Event envelope shape

Every webhook delivery uses these top-level routing fields. This abbreviated shape collapses data to an empty object and omits the required Deposit or Withdrawal fields; use the complete, schema-checked event examples on the payload pages when implementing a handler.
Field names are camelCase; monetary amounts are strings (e.g. "1000.00"); timestamps are RFC 3339 with millisecond precision and a trailing Z. This matches the v3 REST API conventions exactly, deposit, withdrawal, and trade settlement events alike. The field-level reference is in:

Polling fallback

Webhooks are the primary delivery channel, but if your handler is briefly unreachable, OpenFX retries. As a defense-in-depth, periodically reconcile against the read endpoints: Use cursor pagination and walk forward from the last ID you’ve processed. Don’t poll continuously: a slow reconciliation sweep every few minutes is enough.

Common mistakes

  • Parsing the body before verifying. JSON re-serialization changes whitespace and key order, which changes the HMAC digest. Always verify against the raw request body.
  • String comparison on signatures. Use a constant-time comparison (crypto.timingSafeEqual, hmac.compare_digest). Standard === leaks timing.
  • Not deduplicating by event id. A handler that 500s on the first delivery and 200s on the redelivery will receive the same event twice. Store event.id server-side and short-circuit duplicates. Retain event IDs for at least 48 hours. OpenFX retries failed deliveries on a backoff curve within that window; older IDs can be safely purged.
  • Long-running work before acking. A 30-second database write turns into a webhook timeout, which turns into a retry, which lands you with three copies of the same work in flight. Ack first, process async.
  • Mixing Sandbox and Live signing keys. Wire the key from environment config, not a constant.

What’s next

Webhook authentication

Signature scheme + verification code samples.

Deposit webhooks

Event shape for fiat and stablecoin deposits.

Withdrawal webhooks

Event shape for withdrawal completion.

Trade webhooks

Event shape for ready-to-settle, settled, and reversed trades.

Payout webhooks

Event shape for payout status changes.

Verified accounts

Pre-configure withdrawal accounts the webhooks will report on.