Create a FIAT → FIAT Transfer
This guide shows the exact sequence to execute a FIAT→FIAT transfer using Penny API
FIAT→FIAT Transfer (Register Beneficiary → Quote → Deposit w/ Auto Payout)
This guide shows the exact sequence to execute a FIAT→FIAT transfer using Penny API:
- Register a 3rd-party beneficiary (creates
fiatAccountId) - Create an FX quote (creates
quoteId) - Create a deposit intent referencing
quoteId+fiatAccountIdso Penny can auto-execute the payout after funds arrive
Auth for all calls below uses
api-key+api-secretheaders.
Prerequisites
-
You have
api-keyandapi-secret -
You have a Sender
customerIdwith an approved KYC -
You know:
- Source currency / amount (e.g., MXN 100000)
- Destination currency / amount (e.g., ARS)
- Beneficiary banking details for destination rail (e.g., SPEI CLABE for MX, CVU/CBU for AR, etc.)
Base URL:
https://penny-api-restricted-dev.alfredpay.io/api/v1/third-party-service/penny
1) Register Beneficiary (Third-Party Fiat Account)
Creates a beneficiary record and returns fiatAccountId.
Critical: set "isExternal": true.
Endpoint
POST /fiatAccounts
Example (Mexico SPEI / CLABE)
curl --location 'https://penny-api-restricted-dev.alfredpay.io/api/v1/third-party-service/penny/fiatAccounts' \
--header 'Content-Type: application/json' \
--header 'accept: */*' \
--header 'api-key: xxxxxxx' \
--header 'api-secret: xxxxxxx' \
--data '{
"customerId": "21ef567b-d4c5-4bad-b4ce-ce975cc1ac57",
"type": "SPEI",
"fiatAccountFields": {
"accountNumber": "012020477538404708",
"accountType": "CLABE"
},
"isExternal": true
}'Response (example)
{
"fiatAccountId": "797466c0-9774-4875-acfd-06adcc16596c",
"type": "SPEI",
"accountNumber": "012020477538404708",
"accountType": "CLABE",
"bankName": "BBVA MEXICO",
"createdAt": "2024-12-03T15:48:54.557Z"
}✅ Save: fiatAccountId
You will pass it in Step 3 under metadata.fiatAccountId.
2) Create Quote (FIAT→FIAT)
Creates the corridor pricing reference that will drive execution once funds arrive.
Endpoint
POST /quotes
Example (MXN → ARS, lock via fromAmount)
curl --request POST \
--url 'https://penny-api-restricted-dev.alfredpay.io/api/v1/third-party-service/penny/quotes' \
--header 'api-key: xxxxxxx' \
--header 'api-secret: xxxxxxx' \
--header 'content-type: application/json' \
--data '{
"fromCurrency": "MXN",
"toCurrency": "ARS",
"fromAmount": "100000"
}'✅ Save: quoteId from the response.
That quoteId becomes the single source of truth for:
- corridor (
fromCurrency,toCurrency) - rate/fees
- expiry behavior
3) Create Deposit with Auto-Payout Metadata
This creates the “deposit order” that:
- generates deposit instructions (where sender deposits funds)
- links the deposit to
quoteId - links the payout destination via
metadata.fiatAccountId
Endpoint
POST /deposit
Example (Deposit Intent referencing quoteId + beneficiary fiatAccountId)
curl --request POST \
--url 'https://penny-api-restricted-dev.alfredpay.io/api/v1/third-party-service/penny/deposit' \
--header 'Content-Type: application/json' \
--header 'accept: */*' \
--header 'api-key: xxxxxxx' \
--header 'api-secret: xxxxxxx' \
--data '{
"customerId": "48ab353c-b1f5-438c-affc-3ba0fb694e1c",
"quoteId": "a38fe669-e754-47e8-a629-23c1902314c4",
"fromCurrency": "MXN",
"toCurrency": "USD",
"amount": "300",
"paymentMethodType": "BANK",
"metadata": {
"fiatAccountId": "b57e8612-0ab2-47d4-b353-ecaa6374c192"
}
}'What you should expect back
Your deposit response should include:
- a deposit/onramp identifier (e.g.,
TransactionId) - deposit instructions (account details + required reference/memo)
- status indicating it is awaiting deposit
✅ Save the TransactionID identifier + deposit reference/instructions. That reference is what the sender must include (memo/reference field) for auto-matching. The sender is expected to complete a push payment to the account provided in the deposit instructions.
What happens after Step 3 (Execution Lifecycle)
Once the sender deposits funds using the instructions/reference from Step 3:
- Penny detects & matches inbound funds to the deposit intent
- Penny emits FIAT_DEPOSIT_RECEIVED
- Penny executes the corridor defined by
quoteId(FX/fees) - Penny initiates payout to the beneficiary referenced by
metadata.fiatAccountId - Penny emits payout lifecycle events:
- FIAT_TRANSFER_INITIATED
- Then FIAT_TRANSFER_COMPLETED marking the beneficiary has been paid out.
Webhooks to listen for (Recommended)
To run this flow reliably, subscribe to webhooks for the lifecycle.
Minimum recommended events:
FIAT_DEPOSIT_RECEIVED(Deposit received)TRADE_COMPLETED(FX Completed)FIAT_TRANSFER_INITIATED(payout created)FIAT_TRANSFER_COMPLETED(payout completed)
{
"eventType": "FIAT_DEPOSIT_RECEIVED",
"timestamp": "2026-01-15T19:02:11Z",
"data": {
"onrampId": "or_01HZX33B2A3G7Z0KXKQ9Y7D5V1",
"sourceAmount": "50000.00",
"sourceCurrency": "MXN",
"senderName": "Empresa ABC SA de CV",
"senderBankName": "BBVA México",
"reference": "ALF-OR-01HZX33B2A3G7Z0KXKQ9Y7D5V1"
}
}Minimal “Do This” Checklist
-
Create beneficiary
- call
POST /fiatAccountswithisExternal: true - store
fiatAccountId
- call
-
Create quote
- call
POST /quotes - store
quoteIdand expiry
- call
-
Create deposit intent
-
call
POST /depositwith:quoteIdmetadata.fiatAccountId
-
show sender the deposit instructions + required reference
-
-
Listen to webhooks
- confirm deposit received, payout initiated, payout settled
Updated about 1 month ago
