API reference
Create Payout
Initiate and confirm a payout to a recipient.
Two-step flow
Creating a payout is a two-call sequence: POST /payments/initiate records the
payout intent, then POST /payments/confirm triggers compliance checks and
submits it for settlement. This matches how the backend's PaymentsService
is implemented. There is no single combined "create" call today.
Step 1: POST /api/v1/payments/initiate
Records a payout intent for the current user. This does not move funds yet.
it validates the request and stores a payment record in INITIATED status.
Authentication
Required. Bearer token in the Authorization header.
Authorization: Bearer YOUR_ACCESS_TOKEN
Idempotency-Key: a-client-generated-uuidRequest body
| Field | Type | Required | Description |
|---|---|---|---|
amount | number | Yes | Positive amount in the given currency's major unit. |
currency | string | Yes | 3-letter currency code (e.g. "USD", "INR"). |
destinationAddress | string | Yes | 56-character recipient wallet address. |
memo | string | No | Free-text memo, max 256 characters. |
purposeCode | string | No | RBI FEMA purpose code, format P#### (e.g. P0801). |
invoiceRef | string | No | Your invoice/reconciliation reference, max 128 characters. |
counterpartyName | string | No | Max 256 characters. |
counterpartyCountry | string | No | ISO 3166-1 alpha-2 country code. |
counterpartyBank | string | No | Max 256 characters. |
TODO: confirm public address format
destinationAddress is validated server-side as exactly 56 characters. The
docs intentionally describe it functionally as a "recipient wallet address"
rather than naming the underlying settlement network. Confirm this framing
matches the public SDK's terminology before publishing.
Example request
curl -X POST https://api.lumeo.co.in/api/v1/payments/initiate \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Idempotency-Key: 7d1b6e2a-7c3e-4b9a-9b1a-3e9a2b5d9c44" \
-H "Content-Type: application/json" \
-d '{
"amount": 500.00,
"currency": "USD",
"destinationAddress": "GADQVQHX...REDACTED...56CHARS",
"purposeCode": "P0801",
"invoiceRef": "invoice-2026-0142"
}'const response = await fetch("https://api.lumeo.co.in/api/v1/payments/initiate", {
method: "POST",
headers: {
Authorization: `Bearer ${accessToken}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
amount: 500.0,
currency: "USD",
destinationAddress: "GADQVQHX...REDACTED...56CHARS",
purposeCode: "P0801",
invoiceRef: "invoice-2026-0142",
}),
});
const payment = await response.json();import requests
import uuid
response = requests.post(
"https://api.lumeo.co.in/api/v1/payments/initiate",
headers={
"Authorization": f"Bearer {access_token}",
"Idempotency-Key": str(uuid.uuid4()),
"Content-Type": "application/json",
},
json={
"amount": 500.00,
"currency": "USD",
"destinationAddress": "GADQVQHX...REDACTED...56CHARS",
"purposeCode": "P0801",
"invoiceRef": "invoice-2026-0142",
},
)
payment = response.json()Response: 201 Created
{
"id": "clx1a2b3c4d5e6f7g8h9",
"txnId": "txn-7d1b6e2a-7c3e-4b9a-9b1a-3e9a2b5d9c44",
"userId": "user_123",
"amount": 500.00,
"currency": "USD",
"status": "INITIATED",
"type": "SEND",
"createdAt": "2026-06-30T09:14:22.000Z",
"updatedAt": "2026-06-30T09:14:22.000Z"
}Step 2: POST /api/v1/payments/confirm
Confirms a previously initiated payout. This is what actually triggers compliance checks and queues the payout for execution.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | The id returned from initiate. |
destinationAddress | string | Yes | Must match the value sent to initiate. |
amount | string | Yes | Amount as a string. |
currency | string | Yes | Currency code. |
memo | string | No | Free-text memo. |
vaultId | string | Yes | ID of the funding wallet/vault on your account. |
TODO: untyped request body
The confirm endpoint currently accepts an inline object rather than a
validated DTO class. Confirm with the backend team whether this is
intentional before this page is treated as fully authoritative.
Response: 201 Created
Same payment object shape as initiate, with status updated:
{
"id": "clx1a2b3c4d5e6f7g8h9",
"status": "COMPLIANCE_CHECK",
"amount": 500.00,
"currency": "USD"
}Payment status values
MANUAL_REVIEW fires neither payout.settled nor payout.failed. A handler
that only listens for those two will show the payment as stuck forever with
no error surfaced. UNKNOWN means the settlement layer never confirmed
either way, so the payment is under reconciliation rather than lost.
| Status | Meaning |
|---|---|
INITIATED | Payout intent recorded, not yet confirmed. |
COMPLIANCE_CHECK | Confirmed; undergoing automated compliance review. |
PROCESSING | Compliance passed, payout queued for execution. |
SUBMITTED | Submitted to the settlement layer, awaiting confirmation. |
CONFIRMED / COMPLETED | Funds have settled. Compliance artifacts (FIRA, tax fields) generate automatically for cross-border payouts. |
MANUAL_REVIEW | Flagged for manual compliance review. |
FAILED | Payout could not be completed. See failedReason on the payment object. |
UNKNOWN | Status could not be confirmed with the settlement layer; under reconciliation. |
TODO: confirm exhaustive status list
This list was assembled by reading the current service implementation, not
from a single canonical enum (the status column is a free-text string).
Reconcile with the backend team if a stricter status enum is introduced.
Error responses
| Status | Notes |
|---|---|
400 | BadRequestException — e.g. funding wallet has no usable key. |
401 | Missing or invalid bearer token. |
404 | ERR_WALLET_NOT_FOUND or ERR_PAYMENT_NOT_FOUND. |
429 | Rate limited — initiate allows 10 req/min, confirm allows 5 req/min per user. |
500 | Internal error. Safe to retry with the same Idempotency-Key. |
What happens next
Once a payment reaches CONFIRMED/COMPLETED, it enters the
compliance pipeline: the transaction
is reconciled against the ledger, a FIRA is generated automatically for
cross-border payouts, and tax fields are recalculated. Subscribe to payment
status webhooks rather than polling. See
Handle webhook retries.
Idempotency
Both initiate and confirm accept an Idempotency-Key header. Retrying
the same call with the same key returns the original result instead of
creating a duplicate payment.