Transaction
The Transaction endpoint is the culmination of the remittance flow. It links together the sender, recipient, funding source, recipient account, and quote to execute a cross-border payment.
Creating a Transaction
Endpoint: POST /organizations/{tenant}/fx/transactions
Authentication: Agent-level (x-api-key + x-agent-id + x-agent-api-key)
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
senderId | string (UUID) | Yes | The participant ID of the sender (from POST /people) |
recipientId | string (UUID) | Yes | The participant ID of the recipient (from POST /people) |
fundingAccountId | string (UUID) | Yes | The sender's funding source (from POST /fundingAccounts) |
recipientAccountId | string (UUID) | Yes | The recipient's payout account (from POST /recipientAccounts/gateway) |
quoteId | string (UUID) | Yes | The locked FX quote (from POST /payout/quotes) |
externalId | string | No | Your internal reference ID for reconciliation. Returned in webhooks as externalTransactionId. |
deviceData | object | Conditional | Device intelligence for fraud prevention (see below) |
deviceData.userIpAddress | string | Conditional | The end user's IP address. Required when fingerprinting is enabled for your tenant (the default). |
deviceData.fingerprint | string | Conditional | The requestId from the device fingerprint library. Required when fingerprinting is enabled (the default). |
deviceData.visitor | string | Recommended | The visitorId from the device fingerprint library |
additionalData | object | No | Legacy location for fingerprint and visitor (both accepted), plus country-specific fields per the transaction schema |
sendingReason | string | No | Purpose of the transfer (e.g., "Family support", "Payment for services") |
senderReceiverRelationship | string | No | Relationship between sender and recipient (e.g., "Family member", "Business partner") |
sourceOfFunds | string | No | Origin of funds (e.g., "Salary", "Savings", "Business income") |
Device Fingerprint
Most tenants have device fingerprinting enabled (it's the platform default). When enabled, every transaction must include a fingerprint and the user's IP address, or the request is rejected. Integrate a device fingerprint library into your client application β Inyo provides the API keys for the fingerprint service during onboarding.
Before submitting a transaction, call the fingerprint library on the client side and pass the results:
deviceData.fingerprintβ therequestIdfrom the fingerprint librarydeviceData.visitorβ thevisitorIdfrom the fingerprint librarydeviceData.userIpAddressβ the end user's IP address
Send BOTH
fingerprintandvisitor. The payment gateway's server-side device-intelligence integration only runs when both values are present. Sending only one silently drops the device-intelligence block from the gateway call, which reduces fraud-scoring accuracy and can contribute to declines.
fingerprintandvisitorare also accepted insideadditionalData(legacy format).deviceDatais the preferred location.
Sample Request
curl --request POST \
--url https://{FQDN}/organizations/$TENANT/fx/transactions \
--header 'Content-Type: application/json' \
--header "x-api-key: $API_KEY" \
--header "x-agent-id: $AGENT_ID" \
--header "x-agent-api-key: $AGENT_KEY" \
--data '{
"externalId": "your-internal-reference-123",
"senderId": "422a55b2-78c3-4509-8654-16e7375d3e40",
"recipientId": "4b9dcb4e-2e2a-4e96-b2a9-fcd868b2f4ed",
"fundingAccountId": "3fe5f23f-65d3-41b6-b34c-33db3c4d8ef9",
"recipientAccountId": "67203d69-dc48-41d4-b2ac-52682d39b032",
"quoteId": "30bf05d2-b416-4e71-b8e8-1ef158a2b414",
"deviceData": {
"userIpAddress": "200.123.131.112",
"fingerprint": "1727364523875.mchLfB",
"visitor": "jkS9xp4FHlqOSfGR"
},
"sendingReason": "Family support",
"senderReceiverRelationship": "Family member",
"sourceOfFunds": "Salary"
}'
Sample Response (HTTP 202)
{
"id": "9035ee18-052b-4176-a940-ccfe599a1829",
"sender": {
"id": "ce06773d-6472-4c93-bdf5-8eb68ab01c7f",
"type": "PERSON",
"name": "Rob Dalas",
"phoneNumber": "+1 123 456435"
},
"recipient": {
"id": "156675d5-1d1b-4b4d-997e-5c15ae129b82",
"type": "PERSON",
"name": "Bob Danilo",
"phoneNumber": "+55 71 91234-5678"
},
"tenantId": "master",
"externalId": "your-internal-reference-123",
"agentId": "41f55211-1eea-4747-9a03-68e99f07ede5",
"createdAt": "2025-05-29T17:26:27.264312",
"anchorCurrencyAmount": { "amount": "102.53", "currency": "USD" },
"complianceStatus": "Pending",
"payoutStatus": "Pending",
"paymentStatus": null,
"quoteId": "af2d55d8-1d40-426a-8d4a-7454ccdcbe8a",
"fundingAccountId": "61bc14a1-b3a2-40b4-b395-0496e4f554a1",
"recipientAccountId": "4fbc57a6-f673-44ba-88aa-c6e71eb9181b",
"exchangeRate": "5.571062480000000",
"totalAmount": { "amount": "102.53", "currency": "USD" },
"receivingAmount": { "amount": "571.20", "currency": "BRL" },
"fee": { "amount": "1.54", "currency": "USD" },
"conversionAmount": { "amount": "102.53", "currency": "USD" },
"type": "FX",
"senderId": "ce06773d-6472-4c93-bdf5-8eb68ab01c7f",
"recipientId": "156675d5-1d1b-4b4d-997e-5c15ae129b82",
"sendingReason": "Family support",
"receipt": { "..." : "regulatory disclosures" }
}
Response Fields
| Field | Description |
|---|---|
id | Unique transaction ID |
complianceStatus | Current compliance screening result (see lifecycle below) |
payoutStatus | Current payout delivery status (see lifecycle below) |
paymentStatus | ActionRequired when the card requires a 3DS challenge, otherwise null |
redirectAcsUrl | 3DS challenge URL β present only when paymentStatus is ActionRequired |
status.messages | Audit trail of status transitions with timestamps |
exchangeRate | The applied FX rate |
totalAmount | Total amount charged to the sender (in source currency) |
receivingAmount | Amount to be received by the beneficiary (in destination currency) |
fee | Transaction fee in source currency |
conversionAmount | Amount that was converted (source currency) |
receipt | Regulatory disclosures that must be shown to the sender (see below) |
type | Transaction type β FX |
Validation Errors
Validation failures return HTTP 400 with a per-field breakdown:
{
"errors": [
{ "fieldName": "fingerprint", "error": "A fingerprint is required (deviceData.fingerprint or additionalData.fingerprint)." },
{ "fieldName": "deviceData.userIpAddress", "error": "The device IP address is required." }
]
}
Handling 3D Secure (3DS)
When the sender's card requires 3DS authentication, the create response includes:
{
"paymentStatus": "ActionRequired",
"redirectAcsUrl": "https://..."
}
Flow:
- Open
redirectAcsUrlin a WebView or popup. - The user completes 3DS verification on the issuer's page.
- Listen for the
postMessageevent from the payment interface to determine whether the challenge succeeded. - If successful: close the WebView and poll
GET /fx/transactions/{id}β the transaction resumes automatically (payout integration, compliance review, etc.).paymentStatusreturns tonullonce the challenge resolves. - If failed: close the WebView and show an error β the transaction moves to
PaymentDeclinedβCancelled.
If the card is not challenged, the transaction proceeds immediately and redirectAcsUrl is absent. While the challenge is pending, the transaction sits in the WaitingChallenge3ds state.
Never skip the 3DS challenge. A transaction stuck in
WaitingChallenge3dswill not progress until the challenge completes or the transaction is cancelled.
Transaction Status Lifecycle
Every transaction exposes two summary status dimensions on GET /fx/transactions/{id}, plus a fine-grained internal status delivered via webhooks.
Compliance Status
| Status | Description |
|---|---|
Pending | Screening not yet complete |
Approved | Passed compliance checks β transaction can proceed |
Rejected | Failed compliance review β transaction is blocked |
Cancelled | Transaction was cancelled before completion |
Refunded | Transaction was refunded after payment capture |
Failed | Payment declined or an unrecoverable error occurred |
Payout Status
| Status | Description |
|---|---|
Pending | Transaction accepted, payment leg still in progress |
Processing | Handed to the payout network (MSB), awaiting delivery |
Completed | Funds delivered to the recipient |
Cancelled | Transaction was cancelled |
Failed | Payout rejected by the network, payment declined, or error |
Voided | Payout voided by the payout network |
Refunded | Transaction was refunded |
Detailed Status Flows
The fine-grained statuses below are delivered in TransactionStatusChanged webhooks and in the status history endpoint.
Card (with 3DS):
Created β PaymentProcessing β WaitingChallenge3ds β [user completes 3DS] β PaymentAuthorized β ProcessingPayout β PayoutAccepted β ManualReview β ReviewApproved β PaymentCaptured β WaitingPayout β [payout network confirms delivery] β Paid β Completed
Card (no 3DS):
Created β PaymentProcessing β PaymentAuthorized β ProcessingPayout β PayoutAccepted β ManualReview β ReviewApproved β PaymentCaptured β WaitingPayout β [payout network confirms delivery] β Paid β Completed
ACH:
Created β PaymentProcessing β PaymentAuthorized β ProcessingPayout β PayoutAccepted β WaitingSettlement β [ACH settles] β PaymentSettled β PaymentCaptured β WaitingPayout β [payout network confirms delivery] β Paid β Completed
Exception branches:
PayoutHoldβ the payout network placed a compliance hold; released automatically or by the compliance team (PayoutHoldβPayoutReleasedβ flow resumes)PayoutRejectedβ the payout network rejected the transaction; it is cancelled and the payment reversedManualReviewβReviewRejectedβ the compliance team rejected the payment; it is voidedBlockedPendingReviewβ a risk rule or blocklist match paused the transaction for operator review before proceedingPendingReversalApprovalβ the payout was voided downstream and the reversal is awaiting operator approval (tenant-configurable)CancelRequestedβ you requested cancellation; the reversal is being processed
Depending on your tenant configuration,
ManualReviewmay be auto-approved (all transactions, card-only, or ACH-only), in which case you'll seeManualReviewβReviewApprovedback-to-back.
Querying Transactions
Get Transaction by ID
curl --request GET \
--url https://{FQDN}/organizations/$TENANT/fx/transactions/$TRANSACTION_ID \
--header "x-api-key: $API_KEY" \
--header "x-agent-id: $AGENT_ID" \
--header "x-agent-api-key: $AGENT_KEY"
List Transactions
curl --request GET \
--url "https://{FQDN}/organizations/$TENANT/fx/transactions?page=0&size=20&senderId=$SENDER_ID" \
--header "x-api-key: $API_KEY" \
--header "x-agent-id: $AGENT_ID" \
--header "x-agent-api-key: $AGENT_KEY"
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
page | integer | Page number (0-indexed), default 0 |
size | integer | Results per page, default 20 |
senderId | string (UUID) | Filter by sender participant |
status | string | Filter by compliance status (e.g., APPROVED) |
Response: { "total": <count>, "transactions": [ ... ] }, most recent first.
Get Transaction Status History
Returns the full audit trail of status changes for a transaction, most recent first.
curl --request GET \
--url https://{FQDN}/organizations/$TENANT/fx/transactions/$TRANSACTION_ID/status \
--header "x-api-key: $API_KEY" \
--header "x-agent-id: $AGENT_ID" \
--header "x-agent-api-key: $AGENT_KEY"
Sample Response:
[
{
"id": "2f6b3f12-a63f-43f7-9bb4-4e98dfc75fe5",
"complianceStatus": "Approved",
"payoutStatus": "Pending",
"transactionId": "46b4c393-3768-4ba4-b6d2-a6398e17dd78",
"tenantId": "master",
"externalId": "GMT000648512199",
"createdAt": "2024-01-03T12:54:09.455",
"messages": [
{
"id": "0fbbddb8-283d-466d-b2e6-42dcbb3e67de",
"message": "STANDARD - PAYMENT_RECEIVED",
"createdBy": "system",
"createdAt": "2025-11-19T13:29:52.221882",
"type": "SystemUpdate"
}
]
}
]
Cancelling / Reversing a Transaction
There is a single reversal endpoint. Inyo determines the correct reversal operation based on how far the transaction progressed:
- Payment only pre-authorized β the authorization is voided
- Payment already captured (but funds not yet delivered) β the payment is refunded
Endpoint: PUT /organizations/{tenant}/fx/transactions/{transactionId}/status/cancel
Authentication: Agent-level
curl --request PUT \
--url https://{FQDN}/organizations/$TENANT/fx/transactions/$TRANSACTION_ID/status/cancel \
--header "x-api-key: $API_KEY" \
--header "x-agent-id: $AGENT_ID" \
--header "x-agent-api-key: $AGENT_KEY"
The reversal is processed asynchronously: the transaction moves to CancelRequested, then to Cancelled (voided) or Refunded once the gateway confirms.
Responses:
| Status | Description |
|---|---|
204 | Reversal accepted and queued |
404 | Transaction not found |
422 | Transaction is not in a reversible state |
Once the payout network has delivered funds to the recipient (
Paid/Completed), the transaction can no longer be reversed through the API β contact Inyo support.
Updating Transaction Metadata
Attach arbitrary key-value data to a transaction for your internal tracking. Returns 204.
Endpoint: PUT /organizations/{tenant}/fx/transactions/{transactionId}/metadata
Authentication: Agent-level
curl --request PUT \
--url https://{FQDN}/organizations/$TENANT/fx/transactions/$TRANSACTION_ID/metadata \
--header 'Content-Type: application/json' \
--header "x-api-key: $API_KEY" \
--header "x-agent-id: $AGENT_ID" \
--header "x-agent-api-key: $AGENT_KEY" \
--data '{
"internalRef": "INV-2025-001",
"department": "treasury"
}'
Batch Transactions
For high-volume use cases you can submit multiple transactions in one call. Each item uses the same shape as the single-create request.
| Operation | Method | Endpoint |
|---|---|---|
| Submit batch | POST | /organizations/{tenant}/fx/transactions/batch |
| Retry a failed batch item | POST | /organizations/{tenant}/fx/transactions/batch/{transactionId}/retry |
{ "transactions": [ { "senderId": "...", "recipientId": "...", "...": "..." } ] }
Both endpoints return 202 Accepted; individual transactions progress independently β track them via webhooks or polling.
Transaction Limits
Before executing a transaction, verify the sender has not exceeded their limits. The numbers reported here are computed with the same rules the transaction validator applies, so what shows as available is what a transaction can actually use.
Endpoint: GET /organizations/{tenant}/fx/participants/{participantId}/limits
Authentication: Agent-level
curl --request GET \
--url https://{FQDN}/organizations/$TENANT/fx/participants/$SENDER_ID/limits \
--header "x-api-key: $API_KEY" \
--header "x-agent-id: $AGENT_ID" \
--header "x-agent-api-key: $AGENT_KEY"
Sample Response:
{
"oneDayLimit": {
"limit": { "amount": "2999.00", "currency": "USD" },
"used": { "amount": "0.00", "currency": "USD" },
"available": { "amount": "2999.00", "currency": "USD" }
},
"thirtyDaysLimit": {
"limit": { "amount": "6000.00", "currency": "USD" },
"used": { "amount": "2959.99", "currency": "USD" },
"available": { "amount": "3040.01", "currency": "USD" }
},
"oneHundredAndEightyDaysLimit": {
"limit": { "amount": "9999.00", "currency": "USD" },
"used": { "amount": "5000.00", "currency": "USD" },
"available": { "amount": "4999.00", "currency": "USD" }
}
}
To see which fields are needed to reach the next compliance level (and unlock higher limits), use GET /participants/{id}/complianceLevels β see Trust Level Limits.
Receipts (Regulatory Requirement)
As an agent of a licensed Money Service Business, you are legally required to issue a receipt to the sender immediately after transaction submission. The transaction response includes a receipt object with mandatory regulatory disclosures that must be displayed verbatim to the end user.
The receipt must include:
- Exchange rate β the locked-in rate from the quote
- Fees β total fees charged to the customer
- Total amount β full amount paid by the sender
- Receive amount β exact amount to be received by the beneficiary
- Regulatory disclosures β dynamic legal text specific to the corridor (e.g., right to refund, cancellation policy)
You must not alter the financial data or regulatory text returned by the API. Display it as-is.
Sandbox: Forcing Status Transitions
In sandbox/staging you can simulate external events (gateway callbacks, payout notifications) to drive a transaction through its lifecycle without real money movement. See Sandbox Testing.
Error Responses
| HTTP Status | Cause |
|---|---|
400 | Missing required fields, invalid data format, missing fingerprint/IP, or expired quote |
403 | Agent not approved, or sender doesn't meet compliance requirements |
404 | Referenced transaction/participant/account not found in your tenant |
422 | Business rule violation (e.g., limit exceeded, invalid corridor, non-reversible state) |
All Endpoints
| Operation | Method | Endpoint |
|---|---|---|
| Create transaction | POST | /organizations/{tenant}/fx/transactions |
| Get transaction | GET | /organizations/{tenant}/fx/transactions/{transactionId} |
| List transactions | GET | /organizations/{tenant}/fx/transactions |
| Get status history | GET | /organizations/{tenant}/fx/transactions/{transactionId}/status |
| Cancel / reverse transaction | PUT | /organizations/{tenant}/fx/transactions/{transactionId}/status/cancel |
| Update metadata | PUT | /organizations/{tenant}/fx/transactions/{transactionId}/metadata |
| Submit batch | POST | /organizations/{tenant}/fx/transactions/batch |
| Retry batch item | POST | /organizations/{tenant}/fx/transactions/batch/{transactionId}/retry |
| Check limits | GET | /organizations/{tenant}/fx/participants/{participantId}/limits |
| Get required/optional fields | GET | /organizations/{tenant}/fx/transactions/schema?countryCode={iso2} |
| Force status change (sandbox) | POST | /organizations/{tenant}/fx/transactions/{transactionId}/events |
