Set up your development environment
Get familiar with the Mandate Labs SDKs and start authorizing agent transactions in minutes.
Who this guide is for: agent platforms integrating pre-presentment. If you are an issuer or processor integrating inside the authorization window, start with the issuer integration guide; production issuer deployments are sales-led and certified via MCI.
Overview
Event naming: kya.zone.* are the current event names (API v0.7). The legacy aliases cts.red / cts.critical remain accepted for 12 months under our deprecation policy.
This guide walks you through everything you need to go from zero to authorizing your first AI agent transaction: become a vetted Client, use the onboarding API to create a Principal and Agent, set spending limits via a mandate, and submit a transaction for real-time authorization.
What you'll learn
- Onboard a Principal and Agent with the Client onboarding API
- Create a spending mandate to set an agent's limits
- Authorize a transaction and interpret the response
- Set up webhooks for real-time event notifications
Sandbox keys (mdt_test_*) work identically to production keys (mdt_live_*) but never touch real payment rails. Sandbox keys are provisioned on request; production access is granted to vetted Clients (Book a call).
Get started
There is no self-serve sign-up. Clients are vetted through KYB onboarding before they are provisioned; sandbox keys come with provisioning. To get started, Book a call.
Once provisioned you receive a Client API key (mdt_live_…) and use the onboarding API shown below. All Client-authed requests pass the key in the X-API-Key header against the base URL https://api.mandatelabs.ai.
Onboard a Principal and Agent
This step uses your Client API key (mdt_live_…). The POST /api/v1/onboard bundle creates a Principal, an Agent, and a Mandate in a single call.
Official SDKs are in beta; the canonical interface is the HTTP API shown here.
curl -X POST https://api.mandatelabs.ai/api/v1/onboard \ -H "X-API-Key: mdt_live_…" -H "Content-Type: application/json" \ -d '{ "principal": { "principal_kind": "merchant", "program_ids": ["prg_…"], "client_customer_ref": "acme-001", "profile": { "display_name": "Acme Corp", "email": "[email protected]", "country": "US" }, "verification": { "type": "KYB", "mode": "client_attested", "attestation": { "attested_by": "[email protected]", "scope": "Tier-2 CDD", "statement": "Acme performed CDD on this merchant", "evidence_ref": "case-123" } } }, "agent": { "name": "Shopping Assistant v1", "capabilities": ["payments.authorize"] }, "mandate": { "rails": ["card"], "limits": { "max_amount_per_transaction": 500, "max_daily_amount": 2000, "currency": "USD" } } }'
import httpx resp = httpx.post( "https://api.mandatelabs.ai/api/v1/onboard", headers={"X-API-Key": "mdt_live_…"}, json={ "principal": { "principal_kind": "merchant", "program_ids": ["prg_…"], "client_customer_ref": "acme-001", "profile": {"display_name": "Acme Corp", "email": "[email protected]", "country": "US"}, "verification": { "type": "KYB", "mode": "client_attested", "attestation": { "attested_by": "[email protected]", "scope": "Tier-2 CDD", "statement": "Acme performed CDD on this merchant", "evidence_ref": "case-123", }, }, }, "agent": {"name": "Shopping Assistant v1", "capabilities": ["payments.authorize"]}, "mandate": { "rails": ["card"], "limits": {"max_amount_per_transaction": 500, "max_daily_amount": 2000, "currency": "USD"}, }, }, ) data = resp.json() agent_id = data["agent"]["id"] # agt_…
const resp = await fetch("https://api.mandatelabs.ai/api/v1/onboard", { method: "POST", headers: { "X-API-Key": "mdt_live_…", "Content-Type": "application/json" }, body: JSON.stringify({ principal: { principal_kind: "merchant", program_ids: ["prg_…"], client_customer_ref: "acme-001", profile: { display_name: "Acme Corp", email: "[email protected]", country: "US" }, verification: { type: "KYB", mode: "client_attested", attestation: { attested_by: "[email protected]", scope: "Tier-2 CDD", statement: "Acme performed CDD on this merchant", evidence_ref: "case-123", }, }, }, agent: { name: "Shopping Assistant v1", capabilities: ["payments.authorize"] }, mandate: { rails: ["card"], limits: { max_amount_per_transaction: 500, max_daily_amount: 2000, currency: "USD" }, }, }), }); const data = await resp.json(); const agentId = data.agent.id; // agt_…
One Principal can own many Agents. To add more agents to the same principal, create the principal once with POST /api/v1/principals, then call POST /api/v1/principals/{principal_id}/agents for each agent:
# Create the principal once curl -X POST https://api.mandatelabs.ai/api/v1/principals \ -H "X-API-Key: mdt_live_…" -H "Content-Type: application/json" \ -d '{ "principal_kind": "merchant", "program_ids": ["prg_…"], "client_customer_ref": "acme-001", "profile": { "display_name": "Acme Corp", "email": "[email protected]", "country": "US" } }' # → returns "id": "prn_…" # Add an agent to that principal (repeat per agent) curl -X POST https://api.mandatelabs.ai/api/v1/principals/prn_…/agents \ -H "X-API-Key: mdt_live_…" -H "Content-Type: application/json" \ -d '{ "name": "Shopping Assistant v2", "capabilities": ["payments.authorize"] }'
Create a spending mandate
Mandates define what an agent is allowed to spend. Add or update a mandate for an existing agent with POST /api/v1/agents/{agent_id}/mandates, setting per-transaction and daily limits, allowed merchant categories, and allowed countries.
curl -X POST https://api.mandatelabs.ai/api/v1/agents/agt_…/mandates \ -H "X-API-Key: mdt_live_…" -H "Content-Type: application/json" \ -d '{ "rails": ["card"], "limits": { "max_amount_per_transaction": 500, "max_daily_amount": 2000, "currency": "USD" }, "allowed_mccs": ["5411", "5812"], "allowed_countries": ["US"] }'
import httpx resp = httpx.post( "https://api.mandatelabs.ai/api/v1/agents/agt_…/mandates", headers={"X-API-Key": "mdt_live_…"}, json={ "rails": ["card"], "limits": {"max_amount_per_transaction": 500, "max_daily_amount": 2000, "currency": "USD"}, "allowed_mccs": ["5411", "5812"], "allowed_countries": ["US"], }, ) mandate = resp.json() print(mandate["id"]) # mnd_…
const resp = await fetch("https://api.mandatelabs.ai/api/v1/agents/agt_…/mandates", { method: "POST", headers: { "X-API-Key": "mdt_live_…", "Content-Type": "application/json" }, body: JSON.stringify({ rails: ["card"], limits: { max_amount_per_transaction: 500, max_daily_amount: 2000, currency: "USD" }, allowed_mccs: ["5411", "5812"], allowed_countries: ["US"], }), }); const mandate = await resp.json(); console.log(mandate.id); // mnd_…
Authorize a transaction
This is the core operation. Submit a transaction request to POST /api/v1/authorize and receive a real-time decision with trust scoring, risk assessment, and any gates that fired. Use the agent_id returned when you onboarded the agent above.
curl -X POST https://api.mandatelabs.ai/api/v1/authorize \ -H "X-API-Key: mdt_live_…" -H "Content-Type: application/json" \ -d '{ "agent_id": "agt_…", "amount": "42.99", "currency": "USD", "merchant": "Whole Foods Market #1042", "mcc": "5411", "intent_context": { "intent_type": "PURCHASE", "task_reference": "weekly_grocery_purchase", "reasoning_summary": "Weekly organic produce restock; compared 3 nearby grocers on price and delivery window", "confidence": 0.91, "alternatives_considered": 3 } }'
import httpx resp = httpx.post( "https://api.mandatelabs.ai/api/v1/authorize", headers={"X-API-Key": "mdt_live_…"}, json={ "agent_id": "agt_…", "amount": "42.99", "currency": "USD", "merchant": "Whole Foods Market #1042", "mcc": "5411", "intent_context": { "intent_type": "PURCHASE", "task_reference": "weekly_grocery_purchase", "reasoning_summary": "Weekly organic produce restock; compared 3 nearby grocers on price and delivery window", "confidence": 0.91, "alternatives_considered": 3, }, }, ) result = resp.json() print(f"Decision: {result['decision']}") # "APPROVE" print(f"Trust Score: {result['trust_score']}") # 0.87 print(f"Risk Score: {result['risk_score']}") # 0.12 print(f"Gates Fired: {result['gates_fired']}") # []
const resp = await fetch("https://api.mandatelabs.ai/api/v1/authorize", { method: "POST", headers: { "X-API-Key": "mdt_live_…", "Content-Type": "application/json" }, body: JSON.stringify({ agent_id: "agt_…", amount: "42.99", currency: "USD", merchant: "Whole Foods Market #1042", mcc: "5411", intent_context: { intent_type: "PURCHASE", task_reference: "weekly_grocery_purchase", reasoning_summary: "Weekly organic produce restock; compared 3 nearby grocers on price and delivery window", confidence: 0.91, alternatives_considered: 3, }, }), }); const result = await resp.json(); console.log(`Decision: ${result.decision}`); // "APPROVE" console.log(`Trust Score: ${result.trust_score}`); // 0.87 console.log(`Risk Score: ${result.risk_score}`); // 0.12 console.log(`Gates Fired: ${result.gates_fired}`); // []
decision: APPROVE, DECLINE, or STEP_UP. trust_score: Current KYA Score (0-1). risk_score: Transaction risk (0-1). gates_fired: Array of triggered safety gates. recommendations: Suggested actions if declined.
Set up webhooks
Register a webhook endpoint to receive real-time notifications about important events like gate firings, trust zone changes, and declined authorizations.
curl -X POST https://api.mandatelabs.ai/api/v1/webhooks \ -H "X-API-Key: mdt_live_…" \ -H "Content-Type: application/json" \ -d '{ "url": "https://your-app.com/webhooks/mandate-ai", "event_types": ["gate.fired", "kya.zone.red", "kya.zone.critical", "authorization.decline", "trust.promotion"], "description": "Production alerting webhook" }' # Response: { "id": "...", "signing_secret": "whsec_..." } — the secret is shown only once.
import httpx resp = httpx.post( "https://api.mandatelabs.ai/api/v1/webhooks", headers={"X-API-Key": "mdt_live_…"}, json={ "url": "https://your-app.com/webhooks/mandate-ai", "event_types": ["gate.fired", "kya.zone.red", "kya.zone.critical", "authorization.decline", "trust.promotion"], "description": "Production alerting webhook", }, ) data = resp.json() # Save the signing secret — shown only once! print(data["id"], data["signing_secret"]) # whsec_...
const resp = await fetch("https://api.mandatelabs.ai/api/v1/webhooks", { method: "POST", headers: { "X-API-Key": "mdt_live_…", "Content-Type": "application/json" }, body: JSON.stringify({ url: "https://your-app.com/webhooks/mandate-ai", event_types: ["gate.fired", "kya.zone.red", "kya.zone.critical", "authorization.decline", "trust.promotion"], description: "Production alerting webhook", }), }); const data = await resp.json(); // Save the signing secret — shown only once! console.log(data.id, data.signing_secret); // whsec_...
Available event types:
gate.fired: A safety gate triggered during authorizationkya.zone.red: Agent's trust score dropped to RED zonekya.zone.critical: Agent's trust score dropped to CRITICAL zonesession.terminate: Agent session was terminatedauthorization.decline: A transaction was declinedtrust.promotion: Agent was promoted to a higher trust tier*: Subscribe to all event types
Next steps
Now that you can authorize transactions, explore these guides to deepen your integration: