SwapEazi
SwapEazi
Get early access
Platform · Mandates

Purpose-locked autonomous settlement.

An agent should be able to pay on your behalf without being able to pay for the wrong thing. A SwapEazi mandate is a signed grant of authority: the principal decides what it may do, and the rail enforces exactly that, call after call.

The principal signs the scope once

A principal completes KYC once, then signs a mandate body with an ed25519 key. The body names the agent, the allowed corridors, the delivery types, the per-transaction and per-period limits, a beneficiary allowlist, and an optional purpose allowlist. The allowed purposes are drawn from a fixed set: medical, groceries, education, utilities, trade_supplier, and family_support.

From then on the agent presents the same signed mandate on every settlement. It never holds the authority to change it.

A mandate scoped to groceries cannot buy anything else

On a settle call the agent sends the signed mandate, a beneficiaryRef, an amount, and a declared purpose. The rail verifies the ed25519 signature over the canonicalized body, then checks each of these against the signed scope:

  • the declared purpose must be in allowedPurposes, or the call is denied with PURPOSE_NOT_ALLOWED;
  • the beneficiary must match the pinned allowlist by lookupHash, so an injected recipient is rejected;
  • the amount must sit within maxPerTx and the running maxPerPeriod;
  • the corridor and delivery type must be in the signed lists.

Every check runs against the signed bytes, not against the prompt. An instruction hidden in a message that says to also pay someone new, or to raise the limit, changes nothing: that recipient is not in the allowlist and that number is not the signed number. The authority cannot be widened after signing.

A trimmed mandate

This grant lets one agent pay one grocery beneficiary in South Africa, up to R2,000 per transaction and R6,000 per month, and nothing else.

signed off-ramp mandate (trimmed)
{
  "body": {
    "schemaVersion": "off-1.0.0",
    "mandateId": "mnd_9f2c8a1b",
    "principal": {
      "principalId": "did:swz:ama",
      "principalKycRef": "kyc_01HZX9"
    },
    "agent": { "agentId": "agent:household-ops" },
    "scope": {
      "maxPerTx": 2000,
      "maxPerPeriod": 6000,
      "period": "monthly",
      "allowedCorridors": ["ZAR:ZA"],
      "deliveryTypes": ["bank", "mobile_money"],
      "allowedPurposes": ["groceries"],
      "allowedBeneficiaries": [
        { "beneficiaryRef": "gogo-checkers", "lookupHash": "b3f1a0c9" }
      ]
    },
    "issuedAt": "2026-07-01T09:00:00Z",
    "expiresAt": "2026-10-01T09:00:00Z",
    "nonce": "3a7d1e05"
  },
  "signature": {
    "alg": "ed25519",
    "publicKey": "302a300506032b6570...",
    "value": "9b1e77c4..."
  }
}

The full schema is published at /schemas/offramp-mandate/v1.json. It is the same schema the rail emits at runtime, so what an agent validates against is what the rail enforces.