Overview
Every payment goes throughPOST /quotes, whether or not the currencies differ. A quote
prices the transfer — the amounts, the fees, and, when the currencies differ, the exchange
rate — and creates the transaction that carries the money.
What varies is when you execute it:
- In one request. Set
immediatelyExecuteand Grid creates and executes the quote together. Use this when you don’t need to put rate or fee details in front of your user before the money moves. - In two steps. Create the quote, show your user what the transfer will cost, then call execute before the quote expires. Use this whenever your UX surfaces rates or fees — which includes same-currency transfers, where there is no exchange rate but there can still be fees worth showing.
UMA_ADDRESS destination.
See Sending payments for that flow.
Prerequisites
Before sending payments, ensure you have:- An active internal account with sufficient balance
- A verified external account for the destination
- Valid API credentials with appropriate permissions
- A webhook endpoint configured to receive payment status updates (recommended)
Checking limits before you quote
Every corridor accepts amounts only within a range. You can read that range before you create a quote, so an out-of-range amount surfaces in your own UI rather than as a400 AMOUNT_OUT_OF_RANGE on POST /quotes.
Which endpoint you use depends on what you know:
Across a corridor
Use the exchange rates endpoint when you know the currencies but not yet the recipient — populating a currency picker, or validating an amount as the sender types it.Success (200 OK)
minSendingAmount and maxSendingAmount are in the smallest unit of sourceCurrency — on
this corridor, $1.00 to $100,000.00.
Omit destinationCurrency to get every corridor available from a source currency, each with
its own bounds and rail. Repeat the parameter to compare a few:
?sourceCurrency=USD&destinationCurrency=INR&destinationCurrency=GBP.
For a specific recipient
Once you have a destination, look it up. The lookup prices the corridor against that particular recipient and returns alookupId you can carry into the quote.
Success (200 OK)
min and max are in the
smallest unit of that entry’s currency — here MX$20.00 to MX$1,850,000.00.
minSendingAmount and maxSendingAmount are in the smallest unit of the response’s
sendingCurrency — $1.09 to $100,000.00 — which is the pair you want when your UI collects
an amount in the sender’s currency.
Pass sendingCurrency when your customer holds more than one currency. Without it the
bounds are priced against the customer’s default currency, which may not be the one they
intend to send from.
GET /receiver/uma/{address} returns the same shape for a UMA recipient, with
receiverUmaAddress in place of accountId. A UMA recipient commonly supports several
currencies, so expect more than one entry in supportedCurrencies, each with its own bounds.
minSendingAmount and maxSendingAmount are omitted when Grid cannot resolve a
sending-side bound for that currency. Fall back to dividing min and max by
estimatedExchangeRate, and treat the result as looser than the real limit — the sending
leg can impose a bound of its own that the converted receiving bound doesn’t reflect.lookupId on POST /quotes to price against the same lookup. It is required for
UMA destinations.
Clearing these bounds doesn’t guarantee the quote succeeds. Cumulative limits are enforced
at quote time and surface separately as
DAILY_VOLUME_LIMIT_EXCEEDED (HTTP 429), and a
per-transaction ceiling can surface as TRANSACTION_SIZE_LIMIT_EXCEEDED. Handle both
alongside AMOUNT_OUT_OF_RANGE.Send a payment
1
Get account IDs
Retrieve the internal account (source) and external account (destination) IDs:Note the
id fields from both the internal and external accounts you want to use.2
Create the quote
Specify the source and destination accounts and the amount to lock:
cURL
Success (201 Created)
Same-currency transfers use this exact request. The two currencies simply match, and
the quote comes back with an
exchangeRate of 1 — the fee fields are still populated.
Add "immediatelyExecute": true to create and execute in this one request and skip the
next two steps.Locked currency side determines which amount is fixed:
SENDING: Lock the sending amount (receiving amount calculated based on exchange rate)RECEIVING: Lock the receiving amount (sending amount calculated based on exchange rate)
remittanceInformation is optional. Use it to send a reference that travels with the payment to the recipient (max 80 characters). This populates the ACH Addenda record, FedNow/RTP remittance information, or wire OBI field depending on the payment rail.purposeOfPayment is optional. Some destinations require it, and some rails carry it on
the payment itself. A business payout to China also needs supporting documents for its
purpose. See Supporting documents.Amounts are specified in the smallest unit of the currency (cents for USD, pence for GBP, etc.). For example,
12550 represents $125.50 USD.3
Review the quote
Before executing, check that:
- The exchange rate is acceptable
- Fees are as expected
- The receiving amount meets requirements
- The quote hasn’t expired (check
expiresAt)
Skip this step by setting
immediatelyExecute on the quote. A same-currency quote has no
exchange rate to review, but check feesIncluded if your UX shows the customer what the
transfer costs.4
Execute the quote
Confirm and execute the quote to initiate the transfer:The quote comes back with
cURL
status PROCESSING and the same transactionId it carried at
creation — unless the customer requires
Strong Customer Authentication, in which
case it returns PENDING_AUTHORIZATION and the transfer waits on that.Once executed, the quote creates a transaction and the transfer begins processing. The
transactionId can be used to track the payment.Real-time funding sources: If your quote uses a real-time funding source (USDC, BTC, RTP, or FedNow), you don’t call the execute endpoint. Instead, send a payment to the account specified in the quote’s
paymentInstructions. Grid detects the deposit and processes the transfer automatically.5
Monitor completion
After execution, a transaction is created and progresses through If a transaction fails, Grid initiates a refund automatically. You’ll receive
PENDING → PROCESSING → COMPLETED or FAILED. You’ll receive OUTGOING_PAYMENT.<STATUS> webhooks as the transaction progresses:OUTGOING_PAYMENT.REFUND_PENDING followed by OUTGOING_PAYMENT.REFUND_COMPLETED or OUTGOING_PAYMENT.REFUND_FAILED. The transaction’s refund object tracks the refund status and reference.For the full state diagram, refund object details, and all webhook scenarios (including bank returns and manual cancellations), see the Transaction Lifecycle guide.
Transaction statuses
For the full state diagram including refund tracking and edge cases, see the Transaction Lifecycle guide.
Supporting documents
A business payout to China needs supporting documents, such as an invoice or a contract. That is a CNY bank transfer to an external account withbeneficiaryType: "BUSINESS". The payout’s
purposeOfPayment decides which documents you need.
Which documents each purpose needs
Which documents each purpose needs
To create a business beneficiary account, see the China tab in
External Accounts. Other payouts don’t
take documents. A quote with Limits
documentIds for any other destination returns
400 INVALID_INPUT.The payout must use one of the purposes below. Any other purposeOfPayment returns
400 INVALID_INPUT, including GOODS_OR_SERVICES and SERVICE_CHARGES. A payment for
services uses the purpose that names the service.Each row is one document to supply. Upload one file for each requirement of your purpose.
Where a requirement lists more than one type, the types are alternatives for that one file.
Declare any one of them as the file’s documentType.The four service purposes share one set of requirements. For example, an
ACCOUNTING_SERVICES payout needs three files: a contract, an invoice, and either a
purchase order or a delivery slip.The requirement ID names the document to supply. The document type names what a file is.
400 DOCUMENTS_REQUIRED reports missing documents by requirement ID.- A quote accepts at most 3 documents.
- Each file fills one requirement. A requirement that accepts several types still takes one file.
- Each file is a PDF, JPEG, or PNG, from 1 to 8,000,000 bytes. Grid detects the format from the file contents, not the file name.
- A document can be used for 24 hours after upload, until its
expiresAt. - A document can be used on one quote only.
- A document belongs to the customer in its
customerId. Only that customer’s quotes can use it. OmitcustomerIdwhen the platform itself is the sender. The document can then be used only on the platform’s own quotes.
Send a payout with documents
Send a payout with documents
1
Upload each file
Upload one file per request with Keep the
POST /payment-documents. You can send the requests in
parallel.cURL
Success (201 Created)
id of each document. To check whether a document can still be used, call
GET /payment-documents/{paymentDocumentId}.2
Create the quote with the document IDs
Pass the IDs in Grid attaches every document to the payment before it returns the quote. The quote lists
their IDs in its
documentIds, along with the purposeOfPayment they support. A request
with documentIds must carry an Idempotency-Key header.cURL
documentIds field. For a document’s details, such as its type and its
ATTACHED status, call GET /payment-documents/{paymentDocumentId}. With
immediatelyExecute: true, Grid attaches the documents before it executes the quote.
Otherwise, execute the quote as in Send a payment.Errors
Errors
If Upload the missing documents, then send the quote request again with a new
documentIds doesn’t fill every requirement of the purpose, POST /quotes returns
400 DOCUMENTS_REQUIRED. details.missingRequirements lists the requirement ID of each
missing document.Idempotency-Key. Treat each requirement ID as an opaque value. Grid may add new ones as
requirements change.Other errors on a quote with documents:A request that fails with a
409, 410, or 424 created no quote. A retry with the same
Idempotency-Key runs the request again.What the payout partner checks
What the payout partner checks
Grid does not check what a document says. The payout partner reviews each document after
Grid attaches it. To avoid a rejected or delayed payout:
- Every document must carry the beneficiary’s stamp. A contract must be stamped by both parties.
- The invoice amount must match the transaction amount.
- The invoice currency must match the payout currency.
- Sender and beneficiary details in the documents must match the details you send through the API.
Payout timing
How long a payout takes is determined by the rail it settles over. Timings below are typical end-to-end times measured from quote execution:These are typical times, not guarantees. Instant rails run continuously — including weekends
and holidays — but still depend on the receiving institution. The slower bank rails settle on
banking hours, so a transfer submitted after a bank’s cutoff, on a weekend, or on a local
holiday starts on the next banking day. Compliance review on a given payment can extend any
of these.
Strong Customer Authentication (EU customers)
Customers in SCA-regulated regions (in practice the EU: EUR / USDC) must confirm payments with Strong Customer Authentication. When it applies, the quote comes backPENDING_AUTHORIZATION carrying an scaChallenge that you authorize
before the transfer is released; for every other customer nothing changes. See
Per-transaction authorization
for the full walkthrough.
Checking Payment Status
Configure a webhook endpoint to receive real-time notifications when payment status changes:Best Practices
Handle quote expiration gracefully
Handle quote expiration gracefully
Quote expiration depends on the corridor (typically ~5 minutes or greater). Always check expiration before executing:
Include descriptive payment references
Include descriptive payment references
Always include meaningful descriptions to help with reconciliation:This makes it easier to match payments in your accounting system and provides context when reviewing transactions.
Store transaction IDs in your system
Store transaction IDs in your system
Always save transaction and quote IDs for audit trails and support: