SDK / API ReferencePayments
Mandates
Spend policy attached to your project's wallet
Scope: payments:spend
A mandate is the per-project spend policy Loomal enforces server-side on every payments.pay() call. One mandate per project at a time (typical setup). Create it once via the Pay tab in the Console or programmatically via this resource — every pay() afterward respects its caps.
Mounted at loomal.payments.mandates.
The Mandate object
{
"mandateId": "m_abc123",
"identityId": "id-...",
"network": "base",
"maxPerCallUsdc": "0.10",
"dailyCapUsdc": "1.00",
"validUntil": "2027-01-01T00:00:00.000Z",
"sessionKeyAddress": "0x<derived-session-key>",
"onchainInstalled": true,
"installTxHash": "0x<install-tx-hash>",
"installError": null,
"spentTodayUsdc": "0.05",
"remainingTodayUsdc": "0.95",
"totalSpentUsdc": "1.23",
"callCount": 27,
"revokedAt": null,
"createdAt": "2026-05-12T10:00:00.000Z"
}| Field | Description |
|---|---|
maxPerCallUsdc | Max USDC any single pay() call can spend, decimal e.g. "0.10". |
dailyCapUsdc | Max cumulative USDC per UTC day. Must be ≥ maxPerCallUsdc. |
validUntil | ISO 8601. Defaults to 7 days from creation. Mandate stops authorizing payments after this. |
sessionKeyAddress | Address of the session key Loomal derived for this mandate. The key signs EIP-3009 authorizations on your Kernel smart account. |
onchainInstalled | true once the session key landed on Base. First-time install takes 10–30s. |
installError | Set when the session-key install failed. The mandate is unusable until reinstalled — call create() again. |
spentTodayUsdc / remainingTodayUsdc | Live counters, reset at UTC midnight. |
revokedAt | ISO 8601 when the mandate was revoked, otherwise null. |
Create
const mandate = await loomal.payments.mandates.create({
maxPerCallUsdc: "0.10",
dailyCapUsdc: "1.00",
});
if (mandate.installError) {
throw new Error(`install failed: ${mandate.installError}`);
}import { Loomal } from "@loomal/sdk";
const loomal = new Loomal({ apiKey: process.env.LOOMAL_API_KEY! });
const mandate = await loomal.payments.mandates.create({
maxPerCallUsdc: "0.10",
dailyCapUsdc: "1.00",
validUntil: "2027-01-01T00:00:00Z", // optional, defaults to +7d
});import os
from loomal import Loomal
loomal = Loomal(api_key=os.environ["LOOMAL_API_KEY"])
mandate = loomal.payments.mandates.create(
max_per_call_usdc="0.10",
daily_cap_usdc="1.00",
)curl -X POST https://api.loomal.ai/v0/payments/mandates \
-H "Authorization: Bearer loid-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"maxPerCallUsdc": "0.10",
"dailyCapUsdc": "1.00"
}'Body
| Field | Type | Required | Description |
|---|---|---|---|
maxPerCallUsdc | string | Yes | Decimal USDC, up to 6 fractional digits. |
dailyCapUsdc | string | Yes | Decimal USDC. Must be ≥ maxPerCallUsdc. |
validUntil | string | No | ISO 8601. Defaults to 7 days from now. |
network | string | No | "base" (only supported network). |
Notes
- First-call latency — creating a mandate installs the session key on Base, which takes 10–30s while the bundler confirms. Subsequent
pay()calls reuse the key and are sub-second. - Install failures — if the bundler errors out (out of gas, network issue), the returned mandate has
installErrorset andonchainInstalled: false. The mandate is unusable. Retrycreate(). - Multiple mandates — the API allows multiple active mandates per project;
pay()picks the first non-revoked, non-errored one withnetwork === "base". In practice, keep one.
List
const { mandates } = await loomal.payments.mandates.list();
const active = mandates.find((m) => !m.revokedAt && !m.installError);const { mandates } = await loomal.payments.mandates.list();mandates = loomal.payments.mandates.list()["mandates"]curl https://api.loomal.ai/v0/payments/mandates \
-H "Authorization: Bearer loid-your-api-key"Returns { mandates: Mandate[] }. Includes revoked and errored mandates — filter client-side as shown.
Get
const mandate = await loomal.payments.mandates.get("m_abc123");const mandate = await loomal.payments.mandates.get("m_abc123");mandate = loomal.payments.mandates.get("m_abc123")curl https://api.loomal.ai/v0/payments/mandates/m_abc123 \
-H "Authorization: Bearer loid-your-api-key"Revoke
await loomal.payments.mandates.revoke("m_abc123");Marks the mandate as revoked. The on-chain session key is not uninstalled — that's a manual step from the Console if you want to fully retire the key. Settled payments are unaffected.
await loomal.payments.mandates.revoke("m_abc123");loomal.payments.mandates.revoke("m_abc123")curl -X DELETE https://api.loomal.ai/v0/payments/mandates/m_abc123 \
-H "Authorization: Bearer loid-your-api-key"Errors
| Status | error | Cause |
|---|---|---|
| 400 | bad_request | maxPerCallUsdc <= 0 or dailyCapUsdc < maxPerCallUsdc |
| 401 | unauthorized | Missing or invalid Authorization header |
| 403 | forbidden | API key lacks payments:spend scope |
| 404 | not_found | Mandate id doesn't exist (on get / revoke) |
Next
POST /v0/payments/pay— pay an x402 URL within the mandate's caps.- Pay for tools — the full buyer flow.