Using a FlowLink

Send deposits to trigger automatic conversion and delivery, manage the FlowLink lifecycle (pause, resume, disable), and read transaction history. Examples in Go, TypeScript, Java, Rust, and Python.

Sending deposits

Using a FlowLink is simple: send the source asset to the deposit_address returned at creation. No further API calls are required — the deposit triggers the whole flow automatically.

When a deposit arrives at an active FlowLink, Movmint:

  1. detects and accepts the deposit and records it on the ledger,
  2. screens it via TRM Labs (sanctions/risk),
  3. converts it at the current market rate with your client-specific spread and fees, and
  4. delivers the target asset to the preconfigured destination (DLT transfer, push-to-card, or ACH/FedNow bank transfer).

Each deposit is processed as an independent transaction; concurrent deposits are queued and processed sequentially. Because pricing happens at deposit time, there is no quote to lock or capture.

📘

Delivery timing

As with Quote & Capture delivery, wall-clock time depends on the destination rail: DLT delivery is bounded by network confirmation, push-to-card is minutes to hours, and bank (ACH/FedNow) ranges from near-instant to a few business days. A deposit flagged by screening is held for compliance review before delivery.

What happens to deposits when not active

FlowLink statusBehavior on deposit
activeProcessed straight-through.
pausedHeld; processed when the FlowLink is resumed (you are alerted that a deposit arrived at a paused FlowLink).
disabledThe address is decommissioned; any held funds are returned per policy.

Managing the lifecycle

Three endpoints move a FlowLink between states. All are POST, take the flowlink_id in the path, require an x-idempotency-key header, and return the updated FlowLink object.

ActionEndpointValid fromResult
PausePOST /flowlink/{flowlink_id}/pauseactivepaused — deposits are held.
ResumePOST /flowlink/{flowlink_id}/resumepausedactive — held deposits are queued for processing.
DisablePOST /flowlink/{flowlink_id}/disableactive or pauseddisabled — permanent; create a new FlowLink to resume use.

Calling an action from the wrong state returns 409 (e.g., resuming a FlowLink that is not paused). An unknown flowlink_id returns 404.

Lifecycle code examples

The examples define a single helper that performs any lifecycle action (pause, resume, or disable).

cURL

FLOWLINK_ID="2b7a1f4c-9d8e-4a3b-bc12-3456789abcde"
# Pause
curl -X POST "https://api.sandbox.movmint.io/flowlink/$FLOWLINK_ID/pause" \
  -H "Authorization: Bearer <access_token>" -H "x-idempotency-key: $(uuidgen)"
# Resume
curl -X POST "https://api.sandbox.movmint.io/flowlink/$FLOWLINK_ID/resume" \
  -H "Authorization: Bearer <access_token>" -H "x-idempotency-key: $(uuidgen)"
# Disable
curl -X POST "https://api.sandbox.movmint.io/flowlink/$FLOWLINK_ID/disable" \
  -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"

// action is one of "pause", "resume", "disable".
func lifecycle(token, flowlinkID, action string) (string, error) {
	url := fmt.Sprintf("%s/flowlink/%s/%s", baseURL, flowlinkID, action)
	req, _ := http.NewRequest(http.MethodPost, url, nil)
	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("%s failed (%d): %s", action, resp.StatusCode, body)
	}
	return string(body), nil
}

func main() {
	out, err := lifecycle("<access_token>", "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde", "pause")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(out)
}

TypeScript

import { randomUUID } from "crypto";

const BASE_URL = "https://api.sandbox.movmint.io";
type Action = "pause" | "resume" | "disable";

