API reference
Every Partner API operation, generated from the published OpenAPI description: the scope each one needs, whether sandbox keys can call it, and every status it can return.
- Base URL
https://smartremit.ai/api/partner/v1- Authentication
Authorization: Bearer sr_test_…with a sandbox key, or your live key after go-live. The examples below read the key from$SMARTREMIT_TEST_KEYor$SMARTREMIT_LIVE_KEY: export it in your shell first.
Non-custodial remittance orchestration. SmartRemit never holds funds; partners are the licensed money transmitters. Authenticate every request with a Bearer API key: sr_test_… keys work in the sandbox only (they create and see sandbox transactions, which settle on the mock rail), sr_live_… keys after go-live. Errors are JSON { "error": "…" }: match on the HTTP status, not the text. A body that is not valid JSON is treated as an empty object, so it is refused by field validation rather than as malformed input. Rate limit: 120 requests per minute per partner (all keys together) and 120 per minute per key, in fixed UTC calendar minutes.
Corridors
Where this partner can send.
List your corridors
GET /corridors
Data-driven corridor discovery for the partner resolved from the key: the brand, the send-to-India corridors for each allowed send currency, and the full supported destination set.
- Scope
corridors:read- Sandbox
- Sandbox keys: yes
Responses
| Status | Meaning | Body |
|---|---|---|
200 | Corridors available to this partner. | See the example belowapplication/json |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks corridors:read, or the partner is not active. | Errorapplication/json |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
Example 200 response
{
"brand": "Example Remit",
"corridors": [
{
"source_country": "US",
"source_currency": "USD",
"destination_country": "IN",
"destination_currency": "INR"
}
],
"destinations": [
"IN",
"MX"
]
}curl
curl -X GET 'https://smartremit.ai/api/partner/v1/corridors' \
-H "Authorization: Bearer $SMARTREMIT_TEST_KEY"Try it with a sandbox key
Quotes
FX and fee pricing (nothing is stored).
Price a transfer
POST /quote
FX and fee quote at the platform reference rate. Nothing is stored. Pass destination_country (resolved to its home currency) or destination_currency (default INR); destination_country wins when both are present. Pass sender.phone to price the sender's first transfer at no fee (first_transfer_free: true); this needs a key that also holds transactions:write. Without it, or with an invalid number, the standard fee is shown. The quote is not binding: the transaction's fee is final. Sandbox transfers never use up the free first transfer.
- Scope
quote- Sandbox
- Sandbox keys: yes
Request body (required, JSON)
{
"amount_source": 200,
"source_currency": "USD",
"destination_country": "IN",
"sender": {
"phone": "12025550123"
}
}Responses
| Status | Meaning | Body |
|---|---|---|
200 | The quote. first_transfer_free is true when the fee is waived as the sender's first transfer. | See the example belowapplication/json |
400 | Invalid request: amount_source is not a positive number, destination_country or destination_currency is not supported, or the amount is outside the allowed range. | — |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks quote, or the partner is not active. | Errorapplication/json |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
503 | Live FX is temporarily unavailable. Nothing was stored; retry later. | — |
Example 200 response
{
"amount_source": 200,
"source_currency": "USD",
"fee_source": 1.99,
"total_charge_source": 201.99,
"amount_destination": 17040,
"destination_currency": "INR",
"fx_rate": 85.2,
"first_transfer_free": false
}curl
curl -X POST 'https://smartremit.ai/api/partner/v1/quote' \
-H "Authorization: Bearer $SMARTREMIT_TEST_KEY" \
-H 'Content-Type: application/json' \
-d '{
"amount_source": 200,
"source_currency": "USD",
"destination_country": "IN",
"sender": {
"phone": "12025550123"
}
}'Try it with a sandbox key
Beneficiaries
Payout-field validation and stored recipients.
Store a beneficiary
POST /beneficiaries
Validate and store a partner-scoped beneficiary. Payout details are encrypted at rest. Not idempotent: a retry stores a second beneficiary.
- Scope
beneficiaries:write- Sandbox
- Sandbox keys: no
Request body (required, JSON)
{
"name": "Ravi Example",
"country": "IN",
"payout_method": "bank",
"fields": {
"accountNumber": "123456789012",
"ifsc": "HDFC0001234"
}
}Responses
| Status | Meaning | Body |
|---|---|---|
201 | Created: { id, name, country, payout_destination }. | — |
400 | name is missing or not a clean name (1–80 characters, no brackets or control characters), payout_method is not bank, upi or usdc, or country is missing. | — |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks beneficiaries:write (test keys never hold it), or the partner is not active. | Errorapplication/json |
422 | One or more payout fields are invalid. The error string is a JSON-encoded map of field name to message. | — |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
curl
curl -X POST 'https://smartremit.ai/api/partner/v1/beneficiaries' \
-H "Authorization: Bearer $SMARTREMIT_LIVE_KEY" \
-H 'Content-Type: application/json' \
-d '{
"name": "Ravi Example",
"country": "IN",
"payout_method": "bank",
"fields": {
"accountNumber": "123456789012",
"ifsc": "HDFC0001234"
}
}'Validate payout fields
POST /beneficiaries/validate
Stateless payout-field validation for a destination country (a pre-check before creating a beneficiary or a transaction). Nothing is stored.
- Scope
beneficiaries:validate- Sandbox
- Sandbox keys: yes
Request body (required, JSON)
{
"country": "IN",
"fields": {
"accountNumber": "123456789012",
"ifsc": "HDFC0001234"
}
}Responses
| Status | Meaning | Body |
|---|---|---|
200 | The fields are valid: { valid: true, payout_destination }. | — |
400 | country is required. | — |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks beneficiaries:validate, or the partner is not active. | Errorapplication/json |
422 | One or more fields are invalid. The error string is a JSON-encoded map of field name to message. | — |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
curl
curl -X POST 'https://smartremit.ai/api/partner/v1/beneficiaries/validate' \
-H "Authorization: Bearer $SMARTREMIT_TEST_KEY" \
-H 'Content-Type: application/json' \
-d '{
"country": "IN",
"fields": {
"accountNumber": "123456789012",
"ifsc": "HDFC0001234"
}
}'Try it with a sandbox key
Transactions
Mint, read, list and confirm transfers.
List your transfers
GET /transactions
Newest-first keyset list, scoped to the partner and the environment of the key.
- Scope
transactions:read- Sandbox
- Sandbox keys: yes
Parameters
| Name | In | Required | Description |
|---|---|---|---|
limit | query | No | Page size (default 25). Values outside 1–100 are clamped to that range; a non-numeric value uses 25. |
cursor | query | No | The next_cursor of the previous page. Treat it as opaque. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | A page of transactions with the next cursor (null on the last page). | — |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks transactions:read, or the partner is not active. | Errorapplication/json |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
curl
curl -X GET 'https://smartremit.ai/api/partner/v1/transactions' \
-H "Authorization: Bearer $SMARTREMIT_TEST_KEY"Try it with a sandbox key
Mint a transfer
POST /transactions
Mint a transfer. The Idempotency-Key header is required: once a transaction exists under a key, every request with that key and a valid body returns that same transaction with its current state (the body is validated but not compared). Sanctions screening runs on every mint in every mode; a hit is 422 and the attempt is recorded as blocked. Today screening runs against a built-in reference rule set, not yet a live commercial AML feed. A transaction created without sender.name is held for manual review. client_reference is optional: your own order number, 1 to 64 letters, digits and . _ : / # - characters. It is saved once and comes back in every answer and in the settlements statement. It is also in the settlement instruction when your own rail pays the transaction out (never when another payout partner does). A status callback may send payout_reference (for example a bank UTR) with paid_out; it is saved once. purpose is required: why the money is sent, one of family_support, gift, education, medical, savings, bills, business or other (exactly these lower-case values). A missing or unknown purpose is 422 before the Idempotency-Key is bound, so a corrected retry with the same key succeeds. The purpose is saved, comes back in every answer and is in the settlement instruction's compliance block. purpose_detail is required when purpose is other: the sender's reason in their own words, 10 to 120 characters that say what the money is for. A missing, too short, too long or meaningless purpose_detail is 422 before the Idempotency-Key is bound. When the reason names one of the other purposes (for example school fees), the transaction takes that purpose and keeps the reason. With any other purpose, purpose_detail is ignored. SmartRemit checks every reason, and some transactions are held for review (compliance_status flagged).
- Scope
transactions:write- Sandbox
- Sandbox keys: yes
Parameters
| Name | In | Required | Description |
|---|---|---|---|
Idempotency-Key | header | Yes | A fresh random UUID per transaction, with no personal data. Values beginning draft:, b2binvoice:, sched:, test: or paylink: are reserved and refused with 400. |
Request body (required, JSON)
{
"amount_source": 200,
"source_currency": "USD",
"client_reference": "INV-2026-0042",
"purpose": "family_support",
"sender": {
"phone": "+12025550142",
"name": "Jane Doe",
"kyc_status": "verified"
},
"beneficiary": {
"name": "Ravi Example",
"payout_method": "bank",
"payout_destination": "123456789012|HDFC0001234"
}
}Responses
| Status | Meaning | Body |
|---|---|---|
200 | Replay of an existing Idempotency-Key (same transaction, current state). | Transactionapplication/json |
201 | Created. | Transactionapplication/json |
400 | Validation failed before the Idempotency-Key was bound: a missing or reserved Idempotency-Key; a missing or invalid amount, sender.phone, beneficiary name or payout destination; a name that is not clean; an unsupported payout_method or destination_country; a client_reference that is not 1 to 64 letters, digits and . _ : / # -; or an amount outside the allowed range. A corrected retry with the same key succeeds. | — |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks transactions:write, or the partner is not active. | Errorapplication/json |
404 | The beneficiary_id was not found for this partner. | — |
409 | Idempotency-Key conflict. Retry with a new key. | — |
422 | Refused: purpose is missing or is not one of family_support, gift, education, medical, savings, bills, business or other, or purpose is other and purpose_detail is missing, not a string, not 10 to 120 characters or does not say what the money is for (all checked before the Idempotency-Key is bound; a corrected retry with the same key succeeds), the payout destination is a masked display value, the stored beneficiary has no usable name, sender identity verification is required, the transfer exceeds the sender's sending limit, or it was blocked by compliance screening. | — |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
503 | Live FX is temporarily unavailable, another transfer for this sender is in progress, or new transfers are paused (code sends_paused, with a Retry-After header in seconds). Nothing was minted; a retry may reuse the same Idempotency-Key. | Errorapplication/json |
curl
curl -X POST 'https://smartremit.ai/api/partner/v1/transactions' \
-H "Authorization: Bearer $SMARTREMIT_TEST_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H 'Content-Type: application/json' \
-d '{
"amount_source": 200,
"source_currency": "USD",
"client_reference": "INV-2026-0042",
"purpose": "family_support",
"sender": {
"phone": "+12025550142",
"name": "Jane Doe",
"kyc_status": "verified"
},
"beneficiary": {
"name": "Ravi Example",
"payout_method": "bank",
"payout_destination": "123456789012|HDFC0001234"
}
}'Fetch one transfer
GET /transactions/{id}
Partner-scoped read. A transaction outside your partner or your key's environment is 404.
- Scope
transactions:read- Sandbox
- Sandbox keys: yes
Parameters
| Name | In | Required | Description |
|---|---|---|---|
id | path | Yes | The transaction id. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | The transaction. | Transactionapplication/json |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks transactions:read, or the partner is not active. | Errorapplication/json |
404 | Transaction not found. | — |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
curl
curl -X GET 'https://smartremit.ai/api/partner/v1/transactions/{id}' \
-H "Authorization: Bearer $SMARTREMIT_TEST_KEY"Try it with a sandbox key
Confirm funds captured
POST /transactions/{id}/confirm
Confirm that funds were captured, which begins settlement. A flagged transfer is held in_review until compliance staff release it. Confirming a transfer that is already paid, delivered or in_review returns it unchanged.
- Scope
transactions:write- Sandbox
- Sandbox keys: yes
Parameters
| Name | In | Required | Description |
|---|---|---|---|
id | path | Yes | The transaction id. A transaction outside your partner or your key's environment is 404. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | The transaction after confirmation (paid, in_review, or already settled). | Transactionapplication/json |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks transactions:write, or the partner is not active. | Errorapplication/json |
404 | Transaction not found. | — |
409 | The transaction is not awaiting payment (for example, it was cancelled). | — |
422 | The transfer was blocked by compliance screening. | — |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
curl
curl -X POST 'https://smartremit.ai/api/partner/v1/transactions/{id}/confirm' \
-H "Authorization: Bearer $SMARTREMIT_TEST_KEY"Rates
The partner's own wholesale conversion rates.
Your rate sheet
GET /rates
Your current rates. fresh says whether the rate is still competing (it has a rate and its expiry is in the future). margin_bps is your platform-configured fallback margin.
- Scope
rates:read- Sandbox
- Sandbox keys: no
Responses
| Status | Meaning | Body |
|---|---|---|
200 | The rate sheet. | See the example belowapplication/json |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks rates:read (test keys never hold it), or the partner is not active. | Errorapplication/json |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
Example 200 response
{
"rates": [
{
"source_currency": "USD",
"destination_currency": "INR",
"effective_rate": 85.1,
"expires_at": "2026-09-23T11:00:00.000Z",
"fresh": true,
"margin_bps": 150
}
]
}curl
curl -X GET 'https://smartremit.ai/api/partner/v1/rates' \
-H "Authorization: Bearer $SMARTREMIT_LIVE_KEY"Push one corridor rate
PUT /rates
Push the wholesale conversion rate you offer for one corridor. When your fresh rate beats the platform reference rate and your settlement rail is configured, eligible platform transfers are routed to you for settlement. A pushed rate does not change the pricing of your own /quote or /transactions: those stay at the platform reference rate. ttl_seconds defaults to 3600 and is clamped to 60–86400; an expired rate stops competing.
- Scope
rates:write- Sandbox
- Sandbox keys: no
Request body (required, JSON)
{
"source_currency": "USD",
"destination_currency": "INR",
"effective_rate": 85.1,
"ttl_seconds": 3600
}Responses
| Status | Meaning | Body |
|---|---|---|
200 | The saved rate. | See the example belowapplication/json |
400 | source_currency or destination_currency is unsupported or the two are equal, effective_rate is not a number greater than 0 and less than 100000, or ttl_seconds is not a number. | — |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks rates:write (test keys never hold it), or the partner is not active. | Errorapplication/json |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
Example 200 response
{
"source_currency": "USD",
"destination_currency": "INR",
"effective_rate": 85.1,
"expires_at": "2026-09-23T11:00:00.000Z",
"pushed_at": "2026-09-23T10:00:00.000Z"
}curl
curl -X PUT 'https://smartremit.ai/api/partner/v1/rates' \
-H "Authorization: Bearer $SMARTREMIT_LIVE_KEY" \
-H 'Content-Type: application/json' \
-d '{
"source_currency": "USD",
"destination_currency": "INR",
"effective_rate": 85.1,
"ttl_seconds": 3600
}'Settlements
The settlements statement for reconciliation.
Settlements statement
GET /settlements
The instruction ledger for reconciliation: every live transfer of yours that was paid and sent for settlement in a half-open [from, to) window on paid_at, in UTC. Oldest first. Listed: paid and delivered transfers, and cancelled ones only if a rail was instructed (see refund_status: none, requested, pending, completed or failed). A transfer held for compliance review (in_review) is never listed; once released it appears on its release day, with paid_at set to the release time. totals cover the returned page only (every listed row, cancelled included), per currency, in integer minor units (cents: 20000 = 200.00). The statement never includes payout account details or sender or recipient identity. reference is the transaction id; on the reference rail, provider_ref and payout_reference are simulated. In CSV, a text cell starting with = + - @, a tab or a carriage return is prefixed with ' so a spreadsheet never runs it as a formula. Any partner_id parameter is ignored. Each row ends with client_reference and payout_reference (the last two CSV columns).
- Scope
settlements:read- Sandbox
- Sandbox keys: no
Parameters
| Name | In | Required | Description |
|---|---|---|---|
from | query | No | Window start: a date (midnight UTC) or a datetime (no zone means UTC). Default: yesterday. |
to | query | No | Window end (exclusive). The window is at most 31 days. |
limit | query | No | Page size, 1–500 (default 100). |
cursor | query | No | The next_cursor of the previous page. Treat it as opaque. |
format | query | No | json (default) or csv. CSV is an attachment with the next cursor in the X-Next-Cursor header. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | The statement page: { settlements, next_cursor, totals, window }, or a CSV attachment. | application/json, text/csv |
400 | Invalid query: from or to is not a date or datetime, to is not after from, the window is longer than 31 days, or limit, cursor or format is invalid. | — |
401 | Missing or invalid key. | Errorapplication/json |
403 | The key lacks settlements:read (test keys never hold it), or the partner is not active. | Errorapplication/json |
429 | Rate limited. Retry after 60 seconds. | Errorapplication/json |
curl
curl -X GET 'https://smartremit.ai/api/partner/v1/settlements' \
-H "Authorization: Bearer $SMARTREMIT_LIVE_KEY"Schemas
Error
| Field | Type | Required | Description |
|---|---|---|---|
error | string | Yes | Human-readable message. Match on the HTTP status, not the text. |
code | string | No | Machine-readable reason, present on some errors only. sends_paused: new transfers are paused for a short time; retry after the Retry-After header. |
Transaction
| Field | Type | Required | Description |
|---|---|---|---|
id | string | No | |
status | string | No | For example awaiting_payment, paid, in_review, delivered, blocked or cancelled. |
compliance_status | string | No | cleared, flagged or blocked. |
amount_source | number | No | Major units in source_currency. |
source_currency | string | No | ISO 4217. |
amount_destination | number | No | Payout amount in destination_currency. |
destination_currency | string | No | ISO 4217. |
destination_country | string | No | ISO 3166-1 alpha-2. |
fee_source | number | No | |
total_charge_source | number | No | |
fx_rate | number | No | Destination units per 1 source unit. |
sender_phone | string | No | |
sender_name | string | null | No | |
recipient_name | string | No | |
funding_method | string | No | |
funding_ref | string | null | No | |
refund_ref | string | null | No | |
created_at | string (date-time) | No | |
partner_id | string | No | |
client_reference | string | null | No | Your own order number, exactly as you sent it on create. Write-once. |
payout_reference | string | null | No | The payout partner's confirmation (for example a bank UTR), from its signed status callback. Set once; null until reported. |
purpose | string | null | No | Why the money is sent, as given on create. When you sent other with a purpose_detail that names one of the other purposes (for example school fees), this is that purpose. Null only on a transaction made before purpose was required. |
purpose_detail | string | null | No | The sender's reason in their own words, sent with purpose other (10 to 120 characters, spaces tidied). Returned only to the partner that owns the transaction. Null when none was given. |