Quickstart — first decision in under 30 minutes
The job this does
You run a procurement agent. Its job is to buy the software and APIs your team already uses — renew the cloud subscription, top up a metered API before it throttles, settle a supplier invoice — without a person in the loop for every £40 charge.
Two things have to be true before you can let it near a payment method:
- The routine purchases go through on their own. Approved supplier, sensible amount, working hours, within this month's budget → the agent pays and nobody is interrupted. That is most of the volume, and it is the whole saving.
- The unusual ones stop. A supplier nobody approved, an amount well above what this agent normally spends, a purchase at 3am, or the month's budget already gone → the payment does not happen until a named human says yes, or it is refused outright.
The commercial benefit is that first bullet: the finance controls you would otherwise enforce by making a person approve everything, enforced instead by a service that answers in milliseconds and can prove afterwards what it allowed, under whose authority, and why. You get the throughput of an autonomous agent with the spend controls of a purchase-order process.
Concretely, in the demo organisation you are about to use:
procurement_agent_12 may pay supplier_acme_cloud for cloud infrastructure
up to £750 per transaction on its own, up to £2,500 with a human
approval, never above £2,500, and no more than £1,500 a day or £5,000 a month
in total. So a £500 cloud bill is approved in one call and the agent pays it;
£1,200 goes to a named human for approval; £3,000 is refused outright; and
anything at all to a supplier nobody approved is refused. Each of those
outcomes leaves a signed receipt you can verify without us.
Three pieces make that work, and you will meet them in that order:
| What it is | Why you care | |
|---|---|---|
| Mandate | The versioned policy: who may be paid, how much, when, for what | It is the spend control, written once and enforced on every request |
| Authorisation artifact | A signed, single-use token issued with an approval | It lets your system move the money — mnd8t never touches funds or keys |
| Evidence receipt | A signed, hash-chained record of the decision | It is how you show an auditor what was allowed and on whose authority |
mnd8t is a sandbox developer preview today: sandbox keys never move real money, and live execution is disabled in the service. See known limitations for what is and is not available.
1. Get an API key (2 min)
Sign in with Google or GitHub. Your organisation starts with an admin sandbox key, which is what configures agents, counterparties and mandates. Keys are shown once — store it now.
Your agent should not use this key. An admin key can add a counterparty and publish a mandate, so an agent holding one could rewrite the authority it is about to be evaluated against. Once you reach step 7, mint a separate key for the agent —
POST /v1/api-keysdefaults to theAGENTpreset, which can transact but cannot change what is permitted.
export MANDATE_API_KEY=mdt_test_...
# Your workspace's API base URL is shown on the Developers page next to the key.
export MANDATE_API_URL=https://api.your-workspace.example
Sandbox keys never touch real money. Nothing you do below moves funds.
2. First decision with curl (3 min)
Your sandbox starts with a demo organisation already set up: an agent
(procurement_agent_12), an approved supplier (supplier_acme_cloud), and a
published mandate in enforce mode.
curl -X POST $MANDATE_API_URL/v1/decision-intents \
-H "Authorization: Bearer $MANDATE_API_KEY" \
-H "Idempotency-Key: quickstart-001" \
-H "Content-Type: application/json" \
-d '{
"agent_external_id": "procurement_agent_12",
"external_reference": "invoice_9834",
"amount": { "asset": "USDC", "asset_amount_minor": 675000000 },
"valuation": { "policy_amount_minor": 50000, "policy_currency": "GBP", "source": "CUSTOMER_SYSTEM" },
"counterparty_external_id": "supplier_acme_cloud",
"purpose": "cloud_infrastructure"
}'
You get back "decision": "APPROVE" with reason APPROVED_WITHIN_MANDATE,
plus an evidence_receipt_id.
Two amounts, both integer minor units, deliberately separate:
amount.asset_amount_minoris what would actually move, in the asset's own minor units (USDC has 6 decimal places, so675000000= 675 USDC).valuation.policy_amount_minoris what the mandate evaluates, in the policy currency's minor units (50000= £500.00).
The valuation is an assertion by the caller — mnd8t does not fetch exchange rates. It must come from a system you trust (pricing service, provider quote), never from the agent whose spending is being constrained.
Try the other demo scenarios by changing valuation.policy_amount_minor:
120000 escalates to a human (if today's budget allows), 300000 rejects on
the absolute limit, and any amount to vendor_unknown_offshore rejects as
blocked.
3. Same thing with the SDK (10 min)
npm install @mnd8t/sdk
import { MandateClient } from "@mnd8t/sdk";
const mandate = new MandateClient({
apiKey: process.env.MANDATE_API_KEY!,
baseUrl: process.env.MANDATE_API_URL!, // omitting it defaults to http://localhost:4000
});
const decision = await mandate.decisions.authorize({
agent_external_id: "procurement_agent_12",
amount: { asset: "USDC", asset_amount_minor: 675_000_000 },
valuation: { policy_amount_minor: 50_000, policy_currency: "GBP", source: "CUSTOMER_SYSTEM" },
counterparty_external_id: "supplier_acme_cloud",
purpose: "cloud_infrastructure",
});
if (decision.effective_decision === "APPROVE") {
// An enforce-mode APPROVE comes with a single-use authorisation artifact.
// The execution sequence is always: verify → claim → execute → confirm.
// Section 5 walks through it — don't execute without the artifact steps.
} else if (decision.effective_decision === "ESCALATE") {
console.log("send this to a human:", decision.approval?.approval_url);
}
Idempotency keys are generated automatically (override with idempotencyKey).
4. Create your own agent and mandate (10 min)
const agent = await mandate.agents.create({
name: "My Agent",
external_id: "my_agent_1",
purpose: "Pay approved API vendors",
});
const m = await mandate.mandates.create({
agent_id: agent.id,
name: "API spend mandate",
mode: "SHADOW", // start in shadow; switch to ENFORCE when ready
policy: {
currency: "GBP",
allowed_assets: ["USDC"],
per_transaction: { autonomous_limit: 10_000, absolute_limit: 50_000 },
budgets: [{ period: "MONTH", limit: 100_000 }],
approval: { new_counterparty_requires_approval: true },
allowed_purposes: ["api_usage"],
effective_at: new Date().toISOString(),
expires_at: new Date(Date.now() + 30 * 86_400_000).toISOString(),
},
});
// Publishing requires a delegator attestation — you assert that you hold
// the authority being delegated. mnd8t records it; it does not verify it.
await mandate.mandates.publish(m.id, {
delegation: { delegatorRole: "Finance Director", authoritySource: "CORPORATE_POLICY" },
});
Published versions are immutable — edits create a new version to publish.
5. Execute with YOUR system via the authorisation artifact (5 min)
mnd8t decides and proves — you (or your regulated provider) execute.
An enforce-mode APPROVE response includes a signed, one-time
authorisation_artifact. Your executor verifies it locally, claims it
(single use), executes with your own credentials, and reports the outcome:
const artifact = decision.authorisation_artifact!;
// 1. Fetch + verify the artifact signature locally (independent of mnd8t).
const envelope = await mandate.artifacts.get(artifact.id);
const check = await mandate.artifacts.verify(envelope.artifact);
if (!check.valid) throw new Error("refusing to execute an unverified artifact");
// 2. Claim it — atomic, exactly one executor wins.
await mandate.artifacts.claim(artifact.id, "my-executor-1");
// 3. Execute through YOUR wallet/provider (mnd8t never sees these credentials).
const txRef = await myWallet.pay(envelope.artifact);
// 4. Report the outcome back with provenance.
await mandate.decisions.confirm(decision.id, {
provider: "MY_PROVIDER",
external_reference: txRef,
authorisation_artifact_id: artifact.id,
reported_by: "CUSTOMER_EXECUTOR",
verification: { method: "PROVIDER_LOOKUP", verified: true },
});
A complete reference executor — running against a simulated wallet with no
credentials, or an EVM testnet with your own key — is yours to download and
run: reference-executor.ts, then
npm install @mnd8t/sdk and npx tsx reference-executor.ts with your sandbox
key. See wallet adapters for the details.
If the provider rejects the payment, report it with
mandate.artifacts.fail(artifact.id, "PROVIDER_REJECTED") — not cancel.
Once an artifact has been claimed, cancel returns 409, because releasing
budget for a payment that may already have gone out is exactly what the
reservation model exists to prevent.
6. Verify an evidence receipt independently (5 min)
const envelope = await mandate.receipts.get(decision.evidence_receipt_id!);
const { valid } = await mandate.receipts.verify(envelope.receipt); // local Ed25519 check
Verification is local and offline-capable: it needs only the receipt and the
public key from /.well-known/mandate-keys.json. You never have to trust
mnd8t to confirm what a receipt says.
7. Give the agent its own key
Everything above used the admin key, which is right for setup and wrong for runtime. Mint the key your agent will actually carry:
curl -X POST $MANDATE_API_URL/v1/api-keys \
-H "Authorization: Bearer $MANDATE_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "procurement agent", "preset": "AGENT" }'
The AGENT preset carries decisions:*, artifacts:*, receipts:read and
registry:read — enough to propose actions, claim artifacts, confirm
execution and read its own evidence. It cannot create a counterparty, publish
a mandate, or mint another key, so a compromised agent cannot widen its own
authority.
Deploy that key to the agent. Keep the admin key with whoever administers the organisation, and rotate it if it has ever been deployed alongside an agent.
Next: policy model for what a mandate can express · evidence receipts for the proof format · API reference for every endpoint.