Skip to main content
Send payments between internal and external accounts, with automatic exchange rate handling when the currencies differ.

Overview

Every payment goes through POST /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 immediatelyExecute and 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.
Either way the request shape is the same, and the payment rail is chosen from the destination account. The same endpoint sends to UMA addresses by giving the quote a 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)
If you don’t have these set up yet, review the Internal Accounts and External Accounts guides first.

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)
The paymentRail field is optional. If omitted, Grid selects a default rail for the destination. Specify a rail (e.g., ACH, WIRE, RTP, FEDNOW) when you need to control which payment network processes the transfer.
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.
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)
Quote expiration depends on the corridor but is typically ~5 minutes or greater. If expired, create a new quote to get an updated exchange rate.
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:
cURL
The quote comes back with status PROCESSING and the same transactionId it carried at creation.
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 PENDINGPROCESSINGCOMPLETED or FAILED. You’ll receive OUTGOING_PAYMENT.<STATUS> webhooks as the transaction progresses:
If a transaction fails, Grid initiates a refund automatically. You’ll receive 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.

Checking Payment Status

Configure a webhook endpoint to receive real-time notifications when payment status changes:
See the Webhooks guide for complete webhook implementation details including signature verification.

Best Practices

Quote expiration depends on the corridor (typically ~5 minutes or greater). Always check expiration before executing:
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.
Always save transaction and quote IDs for audit trails and support:

Next Steps