OpenFX generates events whenever a resource is created, updated, or transitions to a new status. Each event type follows the naming convention resource.action and carries a consistent payload envelope with a full resource snapshot.

Payload structure

Every webhook delivery and every record in the Events API uses this structure:
{
  "id": "whd_01953e1a5f4b7100",
  "type": "payment.completed",
  "createdAt": "2026-02-23T12:05:00Z",
  "data": {
    "resourceType": "payment",
    "resourceId": "pmt_01953e1a5f4b7005",
    "snapshot": {
      "id": "pmt_01953e1a5f4b7005",
      "status": "completed",
      "rail": "ach",
      "sourceAccountId": "acc_01953e1a5f4b7002",
      "counterpartyId": "cpt_01953e1a5f4b7004",
      "sendAmount": {
        "currency": "USD",
        "exponent": 2,
        "value": "150000",
        "displayValue": "1500.00"
      },
      "createdAt": "2026-02-23T12:00:00Z",
      "updatedAt": "2026-02-23T12:05:00Z"
    }
  }
}
FieldTypeDescription
idstringUnique delivery ID (whd_ prefix). Use for deduplication.
typestringEvent type in resource.action format.
createdAtstringISO 8601 timestamp when the event was generated.
data.resourceTypestringType of the affected resource (e.g., payment, customer, account).
data.resourceIdstringID of the affected resource with its typed prefix.
data.snapshotobjectFull resource object as it existed at the time of the event. The shape matches the corresponding GET endpoint response.
previousAttributesobjectOnly present on .updated and .status_changed events. Contains the previous values of fields that changed.

previousAttributes for change events

Events with .updated or .status_changed suffixes include a previousAttributes object at the top level that shows what changed:
{
  "id": "whd_01953e1a5f4b7102",
  "type": "account.status_changed",
  "createdAt": "2026-02-23T12:01:00Z",
  "data": {
    "resourceType": "account",
    "resourceId": "acc_01953e1a5f4b7002",
    "snapshot": {
      "id": "acc_01953e1a5f4b7002",
      "status": "active",
      "type": "demand_deposit",
      "currency": "USD"
    }
  },
  "previousAttributes": {
    "status": "pending"
  }
}
Use previousAttributes to detect specific transitions without querying the API. For example, you can detect a payment moving from processing to completed by checking previousAttributes.status === "processing" and data.snapshot.status === "completed".

Complete event type reference

Customer events (3)

Event typeTriggerKey payload fields
customer.createdNew customer created (via onboarding or directly)entityId, status, kybStatus
customer.status_changedCustomer status transitioned (e.g., draft to active)status, previousAttributes.status
customer.kyb_status_changedKYB review result receivedkybStatus, previousAttributes.kybStatus
The customer.kyb_status_changed event is critical for onboarding flows. Listen for kybStatus: "approved" in the snapshot to know when a customer is ready to create accounts and send payments. Handle kybStatus: "rejected" to surface rejection reasons to your users.

Account events (2)

Event typeTriggerKey payload fields
account.createdNew account created via POST /accountstype, currency, customerId, status
account.status_changedAccount status transitionedstatus, previousAttributes.status

Account number events (2)

Event typeTriggerKey payload fields
account_number.createdAccount number provisioned via POST /accounts/{id}/account-numberstype, accountId
account_number.status_changedAccount number status transitionedstatus, previousAttributes.status

Blockchain address events (1)

Event typeTriggerKey payload fields
blockchain_address.createdBlockchain address generated via POST /accounts/{id}/blockchain-addresseschain, asset, address, accountId

Counterparty events (3)

Event typeTriggerKey payload fields
counterparty.createdNew counterparty created via POST /counterpartiesname, customerId
counterparty.activatedCounterparty activated after creationstatus
counterparty.archivedCounterparty archivedstatus

Payment method events (3)