async function lifecycle(token: string, flowlinkId: string, action: Action) {
  const res = await fetch(`${BASE_URL}/flowlink/${flowlinkId}/${action}`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${token}`,
      "x-idempotency-key": randomUUID(),
    },
  });

  if (!res.ok) {
    throw new Error(`${action} failed (${res.status}): ${await res.text()}`);
  }
  return res.json();
}

console.log(await lifecycle("<access_token>", "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde", "pause"));

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 FlowLinkLifecycle {

    private static final String BASE_URL = "https://api.sandbox.movmint.io";

    // action is one of "pause", "resume", "disable".
    static String lifecycle(String token, String flowlinkId, String action) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(BASE_URL + "/flowlink/" + flowlinkId + "/" + action))
                .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(action + " failed (" + response.statusCode()
                    + "): " + response.body());
        }
        return response.body();
    }

    public static void main(String[] args) throws Exception {
        System.out.println(lifecycle("<access_token>",
                "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde", "pause"));
    }
}

Rust

use uuid::Uuid;

const BASE_URL: &str = "https://api.sandbox.movmint.io";

// action is one of "pause", "resume", "disable".
async fn lifecycle(token: &str, flowlink_id: &str, action: &str)
    -> Result<serde_json::Value, Box<dyn std::error::Error>>
{
    let client = reqwest::Client::new();
    let resp = client
        .post(format!("{BASE_URL}/flowlink/{flowlink_id}/{action}"))
        .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!("{action} failed ({status}): {}", resp.text().await?).into());
    }
    Ok(resp.json().await?)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let fl = lifecycle("<access_token>", "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde", "pause").await?;
    println!("{}", serde_json::to_string_pretty(&fl)?);
    Ok(())
}

Python

import uuid
import requests

BASE_URL = "https://api.sandbox.movmint.io"


def lifecycle(token: str, flowlink_id: str, action: str) -> dict:
    """action is one of 'pause', 'resume', 'disable'."""
    resp = requests.post(
        f"{BASE_URL}/flowlink/{flowlink_id}/{action}",
        headers={
            "Authorization": f"Bearer {token}",
            "x-idempotency-key": str(uuid.uuid4()),
        },
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()


print(lifecycle("<access_token>", "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde", "pause"))

Reading transaction history

GET /flowlink/{flowlink_id}/transactions returns the executions for a FlowLink. Each record links the originating deposit, the market-rate conversion (rate, spread, fees), and the delivery.

ParameterInRequiredDescription
flowlink_idpathyesThe FlowLink to query.
statusquerynoFilter by transaction status (see below).
from / toquerynodate-time bounds (inclusive).
limitquerynoPage size, 1–200 (default 50).
cursorquerynoOpaque cursor from a previous response's next_cursor.
x-idempotency-keyheaderyesA UUID you generate.

Transaction status lifecycle

StatusMeaning
PENDINGDeposit registered, processing not yet started.
DEPOSIT_RECEIVEDDeposit detected and accepted.
COMPLIANCE_HOLDHeld by TRM screening, pending compliance review.
HELDHeld (e.g., arrived at a paused FlowLink).
CONVERTINGFX conversion in progress.
DELIVERINGDelivering the target asset to the destination.
COMPLETEDDelivered and settled.
FAILEDFailed at some step.
RETURNEDFunds returned to source.

Transaction code examples

cURL

curl "https://api.sandbox.movmint.io/flowlink/2b7a1f4c-9d8e-4a3b-bc12-3456789abcde/transactions?status=COMPLETED&limit=50" \
  -H "Authorization: Bearer <access_token>" \
  -H "x-idempotency-key: $(uuidgen)"

Go

package main

import (
	"fmt"
	"io"
	"log"
	"net/http"
	"net/url"

	"github.com/google/uuid"
)

const baseURL = "https://api.sandbox.movmint.io"

func main() {
	token := "<access_token>"
	flowlinkID := "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde"

	q := url.Values{}
	q.Set("status", "COMPLETED")
	q.Set("limit", "50")

	endpoint := fmt.Sprintf("%s/flowlink/%s/transactions?%s", baseURL, flowlinkID, q.Encode())
	req, _ := http.NewRequest(http.MethodGet, endpoint, nil)
	req.Header.Set("Authorization", "Bearer "+token)
	req.Header.Set("x-idempotency-key", uuid.NewString())

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	if resp.StatusCode != http.StatusOK {
		log.Fatalf("get transactions failed (%d): %s", resp.StatusCode, body)
	}
	fmt.Println(string(body))
}

TypeScript

import { randomUUID } from "crypto";

const BASE_URL = "https://api.sandbox.movmint.io";

async function flowLinkTransactions(token: string, flowlinkId: string) {
  const url = new URL(`${BASE_URL}/flowlink/${flowlinkId}/transactions`);
  url.searchParams.set("status", "COMPLETED");
  url.searchParams.set("limit", "50");

  const res = await fetch(url, {
    headers: {
      Authorization: `Bearer ${token}`,
      "x-idempotency-key": randomUUID(),
    },
  });

  if (!res.ok) {
    throw new Error(`Get transactions failed (${res.status}): ${await res.text()}`);
  }
  return res.json();
}

console.log(await flowLinkTransactions("<access_token>", "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde"));

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 FlowLinkTransactions {

    private static final String BASE_URL = "https://api.sandbox.movmint.io";

    public static void main(String[] args) throws Exception {
        String token = "<access_token>";
        String flowlinkId = "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde";

        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(BASE_URL + "/flowlink/" + flowlinkId
                        + "/transactions?status=COMPLETED&limit=50"))
                .header("Authorization", "Bearer " + token)
                .header("x-idempotency-key", UUID.randomUUID().toString())
                .GET()
                .build();

        HttpResponse<String> response = client.send(
                request, HttpResponse.BodyHandlers.ofString());

        if (response.statusCode() != 200) {
            throw new RuntimeException("Get transactions 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 flowlink_id = "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde";

    let client = reqwest::Client::new();
    let resp = client
        .get(format!("{BASE_URL}/flowlink/{flowlink_id}/transactions"))
        .query(&[("status", "COMPLETED"), ("limit", "50")])
        .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!("get transactions 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 flowlink_transactions(token: str, flowlink_id: str, **filters) -> dict:
    resp = requests.get(
        f"{BASE_URL}/flowlink/{flowlink_id}/transactions",
        headers={
            "Authorization": f"Bearer {token}",
            "x-idempotency-key": str(uuid.uuid4()),
        },
        params={"status": "COMPLETED", "limit": 50, **filters},
        timeout=30,
    )
    resp.raise_for_status()
    return resp.json()


print(flowlink_transactions("<access_token>", "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde"))

Example response (200)

{
  "data": [
    {
      "transaction_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "flowlink_id": "2b7a1f4c-9d8e-4a3b-bc12-3456789abcde",
      "status": "COMPLETED",
      "deposit": {
        "tx_id": "0xdeadbeef...",
        "asset": "USDC",
        "network": "ethereum",
        "amount": 5000.00,
        "sender_address": "0xsender...",
        "received_at": "2026-06-24T12:10:00Z"
      },
      "conversion": {
        "from_asset": "USDC",
        "to_asset": "USD",
        "market_rate": 1.0,
        "spread": 0.0015,
        "fees": 2.50,
        "input_amount": 5000.00,
        "output_amount": 4990.00,
        "executed_at": "2026-06-24T12:10:20Z"
      },
      "delivery": {
        "destination_type": "BANK_ACCOUNT",
        "asset": "USD",
        "amount": 4990.00,
        "provider_reference": "ach_01H...",
        "delivered_at": "2026-06-24T12:11:00Z"
      },
      "created_at": "2026-06-24T12:10:00Z",
      "updated_at": "2026-06-24T12:11:00Z"
    }
  ],
  "next_cursor": null
}

Each transaction links the deposit, the conversion (with market_rate, spread, fees, and input/output amounts), and the delivery (with a provider_reference such as an ACH/FedNow payment ID, an iPayout transfer ID, or a DLT transfer ID). Page through results using next_cursor until it is null.

Error responses

HTTPCause
404FlowLink not found.
500Internal server error — retry with backoff.

Did this page help you?