Transfers & Payments API
APIs for the three-step transfer workflow: prepare, execute, and monitor transfers.
Base URL: https://sandbox.finhub.cloud/api/v2.1/fintrans
For complete details on authentication and headers, refer to the Standard HTTP Headers reference documentation.
Transfer Prerequisites Checklist
Before executing transfers:
Complete Transfer Flow
3-Step Process
Step 1: Prepare Transfer Order
Validates transfer details and beneficiary
Calculates fees and total debit amount
Reserves funds in the source account
Returns preparedOrderId with validUntil timestamp
Critical: Order expires after 15 minutes and must be executed before validUntil
Step 2: Execute Transfer Order
Confirms payment consent (if required)
Processes multi-level approvals (for high-value transactions)
Attaches supporting documents (for transactions above threshold)
Executes the actual payment
Returns executionId and transaction reference
Step 3: Monitor Status
Track execution progress (PENDING → PROCESSING → COMPLETED)
View state transition history
Check settlement status
Download transaction receipt
Two-Phase Pattern Explained
15-Minute Expiration : Prepared orders are only valid for 15 minutes from creation. You must execute the order before the validUntil timestamp or the order will expire and need to be prepared again.
Why Two Phases?
The prepare/execute pattern provides several benefits:
Pre-Validation - Catch errors before committing funds
Fee Transparency - Show exact fees before execution
Balance Reservation - Prevent overdrafts during execution
Compliance Window - Allow time for high-value transaction document upload
User Confirmation - Give users time to review and confirm
Idempotency - Prevent duplicate transfers
Timing Example
// Prepare at 10:00:00
const prepared = await prepareTransfer ( accountId , {
amount: { value: '10000' , scale: 2 , currency: 'EUR' },
target: { walletId: 'ben_123' },
beneficiaryId: 'ben_123' ,
type: 'TRANSFER'
});
console . log ( 'Prepared Order ID:' , prepared . preparedOrderId );
console . log ( 'Valid Until:' , prepared . validUntil ); // 2026-01-13T10:15:00Z
// You have until 10:15:00 to execute (15 minutes)
// After 10:15:00, the order expires and returns error:
// "Prepared order expired or not found"
// Execute before expiration
if ( Date . now () < new Date ( prepared . validUntil ). getTime ()) {
const result = await executeTransfer ( accountId , prepared . preparedOrderId );
console . log ( 'Executed!' , result . executionId );
} else {
console . error ( 'Order expired - prepare again' );
}
High-Value Transaction Flow
For transactions above the threshold (typically €10,000), additional documents are required:
// 1. Prepare high-value transfer
const prepared = await prepareTransfer ( accountId , {
amount: { value: '1500000' , scale: 2 , currency: 'EUR' } // €15,000
});
// 2. Upload supporting documents (BEFORE execute)
const documents = [
{ type: 'PROOF_OF_FUNDS' , content: proofOfFundsBase64 },
{ type: 'INVOICE' , content: invoiceBase64 },
{ type: 'CONTRACT' , content: contractBase64 }
];
for ( const doc of documents ) {
await uploadPaymentDocument ( walletId , prepared . preparedOrderId , doc );
}
// 3. Execute with consent confirmation
await executeTransfer ( accountId , prepared . preparedOrderId , {
consentConfirmation: {
consentId: 'consent_abc123' ,
confirmed: true ,
timestamp: new Date (). toISOString (),
channel: 'WEB' ,
signature: 'user_digital_signature'
},
authenticationCode: '123456' , // 2FA code
supportingDocuments: documents . map ( d => d . documentId )
});
Get Allowed Operations
Returns available transfer types, saved beneficiaries, current limits, and applicable fees.
Request
Source account ID for the transfer
Bearer token for authentication
Active session ID (optional)
Code Examples
curl -X GET "https://sandbox.finhub.cloud/api/v2.1/fintrans/acc_12345/allowed-operations" \
-H "Accept: application/json, text/plain, */*" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Tenant-ID: 97e7ff29-15f3-49ef-9681-3bbfcce4f6cd" \
-H "X-Forwarded-From: e2e-test" \
-H "platform: web" \
-H "deviceId: 356938035643809"
Response
Source accounts with asset IDs, balance, currency, and IBAN. Each entry provides the assetId and assetSubUnitId needed for the PrepareOrder call.
Registered beneficiaries with kind (B2B, B2C, EXTERNAL, INTERNAL, C2B), walletId/assetId, iban, and name.
Available operation types with opType, fromAccountId, fromAssetId, and toRefId (destination asset reference).
Current transaction limits and usage
Fee structure per transfer type
Consent model configuration (if applicable)
Active session ID (if applicable)
{
"success" : true ,
"data" : {
"accounts" : [
{
"accountId" : "69a7d5b0-58f0-4394-a3fc-4d33058dc89e" ,
"assetId" : "a1b2c3d4-e5f6-7890-abcd-ef1234567890" ,
"assetSubUnitId" : "dd35508b-7529-49b8-ae56-fafd523f953f" ,
"balance" : "50000.00" ,
"currency" : "EUR" ,
"iban" : "FI2112345600000785"
}
],
"beneficiaries" : [
{
"beneficiaryId" : "ben_12345" ,
"walletId" : "f1e2d3c4-b5a6-9807-fedc-ba0987654321" ,
"name" : "John Doe" ,
"iban" : "DE89370400440532013000" ,
"kind" : "EXTERNAL"
}
],
"operations" : [
{
"opType" : "sepa_transfer_internal" ,
"fromAccountId" : "69a7d5b0-58f0-4394-a3fc-4d33058dc89e" ,
"fromAssetId" : "a1b2c3d4-e5f6-7890-abcd-ef1234567890" ,
"toRefId" : "f1e2d3c4-b5a6-9807-fedc-ba0987654321"
},
{
"opType" : "sepa_transfer_external" ,
"fromAccountId" : "69a7d5b0-58f0-4394-a3fc-4d33058dc89e" ,
"fromAssetId" : "a1b2c3d4-e5f6-7890-abcd-ef1234567890" ,
"toRefId" : ""
}
],
"limits" : {
"daily" : {
"value" : 50000.00 ,
"currency" : "EUR" ,
"used" : 1000.00 ,
"remaining" : 49000.00
},
"perTransaction" : {
"value" : 10000.00 ,
"currency" : "EUR"
}
},
"fees" : {
"INTERNAL" : { "value" : 0 , "currency" : "EUR" },
"SEPA" : { "value" : 1.50 , "currency" : "EUR" },
"SEPA_INSTANT" : { "value" : 2.50 , "currency" : "EUR" },
"SWIFT" : { "value" : 25.00 , "currency" : "EUR" }
}
}
}
Asset IDs for PrepareOrder: Use accounts[0].assetId as assetId, accounts[0].assetSubUnitId as assetSubUnitId, and operations[0].toRefId as beneficiaryId (destination asset) in the PrepareOrder request. The opType from operations determines the URL path segment (e.g., sepa_transfer_internal).
Prepare Transfer
Validates transfer details and stages the order for execution. Returns fees and estimated arrival.
Request
Source account ID for the transfer
Bearer token for authentication
Source account reference Source IBAN (for topup from external)
Destination account reference
Transfer amount (AmountRequest) Amount value as string (in minor units when scale > 0) Example: "10000" (= €100.00 with scale 2)
Decimal scale. Example: 2
ISO 4217 currency code. Example: "EUR"
Top-level currency (must match amount.currency)
Source asset ID from /allowed-operations response (accounts[0].assetId). Required in INTEGRATION mode.
Asset sub-unit ID from /allowed-operations response (accounts[0].assetSubUnitId). Required in INTEGRATION mode.
Destination asset ID from /allowed-operations response (operations[0].toRefId or beneficiaries[0].walletId). In INTEGRATION mode, this must be the WSO2 asset ID , not the beneficiary UUID.
Payment reference (max 140 characters)
Internal description for the transfer
Consent reference (if payment consent is required) Consent ID from the payment consent creation step
Code Examples
curl -X POST "https://sandbox.finhub.cloud/api/v2.1/fintrans/acc_12345/types/TRANSFER/prepare" \
-H "Accept: application/json, text/plain, */*" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Tenant-ID: 97e7ff29-15f3-49ef-9681-3bbfcce4f6cd" \
-H "X-Session-Id: YOUR_SESSION_ID" \
-H "X-Forwarded-From: e2e-test" \
-H "platform: web" \
-H "deviceId: 356938035643809" \
-d '{
"source": { "walletId": "acc_12345" },
"target": { "iban": "DE89370400440532013000", "name": "Acme Corp" },
"amount": { "value": "50000", "scale": 2, "currency": "EUR" },
"currency": "EUR",
"assetId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"assetSubUnitId": "dd35508b-7529-49b8-ae56-fafd523f953f",
"beneficiaryId": "f1e2d3c4-b5a6-9807-fedc-ba0987654321",
"beneficiaryIban": "DE89370400440532013000",
"beneficiaryName": "Acme Corp",
"reference": "Invoice Payment",
"description": "Payment for services"
}'
Response
Unique identifier for the prepared order
Applicable fees for this transfer
Total amount to be debited (amount + fees)
Order expiration timestamp (must execute before this time)
200 - Success
400 - Validation Error
403 - Insufficient Funds
{
"success" : true ,
"data" : {
"orderId" : "ord_12345" ,
"status" : "PREPARED" ,
"amount" : {
"value" : 500.00 ,
"currency" : "EUR"
},
"fees" : {
"value" : 1.50 ,
"currency" : "EUR"
},
"totalDebit" : {
"value" : 501.50 ,
"currency" : "EUR"
},
"estimatedArrival" : "2024-01-17" ,
"expiresAt" : "2024-01-15T11:00:00Z"
}
}
Execute Transfer
Executes a prepared transfer order. The order must be executed before it expires.
Request
Bearer token for authentication
Prepared order ID from the prepare step
Authentication/2FA code (e.g., "427934")
Consent confirmation (if payment consent was used during prepare) Consent ID (defaults to preparedOrderId if not specified)
Whether the consent is confirmed (true)
Channel used for confirmation: WEB, MOBILE, API
Digital signature (empty string if not applicable)
Optional metadata for the execution (empty object {} if not needed)
Code Examples
curl -X POST "https://sandbox.finhub.cloud/api/v2.1/fintrans/acc_12345/types/TRANSFER/execute" \
-H "Accept: application/json, text/plain, */*" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Tenant-ID: 97e7ff29-15f3-49ef-9681-3bbfcce4f6cd" \
-H "X-Session-Id: YOUR_SESSION_ID" \
-H "X-Forwarded-From: e2e-test" \
-H "platform: web" \
-H "deviceId: 356938035643809" \
-d '{
"preparedOrderId": "ord_12345",
"authenticationCode": "427934",
"consentConfirmation": {
"consentId": "consent_abc123",
"confirmed": true,
"channel": "WEB",
"signature": ""
},
"executionMetadata": {}
}'
Response
Unique transaction identifier for tracking
Transaction status (PROCESSING, COMPLETED, FAILED)
ISO 8601 timestamp of execution
Estimated completion timestamp
200 - Success
400 - Order Expired
404 - Order Not Found
{
"success" : true ,
"data" : {
"transactionId" : "txn_67890" ,
"orderId" : "ord_12345" ,
"status" : "PROCESSING" ,
"executedAt" : "2024-01-15T10:35:00Z" ,
"estimatedCompletion" : "2024-01-17T12:00:00Z"
}
}
Transfer Types
INTERNAL Speed: Instant
Fee: Free
Between FinHub accounts
SEPA Speed: 1-2 business days
Fee: €1.50
Eurozone bank transfers
SEPA_INSTANT Speed: Under 10 seconds
Fee: €2.50
Real-time Eurozone transfers
SWIFT Speed: 2-5 business days
Fee: €25.00
International wire transfers
Direct Transfer Operations
Not Yet Implemented — The direct transfer endpoints below (/transfers/types.topup, /transfers/types.withdraw, etc.)
are registered in the BFF but currently return HTTP 501 Not Implemented .Use the prepare-execute workflow above instead:
POST /fintrans/{accountId}/types/{operationType}/prepare
POST /fintrans/{accountId}/types/{operationType}/execute
Planned Direct Endpoints
These endpoints will bypass the two-step workflow once implemented:
POST /api/v2.1/fintrans/{accountId}/transfers/types.topup → 501 Not Implemented
POST /api/v2.1/fintrans/{accountId}/transfers/types.withdraw → 501 Not Implemented
POST /api/v2.1/fintrans/{accountId}/transfers/types.transfer → 501 Not Implemented
POST /api/v2.1/fintrans/{accountId}/transfers/types.purchase → 501 Not Implemented
POST /api/v2.1/fintrans/{accountId}/transfers/types.sale → 501 Not Implemented
Muse-Proxy Compatibility Aliases
The TransferResource provides convenience aliases that delegate to the same prepare-execute logic in FinTransResource. These exist for backward compatibility with the muse-proxy API.
Prepare Transfer (Alias)
POST /api/v2.1/transfers/{walletId}/prepare
Delegates to POST /api/v2.1/fintrans/{walletId}/types/{operationType}/prepare. The operation type is inferred from the request body.
Execute Transfer (Alias)
POST /api/v2.1/transfers/{walletId}/execute
Delegates to POST /api/v2.1/fintrans/{walletId}/types/{operationType}/execute.
Order Wizard Aliases (via WalletResource)
These convenience endpoints are used by the Order Review Widget (step 7) and delegate to FinTransResource:
POST /api/v2.1/wallets/orders/prepare → delegates to FinTransResource.prepareOperation
POST /api/v2.1/wallets/orders/execute → delegates to FinTransResource.executeOperation
Request body for /wallets/orders/prepare:
{
"walletId" : "69a7d5b0-58f0-4394-a3fc-4d33058dc89e" ,
"operationType" : "SEPA_CREDIT_TRANSFER" ,
"amount" : "500.00" ,
"currency" : "EUR" ,
"beneficiaryId" : "ben_123" ,
"beneficiaryIban" : "DE89370400440532013000" ,
"beneficiaryName" : "Acme Corp" ,
"description" : "Invoice payment" ,
"e2eReference" : "INV-2026-001" ,
"consentId" : "consent_abc123"
}
Request body for /wallets/orders/execute:
{
"walletId" : "69a7d5b0-58f0-4394-a3fc-4d33058dc89e" ,
"operationType" : "SEPA_CREDIT_TRANSFER" ,
"orderId" : "ord_12345" ,
"otp" : "123456" ,
"consentId" : "consent_abc123"
}
Operation Types Summary
Type Description Use Case Status topupAdd funds Deposits, incoming transfers ✅ via prepare/execute withdrawRemove funds Bank withdrawals ✅ via prepare/execute transferMove between accounts P2P, internal transfers ✅ via prepare/execute sepa_transfer_externalExternal SEPA transfer Cross-bank payments ✅ via prepare/execute sepa_instant_paymentInstant SEPA payment Real-time payments ✅ via prepare/execute sepa_transfer_internalInternal SEPA transfer Same-bank transfers ✅ via prepare/execute purchaseDebit for purchase Merchant payments 🔜 Planned saleCredit from sale Revenue recognition 🔜 Planned
Transfer Status Flow
Status Description Next Step PREPARED Order validated, funds reserved Execute within 15 min EXECUTING Payment being processed Wait for completion COMPLETED Transfer successful None FAILED Transfer failed Review error, retry CANCELLED Manually cancelled Create new order
Idempotency (Prevent Duplicates)
Use X-Idempotency-Key header to prevent duplicate transfers:
const idempotencyKey = `transfer- ${ Date . now () } - ${ Math . random () } ` ;
await fetch ( url , {
method: 'POST' ,
headers: {
'X-Idempotency-Key' : idempotencyKey ,
... otherHeaders
},
body: JSON . stringify ( transferData )
});
Important: Same key within 24 hours = same response (no duplicate charge)
Response Codes
Code Description 200Transfer status retrieved successfully 201Transfer prepared/executed successfully 400Invalid request data or prepared order expired (>15 min) 403Insufficient funds or limits exceeded 404Account, order, or beneficiary not found 409Duplicate idempotency key 422Missing consent or approval required 500Internal server error
API Schema Reference
For the complete OpenAPI schema specification, see the API Schema Mapping document:
Changelog
Version Date Changes v1.0 2026-01-13 Comprehensive transfer operations documentation
Example: "97e7ff29-15f3-49ef-9681-3bbfcce4f6cd"
Authenticated user identifier
Example: "87b3af37-4ac1-402b-a0ea-53cfdc695e02"