Event typeTriggerKey payload fields
payment_method.createdPayment method added via POST /counterparties/{id}/payment-methodstype, currency, counterpartyId
payment_method.validatedPayment method validation completed successfullyvalidationResult
payment_method.rejectedPayment method validation failedvalidationResult, status

Payment events (10)

Event typeTriggerKey payload fields
payment.createdNew payment created via POST /payments or rail-specific endpointstatus, rail, sendAmount, sourceAccountId
payment.requires_actionPayment needs user action (compliance, RFI, quote refresh)status, requiresActionReason
payment.in_reviewPayment entered compliance reviewstatus
payment.processingPayment submitted to the payment railstatus
payment.completedPayment successfully delivered to counterpartystatus, receiveAmount, completedAt
payment.returnedPayment returned by receiving bankstatus, returnReason
payment.reversedPayment reversed after completionstatus
payment.refundedRefund issued for paymentstatus
payment.failedPayment failed to processstatus, failureReason
payment.canceledPayment canceled by callerstatus

Conversion events (4)

Event typeTriggerKey payload fields
conversion.createdNew FX conversion created via POST /fx/conversionssellCurrency, buyCurrency, status
conversion.processingConversion submitted for executionstatus
conversion.completedConversion settled successfullystatus, exchangeRate, sellAmount, buyAmount
conversion.failedConversion failedstatus, failureReason

Transfer events (3)

Event typeTriggerKey payload fields
transfer.createdInternal transfer created via POST /transferssourceAccountId, destinationAccountId, amount
transfer.completedTransfer completedstatus
transfer.failedTransfer failedstatus, failureReason

Transaction events (1)

Event typeTriggerKey payload fields
transaction.createdNew ledger entry created for any balance-affecting eventtype, direction, amount, accountId, referenceType, referenceId
The transaction.created event fires for every balance change — payments, conversions, transfers, fees, and adjustments. It is the most reliable signal for real-time balance reconciliation. Filter by referenceType to distinguish the source operation.

Onboarding events (3)

Event typeTriggerKey payload fields
onboarding.createdOrchestrated onboarding started via POST /onboardingsstatus, customerId
onboarding.completedAll onboarding steps completed successfullystatus
onboarding.failedOnboarding failed at one or more stepsstatus, failureReason

Collection events (7)

Event typeTriggerKey payload fields
collection.createdNew collection created via POST /collectionsstatus, rail, amount
collection.requires_actionCollection needs user actionstatus, requiresActionReason
collection.submittedCollection submitted to ACH networkstatus
collection.processingCollection being processedstatus
collection.completedCollection completed — funds receivedstatus, amount
collection.returnedCollection returned (e.g., insufficient funds)status, achReturnCode
collection.failedCollection failed to processstatus, failureReason
collection.canceledCollection canceledstatus

Filtering strategies

Subscribe to what you need

Rather than subscribing to all events and filtering in your application, subscribe only to the event types you process. This reduces webhook traffic and simplifies your handler:
{
  "eventTypes": [
    "payment.completed",
    "payment.failed",
    "payment.returned",
    "transaction.created"
  ]
}

Filter by resource type in your handler

If you subscribe to multiple event types, route them by data.resourceType or type:
def handle_event(event):
    resource_type = event["data"]["resourceType"]

    if resource_type == "payment":
        handle_payment_event(event)
    elif resource_type == "customer":
        handle_customer_event(event)
    elif resource_type == "transaction":
        handle_transaction_event(event)
    else:
        log.info(f"Ignoring event type: {event['type']}")

Use the Events API for filtered queries

The GET /events endpoint supports filtering by event type, which is useful for backfill and recovery scenarios:
curl "https://sandbox.api.openfx.com/v1/events?type=payment.completed&limit=50" \
  -H "Authorization: Bearer sk_sandbox_your_api_key" \
  -H "X-Signature: <computed-signature>" \
  -H "X-Timestamp: 1740500000"

Next steps