Payments
Payments in Caliza refer to the process of transferring funds from a beneficiary to a recipient. To manage payments, follow a two-step process:
- Simulate the payment to get a quote.
- Execute the payment based on the simulation.
See the sections below for more details on each step.
Simulate the payment
To simulate a payment using the Caliza API, you can use the following cURL command. Make sure to replace the following:
YOUR_TOKENwith your actual API tokenbeneficiaryIdwith the ID of the beneficiary you want to simulate the payment for.
See an example request and response below:
See an example request below:
curl -s --location --request POST 'https://api.sandbox.caliza.com/core-api/v2/simulations' \
--header "Authorization: Bearer Your_Token" \
--header 'Content-Type: application/json' \
--data '{
"from": {
"currencyCode": "USDC",
"value": 500
},
"to": {
"currencyCode": "USD"
},
"beneficiaryId": "69261109adcc7b2db705a123",
"recipientId": "12ace467-8189-42ec-8b21-0a5bf7cd7f5e",
"funding": "ETH",
"payout": "WIRE"
}'Where:
from: An object containing details about the source of funds, including currency code and value.to: An object containing details about the destination of funds, including currency code.beneficiaryId: The ID of the beneficiary associated with the payment.recipientId: The ID of the recipient where the funds will be sent.funding: The funding rail used for the transaction (e.g., "ETH").payout: The payout rail used for the transaction (e.g., "WIRE").
The response should look like this:
{
"id": "34c9dfe7-12da-4119-8beb-4db895c01a47",
"integratorId": "6920925fdbadfe09b1bf9e5e",
"beneficiaryId": "69261109adcc7b2db705a123",
"from": {
"currencyCode": "USDC",
"value": 500.00
},
"to": {
"currencyCode": "USD",
"value": 495.00
},
"flowOfFundsId": "55b778b2-1d8e-48fb-8229-0386d0f33fe0",
"transactionDetails": {
"conversionDetails": {
"effectiveTransactionValue": 495.00,
"marketExchangeRate": 1.00
},
"feeDetails": {
"totalFees": {
"currencyCode": "USDC",
"value": 5.00
},
"integratorFee": {
"currencyCode": "USDC",
"value": 0.00
},
"taxes": {
"currencyCode": "USDC",
"value": 0.00
}
}
},
"executed": false,
"guaranteedRate": true,
"rateValidityInMinutes": 15,
"expiresAt": "2025-11-27T15:00:00Z",
"createdDate": "2025-11-27T14:45:00Z",
"lastModifiedDate": "2025-11-27T14:45:00Z"
}Where:
id: The unique identifier for the simulation.integratorId: The ID of the integrator associated with the simulation.beneficiaryId: The ID of the beneficiary associated with the simulation.from: An object containing details about the source of funds, including currency code and value.to: An object containing details about the destination of funds, including currency code and value.flowOfFundsId: The unique identifier for the flow of funds associated with the simulation.transactionDetails: An object containing details about the transaction, including conversion details and fee details.executed: A boolean indicating whether the simulation has been executed.guaranteedRate: A boolean indicating whether the rate is guaranteed.rateValidityInMinutes: The validity period of the rate in minutes.expiresAt: The expiration date and time of the simulation.createdDate: The date and time when the system created the simulation.lastModifiedDate: The date and time when the system last modified the simulation.
Adding your own spread
When a payment involves a cross-currency payout — the beneficiary holds stablecoins but the recipient is paid in a local currency (for example USDC → BRL over PIX, or USDC → MXN over SPEI) — the payment includes a currency-conversion (FX) leg. On that leg you can add your own spread on top of Caliza's spread, and that spread becomes your revenue. You set it by passing a customFees object with an INTEGRATOR_SPREAD value on the simulation request.
- The value is expressed in basis points (BPS), where
100 BPS = 1%. A value of10means a0.10%markup. - The value must be
≥ 0. Passing0(or omittingcustomFees) disables your spread; Caliza's spread always applies. - It only affects the currency-conversion (FX) leg. Same-currency payouts (for example a
USD → USDwire) have no FX leg and are unaffected.
How the rates relate
A converted amount is built up in two markup tiers on top of the raw market rate. Each margin is revenue for a different party:
| Rate | Public field | What it is | Revenue gap |
|---|---|---|---|
| Market rate | marketExchangeRate | Raw mid-market FX rate | — |
| Caliza rate | calizaRate | Market rate + Caliza spread | Market → Caliza = Caliza's revenue |
| Integrator rate | integratorRate | Caliza rate + your spread | Caliza → Integrator = your revenue |
The two margins stack, so the integrator rate is the least favorable of the three. The recipient is paid the amount converted at the integrator rate.
Request
curl -s --location --request POST 'https://api.sandbox.caliza.com/core-api/v2/simulations' \
--header "Authorization: Bearer Your_Token" \
--header 'Content-Type: application/json' \
--data '{
"from": {
"currencyCode": "USDC",
"value": 500
},
"to": {
"currencyCode": "BRL"
},
"customFees": {
"INTEGRATOR_SPREAD": 100
},
"beneficiaryId": "69261109adcc7b2db705a123",
"recipientId": "12ace467-8189-42ec-8b21-0a5bf7cd7f5e",
"funding": "ETH",
"payout": "PIX"
}'Where customFees.INTEGRATOR_SPREAD is your spread in basis points (here 100 BPS = 1%).
Response
The simulation (and later the transaction) reports the rate tiers under conversionDetails, and each margin as an item under feeDetails.spreadFees[] — a CALIZA item for Caliza's revenue and an INTEGRATOR_SPREAD item for yours:
"transactionDetails": {
"conversionDetails": {
"effectiveTransactionValue": 2633.76,
"marketExchangeRate": 5.40000000,
"clientRate": 5.32019704,
"calizaRate": 5.32019704,
"integratorRate": 5.26752183
},
"feeDetails": {
"spreadFees": [
{
"type": "CALIZA",
"spreadBasisPoint": 150,
"marketRate": 5.40000000,
"effectiveRate": 5.32019704,
"calizaRate": 5.32019704,
"calculatedFee": 39.90
},
{
"type": "INTEGRATOR_SPREAD",
"spreadBasisPoint": 100,
"marketRate": 5.40000000,
"effectiveRate": 5.26752183,
"calizaRate": 5.26752183,
"calculatedFee": 26.34
}
]
}
}Worked example
Paying out 500 USDC → BRL at a market rate of 5.40, with a Caliza spread of 150 BPS and your spread of 100 BPS:
- At the market rate (
5.40000000):500 × 5.40 = 2700.00 BRL. - At the Caliza rate (
5.32019704):500 × 5.32019704 ≈ 2660.10 BRL. The39.90 BRLgap is Caliza's revenue (theCALIZAspread item). - At the integrator rate (
5.26752183):500 × 5.26752183 ≈ 2633.76 BRL. The26.34 BRLgap is your revenue (theINTEGRATOR_SPREADspread item).
The recipient is paid 2633.76 BRL — the amount converted at the integrator rate.
Passing
INTEGRATOR_SPREAD: 0or omittingcustomFeesdisables your spread, sointegratorRateequalscalizaRateand noINTEGRATOR_SPREADitem is returned. Caliza's spread always applies regardless.
Execute the payment
After simulating and agreeing on the from and to values, you'll need to start the payment.
To execute the payment using the Caliza API, you can use the following cURL command. Make sure to replace YOUR_TOKEN with your actual API token and simulationId with the ID of the simulation you want to execute:
curl 'https://api.sandbox.caliza.com/core-api/v1/payments' \
--header 'Authorization: Bearer YOUR_TOKEN' \
--header 'Content-Type: application/json' \
--data '{
"simulationId": "{{simulationId}}",
"beneficiaryIp": "{{beneficiaryIp}}"
}'Where:
simulationId: The ID of the simulation you want to execute.beneficiaryIp: The IP address of the beneficiary initiating the payment.
The response includes details about the executed payment, including its status and timestamps. The response should look like this:
{
"id": "5d097212-9902-4d86-beec-c578640105f4",
"simulationId": "34c9dfe7-12da-4119-8beb-4db895c01a47",
"integratorId": "6920925fdbadfe09b1bf9e5e",
"beneficiaryId": "69261109adcc7b2db705a123",
"status": "PROCESSING",
"from": {
"currencyCode": "USDC",
"value": 500.00
},
"to": {
"currencyCode": "USD",
"value": 495.00
},
"transactionDetails": {
"conversionDetails": {
"effectiveTransactionValue": 495.00,
"marketExchangeRate": 1.00
},
"feeDetails": {
"totalFees": {
"currencyCode": "USDC",
"value": 5.00
}
}
},
"localInfo": null,
"blockchainInfo": null,
"createdDate": "2025-11-27T15:00:00Z",
"lastModifiedDate": "2025-11-27T15:00:00Z"
}Where:
id: The unique identifier for the payment.simulationId: The ID of the simulation associated with the payment.integratorId: The ID of the integrator associated with the payment.beneficiaryId: The ID of the beneficiary associated with the payment.status: The current status of the payment (e.g., PROCESSING, COMPLETED).from: An object containing details about the source of funds, including currency code and value.to: An object containing details about the destination of funds, including currency code and value.transactionDetails: An object containing details about the transaction, including conversion details and fee details.localInfo: An object containing local payment information (if applicable).blockchainInfo: An object containing blockchain-related information (if applicable).
Attach supporting documents
For high-value transactions, cross-border payments, or any transaction that requires supporting documentation for compliance — invoices, contracts, purchase orders, proof of goods or services, import/export licenses, and similar — Caliza accepts file attachments at the time the payment is created and lets you retrieve them later for audit or reconciliation. Attached documents are validated against the transaction; see Document validation and review for how validation works, what happens when a document is required but missing, and how to resolve a held transaction.
There are two endpoints involved:
| Endpoint | Purpose |
|---|---|
POST /v1/payments/:withDocuments | Create a payment and attach one or more supporting documents in the same request. |
POST /v1/transactions/:withDocuments | Equivalent endpoint under the Transaction API. Use whichever matches your existing integration. |
POST /v1/transactions/{id}/documents | Attach a single supporting document to a transaction that already exists, and trigger document validation. |
GET /v1/transactions/{id}/:withDocuments | Retrieve a transaction together with its attached documents (returned with presigned download URLs). |
File size limitEach file must be non-empty and no larger than 10 MB. Requests containing an empty file or a file that exceeds the limit are rejected with
400 Bad Request(File is emptyorFile size exceeds maximum allowed size of 10MB).
Create a payment with documents
The endpoint accepts a multipart/form-data body with two parts:
request— the same JSON payload you would send toPOST /v1/payments, serialized as a string.files— one or more file parts (repeat the field name to attach multiple files).
curl --location 'https://api.sandbox.caliza.com/core-api/v1/payments/:withDocuments' \
--header 'Authorization: Bearer YOUR_TOKEN' \
--form 'request="{\"simulationId\":\"{{SIMULATION_ID}}\",\"beneficiaryIp\":\"127.0.0.1\"}";type=application/json' \
--form 'files=@"./invoice.pdf"' \
--form 'files=@"./contract.pdf"'Where:
request: A JSON-encoded payment payload, identical in shape to the body ofPOST /v1/payments. At minimum it must includesimulationIdandbeneficiaryIp.files: One file part per attachment. Repeat the form field to send multiple files in a single request.
The response is the same TransactionResponse returned by POST /v1/payments. The transaction is created synchronously; the documents are uploaded to storage and linked to the transaction asynchronously after the response is returned.
{
"id": "5d097212-9902-4d86-beec-c578640105f4",
"simulationId": "34c9dfe7-12da-4119-8beb-4db895c01a47",
"integratorId": "6920925fdbadfe09b1bf9e5e",
"beneficiaryId": "69261109adcc7b2db705a123",
"status": "PROCESSING",
"from": { "currencyCode": "USDC", "value": 500.00 },
"to": { "currencyCode": "USD", "value": 495.00 },
"createdDate": "2025-11-27T15:00:00Z",
"lastModifiedDate": "2025-11-27T15:00:00Z"
}
Document upload is best-effortThe transaction is created first, then files are uploaded. A successful
2xxresponse confirms that the transaction was created. If the file upload step fails afterwards, the transaction itself is not rolled back. UseGET /v1/transactions/{id}/:withDocumentsto confirm which files were stored.
Retrieve a transaction with its documents
Once a payment has been created with attachments, you can fetch the transaction together with metadata for each stored document, including a presigned URL for direct download.
curl --location 'https://api.sandbox.caliza.com/core-api/v1/transactions/{{TRANSACTION_ID}}/:withDocuments' \
--header 'Authorization: Bearer YOUR_TOKEN'{
"id": "5d097212-9902-4d86-beec-c578640105f4",
"status": "SUCCEEDED",
"from": { "currencyCode": "USDC", "value": 500.00 },
"to": { "currencyCode": "USD", "value": 495.00 },
"documents": [
{
"id": "9f0e1a2b-1111-4d2a-9f07-1a3b2c4d5e6f",
"name": "invoice.pdf",
"size": 248321,
"viewUrl": "https://caliza-documents.s3.amazonaws.com/integrators/.../invoice.pdf?X-Amz-Signature=..."
},
{
"id": "1c2d3e4f-2222-4abc-9876-aabbccddeeff",
"name": "contract.pdf",
"size": 512004,
"viewUrl": "https://caliza-documents.s3.amazonaws.com/integrators/.../contract.pdf?X-Amz-Signature=..."
}
]
}Where, for each entry in documents:
id: The unique identifier of the stored document.name: The original filename submitted at upload time.size: The size of the file in bytes.viewUrl: A short-lived presigned URL that can be used to download the file directly. Treat this URL as sensitive — anyone holding it can retrieve the file until it expires.
Upload a document to an existing transaction
When you need to attach a supporting document after a transaction has already been created — for example, to provide a document a transaction is waiting on, or to replace one that was rejected — use POST /v1/transactions/{id}/documents. Uploading a document through this endpoint triggers document validation for the transaction.
The endpoint accepts a multipart/form-data body with a single file part:
curl --location 'https://api.sandbox.caliza.com/core-api/v1/transactions/{{TRANSACTION_ID}}/documents' \
--header 'Authorization: Bearer YOUR_TOKEN' \
--form 'file=@"./invoice.pdf"'Where:
file: A single file part containing the supporting document. To attach several documents, call the endpoint once per file.
The response is a DocumentDto describing the stored document, including a presigned URL for download.
{
"id": "9f0e1a2b-1111-4d2a-9f07-1a3b2c4d5e6f",
"name": "invoice.pdf",
"size": 248321,
"viewUrl": "https://caliza-documents.s3.amazonaws.com/integrators/.../invoice.pdf?X-Amz-Signature=..."
}
Documents cannot be added to a finalized transactionA document can only be uploaded while the transaction is still in progress (including while it is held for document review). Once the transaction reaches a final state — for example completed, failed, cancelled, returned, or refunded — the request is rejected with
409 Conflict(transaction.document_upload_not_allowed_for_status).
To understand what happens after a document is uploaded — how validation runs and how it can release a transaction that is held for review — see Document validation and review.
Get the payment status
To check the status of a payment using the Caliza API, you can use the following cURL command.
See the example request below:
curl --location --globoff 'https://api.sandbox.caliza.com/core-api/v1/payments/{{payment_id}}' \
--header 'Authorization: Bearer YOUR_TOKEN'Where payment_id is the ID of the payment you want to check.
The response should look like this:
{
"id": "5d097212-9902-4d86-beec-c578640105f4",
"simulationId": "34c9dfe7-12da-4119-8beb-4db895c01a47",
"integratorId": "6920925fdbadfe09b1bf9e5e",
"beneficiaryId": "69261109adcc7b2db705a123",
"status": "SUCCEEDED",
"from": {
"currencyCode": "USDC",
"value": 500.00
},
"to": {
"currencyCode": "USD",
"value": 495.00
},
"transactionDetails": {
"conversionDetails": {
"effectiveTransactionValue": 495.00,
"marketExchangeRate": 1.00
},
"feeDetails": {
"totalFees": {
"currencyCode": "USDC",
"value": 5.00
}
}
},
"localInfo": null,
"createdDate": "2025-11-27T15:00:00Z",
"lastModifiedDate": "2025-11-27T15:05:00Z",
"settledAt": "2025-11-27T15:05:00Z"
}Where:
id: The unique identifier for the payment.simulationId: The ID of the simulation associated with the payment.integratorId: The ID of the integrator associated with the payment.beneficiaryId: The ID of the beneficiary associated with the payment.status: The current status of the payment (e.g., SUCCEEDED, FAILED).from: An object containing details about the source of funds, including currency code and value.to: An object containing details about the destination of funds, including currency code and value.transactionDetails: An object containing details about the transaction, including conversion details and fee details.localInfo: An object containing local payment information (if applicable).createdDate: The date and time when the system created the payment.lastModifiedDate: The date and time when the system last modified the payment.settledAt: The date and time when the payment was settled (if applicable).
This allows you to monitor the payments made by beneficiaries and track their statuses effectively.
SWIFT payment status tracking
For payments sent over the SWIFT network, Caliza continuously tracks the payment and provides status updates as information becomes available from banks in the payment chain. The possible SWIFT-specific statuses are:
| Status | Meaning |
|---|---|
PAYMENT_SENT | Caliza has executed the payment and it is in process. For many SWIFT payments, this will be the last status update. |
PROCESSED_BY_INTERMEDIARY_BANK | The payment has successfully moved through an intermediary bank in the SWIFT chain. |
PAYMENT_COMPLETED | The beneficiary bank has confirmed receipt of the funds and credited them to the recipient's account. |
Not all SWIFT payments will progress through every status. After
PAYMENT_SENT, further updates depend on other banks in the chain reporting back through the SWIFT network. Caliza tracks payments continuously and will notify you via webhook if additional information is received.
Webhook events for payments
When a payment is completed, Caliza will send a webhook event to notify you about the successful payment. The relevant webhook event for payments completed is as follows:
{
"operation": "TRANSACTION_COMPLETED",
"resourceId": "{{payment_id}}",
"createdAt": "2025-04-17T22:10:23.476151179",
"integratorId": "{{integrator_id}}",
"beneficiaryId": "{{beneficiary_id}}",
"message": null,
"success": true,
"data": {
"callbackType": "transactionCallback",
"id": "{{payment_id}}",
"integratorId": "{{integrator_id}}",
"beneficiaryId": "{{beneficiary_id}}",
"simulationId": null,
"foFTransactionType": "REGULAR_TRANSACTION",
"amountToBeConverted": {
"currencyCode": "USDC",
"value": 100.00
},
"from": {
"currencyCode": "USD",
"value": 100.00
},
"to": {
"currencyCode": "USD",
"value": 100.00
},
"totalFees": {
"currencyCode": "USD",
"value": 0.0000
},
"totalTaxes": {
"currencyCode": "USD",
"value": 0.0000
},
"status": "PAYMENT_COMPLETED", // CREATED, PROCESSING, PAYMENT_SENT, PAYMENT_COMPLETED
"fiatAccountId": null,
"recipientId": null,
"fundingWallet": {{funding_wallet_address}},
"targetWallet": null,
"depositTransactionHash": null,
"withdrawalTransactionHash": null,
"flowOfFundsId": {{flow_of_funds_id}},
"localTargetAccount": null,
"exchangeRate": null,
"settlement": false
}
}Where:
operation: The type of operation that triggered the webhook (for example,TRANSACTION_COMPLETED).resourceId: The ID of the transaction associated with the webhook event.createdAt: The timestamp when the webhook event was created.integratorId: The ID of the integrator associated with the transaction.beneficiaryId: The ID of the beneficiary associated with the transaction.data: An object containing detailed information about the transaction, including its status and amounts.
You can use these webhook events to track payment status and take appropriate actions based on each transaction.
Related Articles
Updated 4 days ago
