Capture
Capture a quote with POST /fx/quote/capture/{quote_id} to lock the rate and execute the transaction. Covers the response, transaction status, delivery timing, and examples in Go, TypeScript, Java, Rust, and Python.
Overview
Capturing a quote confirms your acceptance of its terms and executes the FX transaction at the locked rate. Call POST /fx/quote/capture/{quote_id} with the quote_id returned from the quote.
POST https://api.sandbox.movmint.io/fx/quote/capture/{quote_id}
Authorization: Bearer <access_token>
x-idempotency-key: <uuid>
| Header | Required | Description |
|---|---|---|
Authorization | yes | Bearer <access_token>. |
x-idempotency-key | yes | A UUID you generate. Reuse it on retries so the capture executes at most once. |
The capture endpoint takes no request body — the quote_id path parameter is the only input.
Before you capture
- Quotes expire. Capture before the
valid_untiltimestamp from the quote response. A late capture returns410 Gone; request a fresh quote. - Quotes are single-use. A successfully captured quote cannot be captured again (
409). - Capture executes the transaction. Once captured, the conversion executes and funds move according to the source and target configurations from the quote.
Code examples
cURL
curl -X POST https://api.sandbox.movmint.io/fx/quote/capture/123e4567-e89b-12d3-a456-426614174000 \
-H "Authorization: Bearer <access_token>" \
-H "x-idempotency-key: $(uuidgen)"Go
package main
import (
"fmt"
"io"
"log"
"net/http"
"github.com/google/uuid"
)
const baseURL = "https://api.sandbox.movmint.io"
func capture(token, quoteID string) (string, error) {
req, err := http.NewRequest(
http.MethodPost,
fmt.Sprintf("%s/fx/quote/capture/%s", baseURL, quoteID),
nil,
)
if err != nil {
return "", err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("x-idempotency-key", uuid.NewString())
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("capture failed (%d): %s", resp.StatusCode, body)
}
return string(body), nil
}
func main() {
out, err := capture("<access_token>", "123e4567-e89b-12d3-a456-426614174000")
if err != nil {
log.Fatal(err)
}
fmt.Println(out)
}TypeScript
import { randomUUID } from "crypto";
const BASE_URL = "https://api.sandbox.movmint.io";
async function capture(token: string, quoteId: string) {
const res = await fetch(`${BASE_URL}/fx/quote/capture/${quoteId}`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"x-idempotency-key": randomUUID(),
},
});
if (!res.ok) {
throw new Error(`Capture failed (${res.status}): ${await res.text()}`);
}
return res.json();
}
console.log(await capture("<access_token>", "123e4567-e89b-12d3-a456-426614174000"));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 Capture {
private static final String BASE_URL = "https://api.sandbox.movmint.io";
public static void main(String[] args) throws Exception {
String token = "<access_token>";
String quoteId = "123e4567-e89b-12d3-a456-426614174000";
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(BASE_URL + "/fx/quote/capture/" + quoteId))
.header("Authorization", "Bearer " + token)
.header("x-idempotency-key", UUID.randomUUID().toString())
.POST(HttpRequest.BodyPublishers.noBody())
.build();
HttpResponse<String> response = client.send(
request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
throw new RuntimeException("Capture failed (" + response.statusCode()
+ "): " + response.body());
}
System.out.println(response.body());
}
}Rust
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 quote_id = "123e4567-e89b-12d3-a456-426614174000";
let client = reqwest::Client::new();
let resp = client
.post(format!("{BASE_URL}/fx/quote/capture/{quote_id}"))
.bearer_auth(token)
.header("x-idempotency-key", Uuid::new_v4().to_string())
.send()
.await?;
if !resp.status().is_success() {
let status = resp.status();
return Err(format!("capture 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 capture(token: str, quote_id: str) -> dict:
resp = requests.post(
f"{BASE_URL}/fx/quote/capture/{quote_id}",
headers={
"Authorization": f"Bearer {token}",
"x-idempotency-key": str(uuid.uuid4()),
},
timeout=30,
)
resp.raise_for_status()
return resp.json()
print(capture("<access_token>", "123e4567-e89b-12d3-a456-426614174000"))Example response (200)
200){
"client_reference_id": "invoice-4821",
"transaction_id": "50796632-715f-477c-991e-acd73bdc6144",
"quote_id": "123e4567-e89b-12d3-a456-426614174000",
"status": "QUOTE_CAPTURED",
"source_funding_instructions": {
"source_type": "DLT_WALLET",
"account_configuration": {
"wallet_address": "0xabc987654321def",
"network": "ethereum"
}
},
"executed_amount": 998,
"executed_at": "2026-06-24T12:04:55Z"
}| Field | Description |
|---|---|
client_reference_id | The reference you set in the quote's tx_configuration. |
transaction_id | Unique ID for the executed transaction. Use it for reconciliation and support. |
quote_id | The captured quote. |
status | The transaction status (see below). |
source_funding_instructions | Where to send source funds, when the source rail requires you to push funds (e.g., a DLT deposit address). |
executed_amount | Amount in the target currency. |
executed_at | When the transaction executed. |
Acting onsource_funding_instructionsFor source rails where Movmint pulls funds (bank account, card, client balance), no further action is needed. For rails where you must push funds — notably
DLT_WALLET—source_funding_instructionstells you the exact address and network to send the source asset to so settlement can complete.
When can I expect funds to be delivered?
Delivery time depends primarily on the target rail, and settlement also depends on how quickly source funds arrive. The conversion itself is immediate; the surrounding rails determine wall-clock time.
| Target rail | Typical delivery time | Notes |
|---|---|---|
CLIENT_BALANCE | Immediate | Converted funds are credited to your prefunded balance on the ledger at execution — no external rail. |
DLT_WALLET | Minutes | Bounded by network confirmation time for the target network. |
CARD (push-to-card) | Minutes to a few hours | Depends on the card network and issuing bank. |
BANK_ACCOUNT | Same day to a few business days | ACH/FedNow timing and banking hours apply; FedNow is near-instant where supported, standard ACH is slower. |
Source-side timing matters too:
CLIENT_BALANCEsource — debited instantly at capture; no waiting on external funds.DLT_WALLETsource — settlement waits for your deposit to the address insource_funding_instructionsto confirm on-chain.BANK_ACCOUNT/CARDsource — settlement follows the clearing time of the pull.
Compliance screening can add timeEvery transaction is screened for sanctions/risk. If a transaction is flagged, it is held for review before delivery completes. Complete, accurate participant and address data minimizes the chance of a hold.
Transaction status
The status field reflects where the transaction is in its lifecycle. Immediately after capture you will typically see QUOTE_CAPTURED; it then progresses as funding and delivery complete. Common terminal and intermediate states:
QUOTE_CAPTURED— capture accepted; the transaction has been initiated.PENDING— settlement in progress (e.g., awaiting source funds or rail clearing).COMPLETED— funds converted and delivered to the target.FAILED— the transaction could not complete. Contact support with thetransaction_id.
Error responses
| HTTP | Cause |
|---|---|
400 | Invalid request or malformed quote_id. |
404 | No quote exists with the provided quote_id. |
409 | The quote was already captured, or a capture workflow is already in progress. |
410 | The quote has expired — request a new one. |
500 | Internal server error — retry with backoff. |
Putting it together
A typical integration:
- Quote —
POST /fx/quote; storequote_idandvalid_until. - Capture —
POST /fx/quote/capture/{quote_id}before expiry; storetransaction_id. - Settle the source side if needed — if
source_funding_instructionsreturns a DLT address, send the source asset there. - Reconcile — track the transaction to
COMPLETEDand matchtransaction_idagainst your records.
Updated 2 months ago
