GuidesAPI ReferenceChangelog
API Reference

Internal Transfers

Webhook events for internal transfers between customers within your organization.

Events

Event NameStatusDescription
transfer.completedCOMPLETEDInternal transfer completed successfully

transfer.completed

Triggered when an internal transfer between two customers in your organization is completed successfully.

Important: Each completed transfer fires two webhook deliveries — one to the receiving customer (direction=IN) and one to the sending customer (direction=OUT). When both customers belong to the same organization (which is always the case for internal transfers), your registered webhook endpoint will receive both events for the same transaction_id. Your handler should be idempotent on transaction_id + direction to avoid double-processing.

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 flowBoth webhooks delivered
Customer-to-customer transfer via the OpenAPI Create Transfer endpointYour applicationSender → Recipient
Customer-to-customer transfer via the 1Money admin consoleOperations teamSender → Recipient
Auto-sweep on a sub-account deposit (when auto-sweep is enabled for your organization)SystemSub-account → Main account
Prefunding withdrawal — initial funding transfer when calling Create Withdrawal with mode=prefundingYour applicationFunder (main) → Sub-account on whose behalf the withdrawal is created

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",
    "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: The resource.id for the sender delivery appends :OUT to the transfer ID. Use data.transaction_id (not resource.id) when calling the Retrieve Transfer API.

{
  "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_sender_id",
    "account_id": "BZ-SEND-1234",
    "transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "direction": "OUT",
    "to_customer_id": "cus_receiver_id",
    "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_idstringbothAccount ID of the customer
transaction_idstringbothUnique identifier for the transfer; matches the 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
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

Payload Comparison

The transfer webhook payload is richer than other transaction webhooks because it identifies both counterparties:

FieldTransfersDeposits / Withdrawals / Conversions
asset✅ Included❌ Not included
amount✅ Included❌ Not included
from_customer_id✅ Included (IN deliveries)N/A
to_customer_id✅ Included (OUT deliveries)N/A
direction✅ IncludedN/A
memorandum✅ IncludedN/A
modified_at✅ Included✅ Included

This design lets both counterparties immediately understand the transfer details without making additional API calls.

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",
    "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",
    "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.