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>
HeaderRequiredDescription
AuthorizationyesBearer <access_token>.
x-idempotency-keyyesA 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_until timestamp from the quote response. A late capture returns 410 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)

{
  "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"
}
FieldDescription
client_reference_idThe reference you set in the quote's tx_configuration.
transaction_idUnique ID for the executed transaction. Use it for reconciliation and support.
quote_idThe captured quote.
statusThe transaction status (see below).
source_funding_instructionsWhere to send source funds, when the source rail requires you to push funds (e.g., a DLT deposit address).
executed_amountAmount in the target currency.
executed_atWhen the transaction executed.
📘

Acting on source_funding_instructions

For 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_WALLETsource_funding_instructions tells 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 railTypical delivery timeNotes
CLIENT_BALANCEImmediateConverted funds are credited to your prefunded balance on the ledger at execution — no external rail.
DLT_WALLETMinutesBounded by network confirmation time for the target network.
CARD (push-to-card)Minutes to a few hoursDepends on the card network and issuing bank.
BANK_ACCOUNTSame day to a few business daysACH/FedNow timing and banking hours apply; FedNow is near-instant where supported, standard ACH is slower.

Source-side timing matters too:

  • CLIENT_BALANCE source — debited instantly at capture; no waiting on external funds.
  • DLT_WALLET source — settlement waits for your deposit to the address in source_funding_instructions to confirm on-chain.
  • BANK_ACCOUNT / CARD source — settlement follows the clearing time of the pull.
🚧

Compliance screening can add time

Every 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 the transaction_id.

Error responses

HTTPCause
400Invalid request or malformed quote_id.
404No quote exists with the provided quote_id.
409The quote was already captured, or a capture workflow is already in progress.
410The quote has expired — request a new one.
500Internal server error — retry with backoff.

Putting it together

A typical integration:

  1. QuotePOST /fx/quote; store quote_id and valid_until.
  2. CapturePOST /fx/quote/capture/{quote_id} before expiry; store transaction_id.
  3. Settle the source side if needed — if source_funding_instructions returns a DLT address, send the source asset there.
  4. Reconcile — track the transaction to COMPLETED and match transaction_id against your records.

Did this page help you?