Webhooks
Receive real-time event notifications for transaction and deposit lifecycle changes.
Webhooks
FV Bank sends an HTTP POST to your configured callback URL whenever a transaction event occurs for one of your clients.
Payload Envelope
Every webhook has this top-level structure:
{
"Event": "<EVENT_TYPE>",
"Id": "<unique delivery UUID>",
"Message": "<human-readable summary>",
"Data": { ... }
}Data Field Reference
| Field | Type | Notes |
|---|---|---|
CreatedDate | ISO 8601 | Timestamp of when the transaction was originally created (e.g. 2026-06-29T08:59:45.000Z) |
TransactionNumber | string | Unique FV Bank transaction reference (e.g. FV000726845). Use this to reconcile with your internal records |
Type | string | Identifies the payment or deposit rail (ACH, Wire, International Wire, Cross-Border, Stablecoin, etc.) — see Type Reference |
Amount | string | Transaction amount as a decimal string (e.g. "20.00") |
Balance | number | Account balance at the time the webhook was delivered, reflecting the effect of this transaction (e.g. 397885.60) |
From | string | Display name of the sender or originating party |
To | string | Display name of the recipient or beneficiary |
Currency | string | Currency code for the transaction (e.g. "USD") |
Description | string | Human-readable transaction description. Not present on all event types (optional) |
AdditionalData | array | Rail-specific metadata as label/value pairs — e.g. IMAD/OMAD for wire transfers, document IDs, stablecoin network info. Format: [{ "label": "...", "value": "..." }] (optional) |
OldStatus | string | Previous transaction status before this change. Present only in TRANSACTION_STATUS_UPDATED events |
NewStatus | string | Updated transaction status. Possible values: IN PROCESS, COMPLETED, RETURNED, FAILED. Present only in TRANSACTION_STATUS_UPDATED events |
Signature Verification
Every request includes a signature header:
x-signature: <HMAC-SHA256 hex>Computed over JSON.stringify(Data) using your Broker Client Secret.
const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', BROKER_CLIENT_SECRET);
const expected = hmac.update(JSON.stringify(req.body.Data)).digest('hex');
const valid = crypto.timingSafeEqual(
Buffer.from(req.headers['x-signature']),
Buffer.from(expected)
);Always verify the signature before processing any webhook.
Fiat Payment Webhooks
Covers Domestic ACH, Domestic Wire, International Wire, FVNet, and Stablecoin Withdraw transactions.
Event Flow
TRANSACTION_CREATED
→ TRANSACTION_AUTHORIZED (compliance approved)
→ TRANSACTION_STATUS_UPDATED (status changes during bank processing)
OR
→ TRANSACTION_DENIED
→ TRANSACTION_CANCELLED
→ TRANSACTION_EXPIRED
→ TRANSACTION_RETURNEDTRANSACTION_CREATED
Triggered when a payment is initiated. Pending compliance review — funds have not moved.
{
"Event": "TRANSACTION_CREATED",
"Id": "7d026990-7f3a-4e15-b00e-9af258b22309",
"Message": "Transaction created",
"Data": {
"CreatedDate": "2026-06-26T10:36:26.687Z",
"TransactionNumber": "FV000726845",
"Type": "Payment - Domestic Wire",
"Amount": "20.00",
"Balance": 397905.60,
"From": "Your Company Name",
"To": "Beneficiary Name",
…TRANSACTION_AUTHORIZED
Triggered when compliance approves the payment. Processing begins.
{
"Event": "TRANSACTION_AUTHORIZED",
"Id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"Message": "Transaction authorized",
"Data": {
"CreatedDate": "2026-06-29T08:59:45.000Z",
"TransactionNumber": "FV000726845",
"Type": "Payment - Domestic Wire",
"Amount": "20.00",
"Balance": 397885.60,
"From": "Your Company Name",
"To": "Beneficiary Name",
…TRANSACTION_STATUS_UPDATED
Triggered when the payment status changes after funds movement begins. May fire multiple times.
OldStatus and NewStatus are added to Data for this event only.
NewStatus | Meaning |
|---|---|
IN PROCESS | Bank is processing the transfer |
COMPLETED | Transfer successfully settled |
RETURNED | Returned by the receiving bank |
FAILED | Failed during processing |
{
"Event": "TRANSACTION_STATUS_UPDATED",
"Id": "a7b8c9d0-e1f2-3456-0123-567890123456",
"Message": "The member transaction status changed",
"Data": {
"CreatedDate": "2026-06-29T08:59:45.000Z",
"TransactionNumber": "FV000726845",
"Type": "Payment - Domestic Wire",
"Amount": "20.00",
"Balance": 397885.60,
"From": "Your Company Name",
"To": "Beneficiary Name",
…TRANSACTION_DENIED / CANCELLED / EXPIRED / RETURNED
Terminal states — no further events follow.
| Event | Message | When |
|---|---|---|
TRANSACTION_DENIED | Transaction denied | Compliance rejected |
TRANSACTION_CANCELLED | Transaction cancelled | Cancelled before processing |
TRANSACTION_EXPIRED | Transaction expired | Auth window elapsed |
TRANSACTION_RETURNED | Transaction returned | Reversed by receiving bank |
These events share the same payload shape as TRANSACTION_CREATED.
Fiat Deposit Webhooks
Covers Wire, ACH, International Wire, and Custodial Transfer deposits with full lifecycle events.
Stablecoin deposits only emit
DEPOSIT_RECEIVED. No further lifecycle events (authorized, denied, etc.) fire for stablecoin.
Event Flow
DEPOSIT_RECEIVED
→ DEPOSIT_AUTHORIZED (funds credited) ← success
OR
→ DEPOSIT_DENIED (compliance rejected) ← terminal
→ DEPOSIT_EXPIRED (auth window elapsed) ← terminal
→ DEPOSIT_CANCELLED (cancelled OR chargeback/reversal) ← terminalDEPOSIT_RECEIVED
Triggered when a deposit arrives. Pending compliance review — funds are not yet credited.
{
"Event": "DEPOSIT_RECEIVED",
"Id": "d0e1f2a3-b4c5-6789-3456-890123456789",
"Message": "Transaction created",
"Data": {
"CreatedDate": "2026-06-29T10:59:45.000Z",
"TransactionNumber": "FV000726900",
"Type": "Deposit Domestic Wire",
"Amount": "50000.00",
"Balance": 75000.00,
"From": "Sender Bank Name",
"To": "Your Company Name",
…Stablecoin Deposit
Only
DEPOSIT_RECEIVEDfires for stablecoin deposits — no authorized, denied, or cancelled events follow.
{
"Event": "DEPOSIT_RECEIVED",
"Id": "f8a9b0c1-d2e3-4567-1234-678901234567",
"Message": "Transaction created",
"Data": {
"CreatedDate": "2026-06-29T16:59:50.000Z",
"TransactionNumber": "FV000726903",
"Type": "Stablecoin Deposit",
"Amount": "1000.00",
"Balance": 26000.00,
"From": "FV Bank",
"To": "Your Company Name",
…DEPOSIT_AUTHORIZED
Triggered when compliance approves the deposit. Funds are now credited. Balance reflects the updated amount.
{
"Event": "DEPOSIT_AUTHORIZED",
"Id": "a3b4c5d6-e7f8-9012-6789-123456789012",
"Message": "Transaction authorized",
"Data": {
"CreatedDate": "2026-06-29T10:59:45.000Z",
"TransactionNumber": "FV000726900",
"Type": "Deposit Domestic Wire",
"Amount": "50000.00",
"Balance": 125000.00,
"From": "Sender Bank Name",
"To": "Your Company Name",
…DEPOSIT_DENIED / EXPIRED / CANCELLED
Terminal states — same payload shape as DEPOSIT_RECEIVED.
| Event | Message | When |
|---|---|---|
DEPOSIT_DENIED | Transaction denied | Compliance rejected the deposit |
DEPOSIT_EXPIRED | Transaction expired | No compliance action within the allowed timeframe |
DEPOSIT_CANCELLED | Transaction cancelled | Cancelled by compliance before processing |
DEPOSIT_CANCELLED | Transaction returned | Deposit reversed or charged back after receipt |
DEPOSIT_CANCELLEDfires for two distinct scenarios — use theMessagefield to distinguish between a cancellation and a chargeback/reversal.
Cross-Border Payment Webhooks
Covers all cross-border rails (SEPA, Wire, Faster Payments, CLABE, etc.).
Event Flow
TRANSACTION_CREATED
→ TRANSACTION_AUTHORIZED (compliance approved)
→ TRANSACTION_STATUS_UPDATED (status changes during processing)
OR
→ TRANSACTION_DENIED ← terminal
→ TRANSACTION_CANCELLED ← terminal
→ TRANSACTION_EXPIRED ← terminalCross-border payments do not emit
TRANSACTION_RETURNED. Chargebacks apply to domestic rails only.
Currency is USD. The destination currency is identified from Data.Type.
TRANSACTION_CREATED (Cross-Border)
{
"Event": "TRANSACTION_CREATED",
"Id": "e741c4bc-a6b0-466a-bf23-f4f20b799f39",
"Message": "Transaction created",
"Data": {
"CreatedDate": "2026-06-26T08:26:44.531Z",
"TransactionNumber": "FV000726696",
"Type": "Payment - Cross Border - Clabe",
"Amount": "1.16",
"Balance": 736.16,
"From": "Your Company Name",
"To": "Beneficiary Name",
…TRANSACTION_STATUS_UPDATED (Cross-Border)
Same structure as fiat — includes OldStatus and NewStatus in Data.
{
"Event": "TRANSACTION_STATUS_UPDATED",
"Id": "a5b6c7d8-e9f0-1234-8901-345678901234",
"Message": "The member transaction status changed",
"Data": {
"CreatedDate": "2026-06-29T08:59:50.000Z",
"TransactionNumber": "FV000726950",
"Type": "Payment - Cross Border - SEPA",
"Amount": "2000.00",
"Balance": 17500.00,
"From": "Your Company Name",
"To": "Beneficiary Name",
…Counterparty & Instrument Status Webhooks
These webhooks are triggered when a Counterparty record status changes to Active or Awaits Approval (Rejected), and when a Counterparty Instrument record status changes to Active or Rejected.
The payload for these webhooks includes a top-level
Datefield (ISO 8601 timestamp) in addition to the standardEvent,Id,Message, andDatafields.
Data Field Reference
| Field | Type | Notes |
|---|---|---|
BrokerId | string | Internal identifier for the GMA partner broker |
UserId | string | Identifier of the customer user associated with the record |
OldStatus | string | Status of the record before this change |
NewStatus | string | Status of the record after this change |
RecordId | string | Unique identifier of the counterparty or instrument record |
RecordType | string | Type of the record — COUNTERPARTY or COUNTERPARTY_INSTRUMENT |
Events
| Event | Message | When |
|---|---|---|
COUNTERPARTY_ACTIVATED | Counterparty activated | Counterparty status changed to Active |
COUNTERPARTY_INSTRUMENT_ACTIVATED | Counterparty Instrument activated | Instrument status changed to Active |
COUNTERPARTY_AWAITING_ACTIVATION | Counterparty awaiting activation | Counterparty status changed to Awaits Approval (Rejected) |
COUNTERPARTY_INSTRUMENT_REJECTED | Counterparty Instrument rejected | Instrument status changed to Rejected |
Sample — COUNTERPARTY_AWAITING_ACTIVATION
{
"Event": "COUNTERPARTY_AWAITING_ACTIVATION",
"Id": "244badfa-64fb-4436-b00a-b43789f8e6ee",
"Message": "Counterparty awaiting activation",
"Date": "2026-07-15T06:14:23",
"Data": {
"BrokerId": "<broker-id>",
"UserId": "<user-id>",
"OldStatus": "Pending",
"NewStatus": "Awaits approval",
"RecordId": "<record-id>",
"RecordType": "COUNTERPARTY"
…Type Reference
Fiat Payments
| Data.Type |
|---|
| Payment - Domestic ACH |
| Payment - Domestic Wire |
| Payment - International Wire |
| FVNet Payment |
| Stable Coin Withdraw |
Fiat Deposits
| Data.Type |
|---|
| BUS ACH Deposit |
| Credit IAT ACH |
| Deposit Domestic Wire |
| Deposit Bridge Wire |
| Business Deposit |
| Deposit Third Party Custodial Transfer |
| Stablecoin Deposit |
Cross-Border Payments
| Data.Type | Destination Currency |
|---|---|
| Payment - Cross Border - AED Domestic Payments | AED |
| Payment - Cross Border - AED Wire Payments | AED |
| Payment - Cross Border - ARS Domestic Payments | ARS |
| Payment - Cross Border - BRL Domestic Payments | BRL |
| Payment - Cross Border - CAD Domestic Payments | CAD |
| Payment - Cross Border - CAD Wire Payments | CAD |
| Payment - Cross Border - SEPA | EUR |
| Payment - Cross Border - EUR Wire Payments | EUR |
| Payment - Cross Border - Faster Payments | GBP |
| Payment - Cross Border - GBP Wire Payments | GBP |
| Payment - Cross Border - HKD Domestic Payments | HKD |
| Payment - Cross Border - HKD Wire Payments | HKD |
| Payment - Cross Border - JPY Domestic Payments | JPY |
| Payment - Cross Border - JPY Wire Payments | JPY |
| Payment - Cross Border - Clabe | MXN |
| Payment - Cross Border - MXN Wire Payments | MXN |
| Payment - Cross Border - SGD Domestic Payments | SGD |
| Payment - Cross Border - SGD Wire Payments | SGD |
| Payment - Cross Border - ZAR Domestic Payments | ZAR |
| Payment - Cross Border - ZAR Wire Payments | ZAR |
Best Practices
- Verify signatures on every request before processing
- Deduplicate using
Id— retries use the sameId - Use
TransactionNumberto reconcile with your internal records - Use
DEPOSIT_AUTHORIZED(notDEPOSIT_RECEIVED) to credit funds — the deposit is not confirmed until authorized - Handle terminal states (
DENIED,CANCELLED,EXPIRED,RETURNED) as failure paths requiring attention