Transfers
Transfers represent the movement of funds associated with an account. frameOS supports two flows:
- Charge flow — a source payment method (card or ACH) is charged and funds move to the merchant's subaccount.
- Payout flow — funds are pushed directly to an account's payment method (ACH or debit card) with no source payment method required.
Transfers are created synchronously. For payout flows, the underlying payout is processed asynchronously and the transfer status is updated once the processor responds.
Before creating a transfer, make sure your merchant has an active billing agreement and the appropriate fee plans configured. See prerequisites for details.
Transfer flows
Charge flow
Provide a source_payment_method_id to charge the payer's card or bank account. The billable event type is resolved automatically based on the payment method type:
| Source type | Billable event |
|---|---|
| Card | transfer.charge.card |
| ACH | transfer.charge.ach |
The destination_payment_method_id is optional for charge flows:
destination_payment_method_id | Funds destination |
|---|---|
| Omitted | Funds go to the merchant (platform) |
| Provided | Funds go to the account that owns the payment method |
Frame's processing fee is calculated against the charged amount. How fees affect the payee depends on the billing plan's fee_application_mode:
| Mode | Behavior |
|---|---|
deduct | Fees are deducted from the transfer amount. The sender is charged amount as-is |
add_on | The sender is charged a grossed-up amount so that after Frame deducts its fee, the full amount reaches the merchant |
Payout flow
Omit source_payment_method_id and provide only destination_payment_method_id. Funds are pushed to the account's payment method. The billable event type is resolved automatically:
| Destination type | Billable event |
|---|---|
| ACH bank account | transfer.payout.ach |
| Debit card | transfer.payout.push_to_card |
For payout flows, net_amount always equals the requested amount — the payee receives the full amount. Frame's frame_fee is charged to the merchant separately.
Payout processing is asynchronous. The transfer starts as pending and transitions to completed or failed once the processor responds.
Statuses
| Status | Description |
|---|---|
| pending | Transfer has been created and is awaiting processing |
| completed | Funds have been successfully moved |
| failed | Transfer could not be completed. See failure_reason for details |
| reversed | A completed transfer has been reversed |
| canceled | Transfer was canceled before processing |
| blocked | Transfer is blocked, typically pending a compliance review |
The Transfer object
Attributes
Unique identifier for the transfer.
Always transfer.
Current status. One of pending, completed, failed, reversed, canceled, or blocked.
Requested transfer amount in the smallest currency unit (e.g. cents). 10000 represents $100.00.
Three-letter ISO currency code in uppercase (e.g. USD).
Amount received by the payee in cents. For payout flows, always equals amount. For charge flows, may be reduced by fees depending on the billing plan.
Frame's fee for this transfer in cents. For payout flows this is charged to the merchant, not deducted from the payee.
Optional description provided at creation.
Human-readable reason when status is failed, otherwise null.
ID of the associated charge intent for charge flows, otherwise null.
ID of the associated payout for payout flows, otherwise null.
ID of the billing agreement used to calculate fees.
The payment method charged in charge flows. Contains id, object, type, status, livemode, created, and updated. null for payout flows.
The payment method funds are sent to in payout flows. Contains id, object, type, status, livemode, created, and updated. null when omitted in charge flows.
A set of key-value pairs you can attach to the transfer for your own reference.
true if the transfer exists in live mode, false for test mode.
Unix timestamp of when the transfer was created.
{
"id": "e36c1cc2-d9f0-44db-a522-12b0aab990db",
"object": "transfer",
"status": "pending",
"amount": 10000,
"currency": "USD",
"net_amount": 10000,
"frame_fee": 100,
"description": "Weekly payout",
"failure_reason": null,
"charge_intent": null,
"payout": null,
"billing_agreement": "b37da62b-c233-4358-967a-7910656fb94f",
"source_payment_method": null,
"destination_payment_method": {
"id": "2a769159-83b7-4b78-8a07-3e5891a414d2",
"object": "payment_method",
"type": "ach",
"status": "active",
"livemode": false,
"created": 1773398080,
"updated": 1773398080
},
"metadata": {},
"livemode": false,
"created": 1773415510
}
Create a transfer
Creates a new transfer. You must provide either source_payment_method_id (charge flow) or destination_payment_method_id (payout flow). Providing neither returns a validation error.
Parameters
Amount in the smallest currency unit (e.g. 10000 for $100.00). Must be greater than 0.
ID of the account associated with the transfer.
ISO currency code (e.g. USD). Defaults to USD.
Required for charge flows. The payment method to charge. Omit for payout flows.
Required for payout flows (must be ACH or debit card). Optional for charge flows — when provided, funds are routed to the account that owns the payment method; when omitted, funds go to the merchant.
Arbitrary description for your records.
Key-value pairs to attach to the transfer.
Returns
Returns the created transfer object.
curl --request POST \
--url https://api.framepayments.com/v1/transfers \
--header 'Authorization: Bearer API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"amount": 10000,
"account_id": "c1a4a27f-d6e4-46e2-9557-fd5faaa31e7d",
"destination_payment_method_id": "2a769159-83b7-4b78-8a07-3e5891a414d2",
"description": "Weekly payout"
}'
{
"id": "e36c1cc2-d9f0-44db-a522-12b0aab990db",
"object": "transfer",
"status": "pending",
"amount": 10000,
"currency": "USD",
"net_amount": 10000,
"frame_fee": 100,
"description": "Weekly payout",
"failure_reason": null,
"charge_intent": null,
"payout": null,
"billing_agreement": "b37da62b-c233-4358-967a-7910656fb94f",
"source_payment_method": null,
"destination_payment_method": {
"id": "2a769159-83b7-4b78-8a07-3e5891a414d2",
"object": "payment_method",
"type": "ach",
"status": "active",
"livemode": false,
"created": 1773398080,
"updated": 1773398080
},
"metadata": {},
"livemode": false,
"created": 1773415510
}
Charge flow example
To create a charge flow transfer, provide a source_payment_method_id instead of a destination_payment_method_id. The charge is processed synchronously and a charge_intent is created.
curl --request POST \
--url https://api.framepayments.com/v1/transfers \
--header 'Authorization: Bearer API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"amount": 5000,
"account_id": "c1a4a27f-d6e4-46e2-9557-fd5faaa31e7d",
"source_payment_method_id": "pm_xyz789",
"currency": "USD",
"description": "Product purchase"
}'
{
"id": "f47a2dd3-e0b1-55ec-b633-23c1bbc101ec",
"object": "transfer",
"status": "pending",
"amount": 5000,
"currency": "USD",
"net_amount": 4789,
"frame_fee": 211,
"description": "Product purchase",
"failure_reason": null,
"charge_intent": "ci_abc123",
"payout": null,
"billing_agreement": "b37da62b-c233-4358-967a-7910656fb94f",
"source_payment_method": {
"id": "pm_xyz789",
"object": "payment_method",
"type": "card",
"status": "active",
"livemode": false,
"created": 1773398080,
"updated": 1773398080
},
"destination_payment_method": null,
"metadata": {},
"livemode": false,
"created": 1773415510
}
Retrieve a transfer
Retrieves the details of an existing transfer by its ID.
Parameters
The ID of the transfer to retrieve.
Returns
Returns the transfer object.
curl --request GET \
--url https://api.framepayments.com/v1/transfers/e36c1cc2-d9f0-44db-a522-12b0aab990db \
--header 'Authorization: Bearer API_KEY'
{
"id": "e36c1cc2-d9f0-44db-a522-12b0aab990db",
"object": "transfer",
"status": "completed",
"amount": 10000,
"currency": "USD",
"net_amount": 10000,
"frame_fee": 100,
"description": "Weekly payout",
"failure_reason": null,
"charge_intent": null,
"payout": null,
"billing_agreement": "b37da62b-c233-4358-967a-7910656fb94f",
"source_payment_method": null,
"destination_payment_method": {
"id": "2a769159-83b7-4b78-8a07-3e5891a414d2",
"object": "payment_method",
"type": "ach",
"status": "active",
"livemode": false,
"created": 1773398080,
"updated": 1773398080
},
"metadata": {},
"livemode": false,
"created": 1773415510
}
List transfers
Returns all transfers for the authenticated merchant, ordered by most recently created. Results are scoped to the current environment (test vs. live mode).
Parameters
Number of results per page.
Page number.
Returns
Returns a paginated list of transfer objects.
curl --request GET \
--url https://api.framepayments.com/v1/transfers \
--header 'Authorization: Bearer API_KEY'
{
"data": [
{
"id": "e36c1cc2-d9f0-44db-a522-12b0aab990db",
"object": "transfer",
"status": "completed",
"amount": 10000,
"currency": "USD",
"net_amount": 10000,
"frame_fee": 100,
"description": "Weekly payout",
"failure_reason": null,
"charge_intent": null,
"payout": null,
"billing_agreement": "b37da62b-c233-4358-967a-7910656fb94f",
"source_payment_method": null,
"destination_payment_method": {
"id": "2a769159-83b7-4b78-8a07-3e5891a414d2",
"object": "payment_method",
"type": "ach",
"status": "active",
"livemode": false,
"created": 1773398080,
"updated": 1773398080
},
"metadata": {},
"livemode": false,
"created": 1773415510
}
],
"meta": {
"current_page": 1,
"total_pages": 1,
"total_count": 1
}
}
Prerequisites
Before creating a transfer the following must be in place:
| Requirement | Applies to | Details |
|---|---|---|
| Active billing agreement | All transfers | The merchant must have an active billing agreement. A merchant-level agreement applies to all accounts unless an account-specific one exists. See billing. |
| Payout fee plan | Payout flows | The merchant must have a fee plan configured for the payout method (ach or push_to_card). |
| Processing fee plan | Charge flows | The merchant must have a default processing fee plan. |
| KYC/KYB verified account | Payout flows | The account must have a verified identity. Individual accounts require a verified kyc_verification requirement; business accounts require kyb_verification. See KYC. |
Webhooks
| Event | Triggered when |
|---|---|
transfer.created | A transfer is successfully created |
transfer.completed | The transfer completes (payout confirmed by processor) |
transfer.failed | The transfer fails. The failure_reason field on the object explains why |
Event payload
{
"type": "transfer.created",
"data": {
"id": "e36c1cc2-d9f0-44db-a522-12b0aab990db",
"object": "transfer",
"status": "pending",
"amount": 10000,
"currency": "USD",
"net_amount": 10000,
"frame_fee": 100,
"livemode": false,
"created": 1773415510
}
}