Mooj API
The payment layer for AI agents. Issue a real, capped virtual card and either use it yourself or hand the whole checkout to Mooj. Card controls, 3DS, and fraud caps are handled for you.
The Mooj API is organized around REST. It has predictable, resource-oriented URLs, accepts JSON request bodies, returns JSON responses, and uses standard HTTP verbs and status codes. Every response includes an X-Mooj-Request-Id header (also echoed as request_id in the body) so any call can be traced.
| Base URL | https://mooj-api-277196974190.us-central1.run.app |
| Version | All endpoints are under /v1. |
| Auth | Bearer API key in the Authorization header. |
| Content type | application/json for all request bodies. |
"amount": 20 means $20.00). Card and wallet math is exact to the cent internally.Authentication #
Authenticate every request with your secret API key. Create and manage keys in the Mooj console under Developer → API keys. Keys look like mk_live_... and are shown only once at creation, so store yours securely.
Send the key as a bearer token. Keep it server-side and never commit it to source control; the examples below read it from an environment variable.
export MOOJ_API_KEY="mk_live_your_key_here"
# every request sends the bearer header:
curl https://mooj-api-277196974190.us-central1.run.app/v1/spend/example \
-H "Authorization: Bearer $MOOJ_API_KEY"
A missing or invalid key returns 401 unauthorized. See Errors for the full list.
Quickstart #
Issue your first card in one call. The response comes back with a real, usable card capped at the amount you set.
curl https://mooj-api-277196974190.us-central1.run.app/v1/cards \
-H "Authorization: Bearer $MOOJ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"amount": 20, "merchant": "namecheap", "single_use": true}'
{
"id": "card_8f2a...",
"brand": "visa",
"last4": "8339",
"exp_month": 4,
"exp_year": 2033,
"spend_limit": 20,
"single_use": true,
"status": "ACTIVE",
"spend_request_id": "spr_1c9d..."
}
From here you can reveal the card to use it yourself, or skip cards entirely and let Mooj complete a checkout for you.
Guide: Give your agent a card #
The safest way to let an agent spend is a single-use card capped at exactly what the purchase should cost. It auto-freezes after the first charge, so it cannot be reused or over-spent.
- Issue a card with
POST /v1/cards, settingsingle_use: trueandamountto the cap. - Reveal the number with
GET /v1/cards/{id}only at the moment of use. - Check the result with
GET /v1/spend/{spend_request_id}(authorized/cleared/declined).
To teach your agent these calls in one paste, grab a ready tool file from Developer → Agent skills (Claude, OpenAI, and Gemini schemas). See Agent skills.
Guide: Complete a checkout #
Hand Mooj a plain-English task and a spend cap. Mooj mints a card, drives the merchant checkout in a real browser, auto-accepts 3DS, and pays. This is asynchronous: you get a checkout_id back, then poll it.
# 1. start the checkout (confirm_pay:false = dry run, stops at review)
curl https://mooj-api-277196974190.us-central1.run.app/v1/checkout \
-H "Authorization: Bearer $MOOJ_API_KEY" \
-H "Content-Type: application/json" \
-d '{"task":"buy the domain mooj.click for 1 year","merchant":"namecheap","amount":20,"confirm_pay":false}'
# 2. poll until it finishes
curl https://mooj-api-277196974190.us-central1.run.app/v1/checkout/CHECKOUT_ID \
-H "Authorization: Bearer $MOOJ_API_KEY"
Set confirm_pay: true to have Mooj pay autonomously, up to the cap. Poll status until it reaches a terminal state:
queued running awaiting_approval needs_login submitted blocked
status is needs_login, the merchant requires a signed-in session. Set up a reusable login once with Connections, then pass its connection_id to the checkout.Guide: Connect a merchant login #
Some merchants require an account. A connection is a reusable, isolated login your users complete once in a hosted browser session; every future checkout on that connection is already signed in.
POST /v1/connectionsreturns aconnect_url. Open it so the user signs in on the merchant's own page (Mooj never sees the password).POST /v1/connections/{id}/completepersists the login. The connection becomes connected.- Pass
connection_idtoPOST /v1/checkoutand the run executes signed-in as that user.
Guide: Handle events #
Instead of polling, get events pushed to your server the moment they happen. Register an endpoint in the Mooj console (Developer -> Webhooks); Mooj sends a signed POST for each event.
Events
card.authorized card.cleared card.declined checkout.submitted checkout.needs_login checkout.blocked
Each delivery is JSON: { "id", "type", "created", "data": { ... } }.
Verify the signature
Every request carries a Mooj-Signature: t=<unix>,v1=<hex> header. Recompute it with your endpoint's signing secret (shown once when you create the endpoint) and compare, so you know the event really came from Mooj.
import crypto from "node:crypto";
function verify(rawBody, header, secret) {
const [t, v1] = header.split(",").map(p => p.split("=")[1]);
const expected = crypto.createHmac("sha256", secret)
.update(`${t}.${rawBody}`).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
GET /v1/spend/{id} and GET /v1/checkout/{id} at any time.API reference
Every live endpoint, its parameters, and an example response. All requests require the Authorization: Bearer header from Authentication.
Cards #
Create a card
Mint a virtual card capped at amount (USD) for a merchant.
| Field | Type | Req | Description |
|---|---|---|---|
| amount | number | yes | Spend cap in US dollars. Per-card ceiling is $5,000. |
| merchant | string | yes | Merchant the card is for, e.g. namecheap. |
| single_use | boolean | no | Default true. Auto-freezes after the first charge. |
| network | string | no | auto (default), visa, or mastercard. |
| card_name | string | no | Label for the card in your dashboard. |
{
"id": "card_8f2a...",
"brand": "visa",
"last4": "8339",
"exp_month": 4,
"exp_year": 2033,
"spend_limit": 20,
"single_use": true,
"status": "ACTIVE",
"spend_request_id": "spr_1c9d..."
}
insufficient_funds if the cap exceeds your available wallet balance.Reveal a card
Return the full card number, CVC, and expiry. Reveal only at the moment of use; Mooj never logs the PAN.
{
"id": "card_8f2a...",
"last4": "8339",
"number": "4775xxxxxxxx8339",
"number_formatted": "4775 xxxx xxxx 8339",
"cvc": "123",
"exp": "04/33"
}
Close a card
Close a card and release any unused reserve back to your wallet.
{
"id": "card_8f2a...",
"status": "closed",
"released_display": "$20.00",
"available_to_issue_display": "$140.00"
}
Spend #
Get spend status
Check whether a card issued with POST /v1/cards was charged. Pass the spend_request_id from the create response.
not_started authorized cleared declined
{
"spend_request_id": "spr_1c9d...",
"merchant": "namecheap",
"decision": "APPROVED",
"status": "cleared",
"settled": true,
"amount": 1.98
}
Checkout #
Start a checkout
Asynchronous. Mooj mints a card, drives the merchant checkout, handles 3DS, and (if authorized) pays. Returns a checkout_id to poll.
| Field | Type | Req | Description |
|---|---|---|---|
| task | string | yes | Plain-English description of what to buy. |
| merchant | string | yes | Merchant to buy from. |
| amount | number | yes | Maximum USD to spend (becomes the card cap). |
| confirm_pay | boolean | no | Default false (dry run, stops at review). true pays up to the cap. |
| connection_id | string | no | Run signed-in on a connection. Auto-matched by merchant if omitted. |
| card_holder_name | string | no | Name on the card for the run. |
| start_url | string | no | URL or bare domain to open first. |
{
"checkout_id": "run_7a1e...",
"status": "queued",
"poll": "/v1/checkout/run_7a1e..."
}
Get checkout status
{
"checkout_id": "run_7a1e...",
"status": "awaiting_approval",
"task": "buy the domain mooj.click for 1 year",
"merchant": "namecheap",
"amount": 20,
"card_last4": "4775",
"review_total": 1.98,
"order_id": null,
"order_total": null,
"needs_login": false,
"error_reason": null
}
Connections #
Create a connection
| Field | Type | Req | Description |
|---|---|---|---|
| merchant | string | yes | Merchant to connect a login for. |
| user_ref | string | no | Your own reference for the end user this login belongs to. |
{
"connection_id": "conn_5d2b...",
"connect_url": "https://connect.browserbase.com/...",
"status": "pending"
}
Complete a connection
Call after the user has signed in at the connect_url. Persists the login; status becomes connected.
List / get connections
List all connections, or poll a single connection's status (pending / connected).
Errors #
Mooj uses conventional HTTP status codes. The body carries a machine-readable string and a request_id.
| Status | Meaning |
|---|---|
| 200 | Success. |
| 401 | unauthorized — missing or invalid API key. |
| 402 | insufficient_funds — the requested cap exceeds available wallet balance. |
| 404 | Resource not found, or not owned by your account. |
| 400 | Bad request — a required field is missing or invalid. |
{
"error": "insufficient_funds",
"request_id": "req_a1b2c3"
}
Postman collection #
Prefer to click instead of curl? Import the Mooj collection into Postman and every endpoint is ready to run.
https://storage.googleapis.com/mooj-docs/mooj.postman_collection.json
- In Postman, choose Import and paste the URL above (or download and drop the file in).
- Open the collection's Variables and set
apiKeyto yourmk_live_key.baseUrlis already filled. - Run Cards → Create a card to get your first card. The other requests reuse the same variables.
Authorization: Bearer {{apiKey}} at the collection level, so every request inherits your key automatically.Agent skills #
Teach your agent to use Mooj in one paste. The Mooj console (Developer → Agent skills) ships ready tool files for each stack:
Each file exposes the same four tools: issue_card, complete_purchase, get_checkout_status, and get_spend_status.
SDKs #
Official SDKs wrap this API so you do not have to hand-roll requests. Same surface in every language: cards, spend, checkout, connections.
npm install @mooj/sdk · npmjs.com/package/@mooj/sdkpip install mooj · pypi.org/project/moojnpx -y @mooj/mcp · npmjs.com/package/@mooj/mcpimport { Mooj } from "@mooj/sdk";
const mooj = new Mooj(process.env.MOOJ_API_KEY);
const card = await mooj.cards.create({ amount: 20, merchant: "namecheap", single_use: true });
from mooj import Mooj
mooj = Mooj(api_key=os.environ["MOOJ_API_KEY"])
card = mooj.cards.create(amount=20, merchant="namecheap", single_use=True)
{
"mcpServers": {
"mooj": {
"command": "npx",
"args": ["-y", "@mooj/mcp"],
"env": { "MOOJ_API_KEY": "mk_live_your_key_here" }
}
}
}