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>
x-idempotency-key: <uuid>
Content-Type: application/json
| Header | Required | Description |
|---|---|---|
Authorization | yes | Bearer <access_token> from Authentication. |
x-idempotency-key | yes | A UUID you generate. Reuse it on retries to guarantee at-most-once quote creation. |
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. |
Required for every participant: participant_type, email, phone_number, and address.
Additional requirements:
sur_nameandgiven_nameare required forBENEFICIARYandULTIMATE_BENEFICIARY.individual_date_of_birthis required when the participant is an individual (formatYYYY-MM-DD).organization_nameis used instead of a personal name when the participant is a company.national_identifierwithnational_identifier_type(one ofSSN,TIN,EIN,PASSPORT,DRIVERS_LICENSE,NATIONAL_ID) supports compliance screening.
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.
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, for example: "Payroll Expenses", "Goods bought / Goods sold", "Contractor Payment", "International Money Transfer", "Fees for advisory, technical, academic or specialist assistance", "Tax Payment", "Insurance payment", among others. Choose the value that best describes the underlying purpose.
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 "x-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("x-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}`,
"x-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("x-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("x-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}",
"x-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 2 months ago
