Quote
Request an FX quote with POST /fx/quote. Covers required fields, participant and address requirements, and how to configure source and destination funding, with examples in Go, TypeScript, Java, Rust, and Python.
Overview
POST /fx/quote prices a conversion and returns a quote_id you can later capture to execute. The quote locks the rate (market rate plus your client-specific spread and fees) until valid_until.
POST https://api.sandbox.movmint.io/fx/quote
Authorization: Bearer <access_token>
Idempotency-Key: <uuid>
Content-Type: application/json
| Header | Required | Description |
|---|---|---|
Authorization | yes | Bearer <access_token> from Authentication. |
Idempotency-Key | yes | A UUID you generate. Reuse it on retries to guarantee at-most-once quote creation. See Idempotency. |
Required fields
The request body has four top-level parts:
| Field | Required | Description |
|---|---|---|
from_currency | yes | Source currency/asset code (e.g., BSD, USDC). |
to_currency | yes | Target currency/asset code (e.g., USDC, USD). |
amount | yes | Amount to convert. Interpreted as the source amount when quote_model is fixed_source, or the target amount when fixed_target. |
quote_model | no | fixed_source (default) or fixed_target. See pricing models. |
participants | yes | The people/organizations involved in the transaction (originator and beneficiary). See below. |
tx_configuration | yes | Reference data, purpose of payment, and the source/target funding configuration. |
Participants
participants is an array describing who is sending and receiving. Each participant has a participant_type:
participant_type | Meaning |
|---|---|
ULTIMATE_ORIGINATOR | The party on whose behalf funds originate. |
ORIGINATOR | The sending party. |
BENEFICIARY | The receiving party. |
ULTIMATE_BENEFICIARY | The party who ultimately receives the funds. |
Individual participant
| Field | Required | Notes |
|---|---|---|
participant_type | yes | |
email | yes | |
given_name | yes | |
sur_name | yes | |
phone_number | yes | |
address | one of | Free-text full address. Mutually exclusive with structured_address. |
structured_address | one of | Address split into components — see Address requirements. |
individual_date_of_birth | yes | Format YYYY-MM-DD. |
national_identifier | recommended | Supports compliance screening. |
national_identifier_type | required if national_identifier is provided | One of SSN, TIN, EIN, PASSPORT, DRIVERS_LICENSE, NATIONAL_ID. |
Organization participant
| Field | Required | Notes |
|---|---|---|
participant_type | yes | |
email | yes | |
organization_name | yes | Used instead of given_name / sur_name when the participant is a company. |
phone_number | yes | |
address | one of | Free-text full address. Mutually exclusive with structured_address. |
structured_address | one of | Address split into components — see Address requirements. |
national_identifier | recommended | Supports compliance screening. |
national_identifier_type | required if national_identifier is provided | One of SSN, TIN, EIN, PASSPORT, DRIVERS_LICENSE, NATIONAL_ID. |
ULTIMATE_BENEFICIARY — beneficiary flow
Instead of providing full participant details, you can supply only email to trigger the beneficiary flow. Movmint contacts the beneficiary directly so they can enter their own details and settlement preference:
emailonly → beneficiary receives an emailemail+phone_number→ beneficiary receives an SMS
On this path, no other fields are required.
Address requirements
Participant address is a required free-text field carrying the participant's full postal address (street, city, state/region, postal code, country). It is used for compliance screening and, where the delivery rail requires it (push-to-card, bank transfers), is transmitted to the delivery provider. Provide a complete, accurate address — incomplete addresses can cause delivery rails to reject the transaction.
Every participant on the full path must include exactly one address form — you cannot send both.
Option 1 — Free text (address)
A single string with the full postal address. Movmint parses it into components downstream.
"address": "246 220th Street SE, Bothell, WA 98021, US"Option 2 — Structured (structured_address)
An object with the address already split into components, stored verbatim. Prefer this when you have the components available — nothing is lost to parsing.
| Field | Required | Description |
|---|---|---|
line1 | yes | Street number and name. |
line2 | no | Apartment, suite, unit, etc. |
city | yes | |
state | yes | State, province, or region. |
postal_code | no | Postal or ZIP code. Not all countries use one. |
country | yes | Uppercase ISO 3166-1 alpha-2 code (e.g. US, CA). |
"structured_address": {
"line1": "246 220th Street SE",
"city": "Bothell",
"state": "WA",
"postal_code": "98021",
"country": "US"
}
A contact-onlyULTIMATE_BENEFICIARYomits both — no address is needed when Movmint is collecting the beneficiary's details directly.
Why participant and address data mattersMovmint screens every transaction for sanctions and risk. Originator and beneficiary identity and address data are required inputs to that screening and to the travel-rule obligations of the delivery rails. Supplying complete data up front avoids holds and rejections at capture/delivery time.
Purpose of payment
tx_configuration.purpose_of_payment classifies the transaction for compliance and reporting. It must be one of the platform's accepted values. Choose the value that best describes the underlying purpose. Examples include:
"Payroll Expenses""Goods bought / Goods sold""Contractor Payment""International Money Transfer""Fees for advisory, technical, academic or specialist assistance""Tax Payment""Insurance payment"
Source and destination funding
tx_configuration.source_configuration and tx_configuration.target_configuration describe how funds enter and leave the conversion. Each has a source_type and an account_configuration whose shape depends on that type.
Field namingBoth source and target configurations use the field name
source_typeto indicate the rail. Intarget_configuration,source_typedenotes the target rail.
BANK_ACCOUNT
BANK_ACCOUNT{
"source_type": "BANK_ACCOUNT",
"account_configuration": {
"account_number": "123456789",
"routing_number": "987654321",
"account_type": "CHECKING"
}
}account_type is CHECKING or SAVINGS.
CARD
CARD{
"source_type": "CARD",
"account_configuration": {
"card_number": "4111111111111111",
"expiry_date": "12/2027",
"card_type": "DebitCard"
}
}expiry_date is MM/YYYY. card_type is DebitCard.
DLT_WALLET
DLT_WALLET{
"source_type": "DLT_WALLET",
"account_configuration": {
"wallet_address": "0x123456789abcdef",
"network": "ethereum"
}
}network is one of ethereum, solana, base, sanddollar.
CLIENT_BALANCE
CLIENT_BALANCE{
"source_type": "CLIENT_BALANCE",
"account_configuration": {
"client_account_id": "12345678901234567"
}
}Use a prefunded balance as source or target. The account's asset must match from_currency (as source) or to_currency (as target), and the account must belong to your organization. When used as source, Movmint checks available_balance >= amount at quote time and rejects the quote if the balance is insufficient.
Example request
Convert 1,000 BSD to USDC, funding from a bank account and delivering to a DLT wallet.
cURL
curl -X POST https://api.sandbox.movmint.io/fx/quote \
-H "Authorization: Bearer <access_token>" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"from_currency": "BSD",
"to_currency": "USDC",
"amount": 1000,
"quote_model": "fixed_source",
"participants": [
{
"participant_type": "ORIGINATOR",
"given_name": "Jane",
"sur_name": "Doe",
"email": "[email protected]",
"phone_number": "+12425550100",
"address": "123 Bay Street, Nassau, Bahamas",
"national_identifier": "000-00-0000",
"national_identifier_type": "TIN",
"individual_date_of_birth": "1990-01-25"
},
{
"participant_type": "BENEFICIARY",
"given_name": "John",
"sur_name": "Smith",
"email": "[email protected]",
"phone_number": "+12025550199",
"address": "1600 Market Street, Philadelphia, PA 19103, USA"
}
],
"tx_configuration": {
"client_reference_id": "invoice-4821",
"purpose_of_payment": "Contractor Payment",
"source_configuration": {
"source_type": "BANK_ACCOUNT",
"account_configuration": {
"account_number": "123456789",
"routing_number": "987654321",
"account_type": "CHECKING"
}
},
"target_configuration": {
"source_type": "DLT_WALLET",
"account_configuration": {
"wallet_address": "0x123456789abcdef",
"network": "ethereum"
}
}
}
}'Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"github.com/google/uuid"
)
const baseURL = "https://api.sandbox.movmint.io"
func main() {
token := "<access_token>"
body := map[string]any{
"from_currency": "BSD",
"to_currency": "USDC",
"amount": 1000,
"quote_model": "fixed_source",
"participants": []map[string]any{
{
"participant_type": "ORIGINATOR",
"given_name": "Jane",
"sur_name": "Doe",
"email": "[email protected]",
"phone_number": "+12425550100",
"address": "123 Bay Street, Nassau, Bahamas",
"individual_date_of_birth": "1990-01-25",
},
{
"participant_type": "BENEFICIARY",
"given_name": "John",
"sur_name": "Smith",
"email": "[email protected]",
"phone_number": "+12025550199",
"address": "1600 Market Street, Philadelphia, PA 19103, USA",
},
},
"tx_configuration": map[string]any{
"client_reference_id": "invoice-4821",
"purpose_of_payment": "Contractor Payment",
"source_configuration": map[string]any{
"source_type": "BANK_ACCOUNT",
"account_configuration": map[string]any{
"account_number": "123456789",
"routing_number": "987654321",
"account_type": "CHECKING",
},
},
"target_configuration": map[string]any{
"source_type": "DLT_WALLET",
"account_configuration": map[string]any{
"wallet_address": "0x123456789abcdef",
"network": "ethereum",
},
},
},
}
payload, _ := json.Marshal(body)
req, _ := http.NewRequest(http.MethodPost, baseURL+"/fx/quote", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Idempotency-Key", uuid.NewString())
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
log.Fatalf("quote failed (%d): %s", resp.StatusCode, out)
}
fmt.Println(string(out))
}TypeScript
import { randomUUID } from "crypto";
const BASE_URL = "https://api.sandbox.movmint.io";
async function getQuote(token: string) {
const res = await fetch(`${BASE_URL}/fx/quote`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Idempotency-Key": randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
from_currency: "BSD",
to_currency: "USDC",
amount: 1000,
quote_model: "fixed_source",
participants: [
{
participant_type: "ORIGINATOR",
given_name: "Jane",
sur_name: "Doe",
email: "[email protected]",
phone_number: "+12425550100",
address: "123 Bay Street, Nassau, Bahamas",
individual_date_of_birth: "1990-01-25",
},
{
participant_type: "BENEFICIARY",
given_name: "John",
sur_name: "Smith",
email: "[email protected]",
phone_number: "+12025550199",
address: "1600 Market Street, Philadelphia, PA 19103, USA",
},
],
tx_configuration: {
client_reference_id: "invoice-4821",
purpose_of_payment: "Contractor Payment",
source_configuration: {
source_type: "BANK_ACCOUNT",
account_configuration: {
account_number: "123456789",
routing_number: "987654321",
account_type: "CHECKING",
},
},
target_configuration: {
source_type: "DLT_WALLET",
account_configuration: {
wallet_address: "0x123456789abcdef",
network: "ethereum",
},
},
},
}),
});
if (!res.ok) {
throw new Error(`Quote failed (${res.status}): ${await res.text()}`);
}
return res.json();
}
console.log(await getQuote("<access_token>"));Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.UUID;
public class Quote {
private static final String BASE_URL = "https://api.sandbox.movmint.io";
public static void main(String[] args) throws Exception {
String token = "<access_token>";
String payload = """
{
"from_currency": "BSD",
"to_currency": "USDC",
"amount": 1000,
"quote_model": "fixed_source",
"participants": [
{
"participant_type": "ORIGINATOR",
"given_name": "Jane",
"sur_name": "Doe",
"email": "[email protected]",
"phone_number": "+12425550100",
"address": "123 Bay Street, Nassau, Bahamas",
"individual_date_of_birth": "1990-01-25"
},
{
"participant_type": "BENEFICIARY",
"given_name": "John",
"sur_name": "Smith",
"email": "[email protected]",
"phone_number": "+12025550199",
"address": "1600 Market Street, Philadelphia, PA 19103, USA"
}
],
"tx_configuration": {
"client_reference_id": "invoice-4821",
"purpose_of_payment": "Contractor Payment",
"source_configuration": {
"source_type": "BANK_ACCOUNT",
"account_configuration": {
"account_number": "123456789",
"routing_number": "987654321",
"account_type": "CHECKING"
}
},
"target_configuration": {
"source_type": "DLT_WALLET",
"account_configuration": {
"wallet_address": "0x123456789abcdef",
"network": "ethereum"
}
}
}
}
""";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/fx/quote"))
.header("Authorization", "Bearer " + token)
.header("Idempotency-Key", UUID.randomUUID().toString())
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(payload))
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("Quote failed (" + response.statusCode()
+ "): " + response.body());
}
System.out.println(response.body());
}
}Rust
use serde_json::json;
use uuid::Uuid;
const BASE_URL: &str = "https://api.sandbox.movmint.io";
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let token = "<access_token>";
let body = json!({
"from_currency": "BSD",
"to_currency": "USDC",
"amount": 1000,
"quote_model": "fixed_source",
"participants": [
{
"participant_type": "ORIGINATOR",
"given_name": "Jane",
"sur_name": "Doe",
"email": "[email protected]",
"phone_number": "+12425550100",
"address": "123 Bay Street, Nassau, Bahamas",
"individual_date_of_birth": "1990-01-25"
},
{
"participant_type": "BENEFICIARY",
"given_name": "John",
"sur_name": "Smith",
"email": "[email protected]",
"phone_number": "+12025550199",
"address": "1600 Market Street, Philadelphia, PA 19103, USA"
}
],
"tx_configuration": {
"client_reference_id": "invoice-4821",
"purpose_of_payment": "Contractor Payment",
"source_configuration": {
"source_type": "BANK_ACCOUNT",
"account_configuration": {
"account_number": "123456789",
"routing_number": "987654321",
"account_type": "CHECKING"
}
},
"target_configuration": {
"source_type": "DLT_WALLET",
"account_configuration": {
"wallet_address": "0x123456789abcdef",
"network": "ethereum"
}
}
}
});
let client = reqwest::Client::new();
let resp = client
.post(format!("{BASE_URL}/fx/quote"))
.bearer_auth(token)
.header("Idempotency-Key", Uuid::new_v4().to_string())
.json(&body)
.send()
.await?;
if !resp.status().is_success() {
let status = resp.status();
return Err(format!("quote failed ({status}): {}", resp.text().await?).into());
}
let data: serde_json::Value = resp.json().await?;
println!("{}", serde_json::to_string_pretty(&data)?);
Ok(())
}Python
import uuid
import requests
BASE_URL = "https://api.sandbox.movmint.io"
def get_quote(token: str) -> dict:
resp = requests.post(
f"{BASE_URL}/fx/quote",
headers={
"Authorization": f"Bearer {token}",
"Idempotency-Key": str(uuid.uuid4()),
"Content-Type": "application/json",
},
json={
"from_currency": "BSD",
"to_currency": "USDC",
"amount": 1000,
"quote_model": "fixed_source",
"participants": [
{
"participant_type": "ORIGINATOR",
"given_name": "Jane",
"sur_name": "Doe",
"email": "[email protected]",
"phone_number": "+12425550100",
"address": "123 Bay Street, Nassau, Bahamas",
"individual_date_of_birth": "1990-01-25",
},
{
"participant_type": "BENEFICIARY",
"given_name": "John",
"sur_name": "Smith",
"email": "[email protected]",
"phone_number": "+12025550199",
"address": "1600 Market Street, Philadelphia, PA 19103, USA",
},
],
"tx_configuration": {
"client_reference_id": "invoice-4821",
"purpose_of_payment": "Contractor Payment",
"source_configuration": {
"source_type": "BANK_ACCOUNT",
"account_configuration": {
"account_number": "123456789",
"routing_number": "987654321",
"account_type": "CHECKING",
},
},
"target_configuration": {
"source_type": "DLT_WALLET",
"account_configuration": {
"wallet_address": "0x123456789abcdef",
"network": "ethereum",
},
},
},
},
timeout=30,
)
resp.raise_for_status()
return resp.json()
print(get_quote("<access_token>"))Example response (200)
200){
"quote_id": "123e4567-e89b-12d3-a456-426614174000",
"from_currency": "BSD",
"to_currency": "USDC",
"input_amount": 1000,
"quoted_amount": 998,
"expiry_seconds": 30,
"valid_until": "2026-06-24T12:05:00Z",
"created_at": "2026-06-24T12:04:30Z"
}| Field | Description |
|---|---|
quote_id | Identifier used to capture and execute the quote. |
input_amount | The input amount being converted. |
quoted_amount | The amount in the target currency at the locked rate. |
expiry_seconds | Seconds the quote remains valid from creation. |
valid_until | Absolute expiry timestamp — capture before this time. |
created_at | When the quote was created. |
Error responses
| HTTP | Cause |
|---|---|
400 | Invalid request parameters (missing required fields, asset mismatch on a CLIENT_BALANCE config, insufficient client balance, etc.). |
409 | A quote workflow is already in progress for this request (often a replayed idempotency key mid-flight). |
500 | Internal server error — retry with backoff. |
Next steps
Take the quote_id and capture the quote before valid_until to execute the conversion.
Updated 29 days ago
