GuidesAPI ReferenceChangelog
API Reference

Internal Transfers

Webhook events for internal transfers between customers, including transfers across organizations.

Events

Event NameStatusDescription
transfer.completedCOMPLETEDInternal transfer completed successfully

transfer.completed

Triggered when an internal transfer between two customers completes successfully. The customers can belong to the same organization or different organizations.

Important: Each completed transfer generates an IN event for the receiving customer's organization and an OUT event for the sending customer's organization. Delivery depends on the subscriptions configured by each organization. One endpoint receives both directions only when both customers belong to the same organization and its subscription matches both events. For a cross-organization transfer, each organization normally receives only its own direction; do not wait for both to determine completion. Make your handler idempotent on transaction_id + direction.

When this event fires

The same transfer.completed event covers all internal transfer flows. Use the direction, from_customer_id, and to_customer_id fields to distinguish the participants:

TriggerInitiatorFunds flowEvent recipients
Customer-to-customer transfer via the OpenAPI Create Transfer endpointYour applicationSender → RecipientSender and receiver, under their respective subscriptions
Customer-to-customer transfer via the 1Money admin consoleOperations teamSender → RecipientSender and receiver, under their respective subscriptions
Auto-sweep on a sub-account deposit (when auto-sweep is enabled for your organization)SystemSub-account → Main accountSender and receiver, under their respective subscriptions
Prefunding withdrawal — initial funding transfer when calling Create Withdrawal with mode=prefundingYour applicationFunder (main) → Sub-account on whose behalf the withdrawal is createdSender and receiver, under their respective subscriptions

Compensating reversals (e.g. internal rollback when a prefunded withdrawal fails) do not emit transfer.completed. They are reflected only in the underlying withdrawal status events.

Payload — receiver delivery (direction=IN)

{
  "event_name": "transfer.completed",
  "resource": {
    "type": "transfers",
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "idempotency_key": "678160c6-ca41-40ee-bfe7-35057ade157d"
  },
  "data": {
    "status": "COMPLETED",
    "customer_id": "cus_receiver_id",
    "account_id": "BZ-RECV-1234",
    "transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "direction": "IN",
    "from_customer_id": "cus_sender_id",
    "from_account_id": "BZ-SEND-1234",
    "asset": "USDC",
    "amount": "1000.00",
    "memorandum": "Payment for invoice #12345",
    "created_at": "2024-01-22T12:00:00.000Z",
    "modified_at": "2024-01-22T12:00:00.000Z"
  }
}

Payload — sender delivery (direction=OUT)

Note: Each direction has a stable resource.id. The sender delivery uses a distinct UUID. Treat resource IDs as opaque. Use data.transaction_id when calling Retrieve Transfer.

{
  "event_name": "transfer.completed",
  "resource": {
    "type": "transfers",
    "id": "604230b2-5ba6-5413-b58f-4b9803dd73bb",
    "idempotency_key": "678160c6-ca41-40ee-bfe7-35057ade157d"
  },
  "data": {
    "status": "COMPLETED",
    "customer_id": "cus_sender_id",
    "account_id": "BZ-SEND-1234",
    "transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "direction": "OUT",
    "to_customer_id": "cus_receiver_id",
    "to_account_id": "BZ-RECV-1234",
    "asset": "USDC",
    "amount": "1000.00",
    "memorandum": "Payment for invoice #12345",
    "created_at": "2024-01-22T12:00:00.000Z",
    "modified_at": "2024-01-22T12:00:00.000Z"
  }
}

Fields

FieldTypeDirectionDescription
statusstringbothAlways COMPLETED for this event
customer_idstringbothThe customer this delivery is addressed to (receiver for IN, sender for OUT)
account_idstring | nullbothAccount ID of the customer, when available
transaction_idstringbothUnique identifier for the transfer; matches the transaction_id returned by Create Transfer
directionstringbothIN for the recipient delivery, OUT for the sender delivery
from_customer_idstringIN onlyThe sending customer's ID
to_customer_idstringOUT onlyThe receiving customer's ID
from_account_idstring | nullIN onlyAccount ID of the sending customer, when available
to_account_idstring | nullOUT onlyAccount ID of the receiving customer, when available
assetstringbothAsset code (e.g. USDC, USD, BTC, ETH)
amountstringbothTransfer amount as a decimal string
memorandumstring | nullbothOptional memo provided by the sender; null when no memo was supplied
created_atstringbothISO 8601 timestamp (millisecond precision, UTC) when the transfer record was created
modified_atstringbothISO 8601 timestamp (millisecond precision, UTC) when the transfer record was last updated

Participant fields

The customer_id and account_id identify the customer addressed by this delivery. For an IN event, from_customer_id and from_account_id identify the sender. For an OUT event, to_customer_id and to_account_id identify the receiver. The counterparty belongs to another organization for a cross-organization transfer; its presence in the payload does not mean your endpoint receives that party's event.

Example Use Cases

Scenario 1 — Payment notification on the recipient side

When Customer A sends 1,000 USDC to Customer B, Customer B receives the IN delivery:

{
  "data": {
    "direction": "IN",
    "customer_id": "cus_customer_b",
    "account_id": "BZ-CUST-B123",
    "from_customer_id": "cus_customer_a",
    "from_account_id": "BZ-CUST-A123",
    "asset": "USDC",
    "amount": "1000.00",
    "memorandum": "Invoice #INV-2024-001"
  }
}

Scenario 2 — Outgoing confirmation on the sender side

For the same transfer, Customer A receives the OUT delivery:

{
  "data": {
    "direction": "OUT",
    "customer_id": "cus_customer_a",
    "account_id": "BZ-CUST-A123",
    "to_customer_id": "cus_customer_b",
    "to_account_id": "BZ-CUST-B123",
    "asset": "USDC",
    "amount": "1000.00",
    "memorandum": "Invoice #INV-2024-001"
  }
}

Use the OUT event to confirm that an outbound transfer initiated by your system has been settled, without polling the API.

Scenario 3 — Auto-sweep on sub-account deposit

When auto-sweep is enabled and a deposit lands on a sub-account, the system automatically transfers the funds to the main account. Both deliveries are emitted with from_customer_id = sub-account and to_customer_id = main account. The memorandum will reference the originating deposit order ID, e.g. Auto transfer from sub-account deposit dep_xxx.

Scenario 4 — Prefunding withdrawal initial transfer

When you call Create Withdrawal with mode=prefunding and on_behalf_of=<sub-account>, the funder (main account) transfers the withdrawal amount to the sub-account before the external payout begins. This produces a transfer.completed event with from_customer_id = funder and to_customer_id = sub-account, so your reconciliation system can correctly attribute the internal funding leg.

Idempotency & deduplication

Because both deliveries share the same transaction_id, your webhook handler should:

  1. Use (transaction_id, direction) as the deduplication key, not transaction_id alone.
  2. Treat the IN and OUT deliveries as two independent ledger updates — they describe the same transfer from each counterparty's perspective.

Retrieving full transfer details

To fetch the complete transfer record (including fees, status history, and timestamps), call the Retrieve Transfer endpoint with the transaction_id.