Mandact — The Trust Layer of the Agent Economy

Developers · Quickstart

Your first allow in 10 minutes

Your agent asks Mandact before every action and acts only on «allow». A test needs no payment method and no merchant: invent an action such as «hardware up to CHF 500 from ACME AG» and send different amounts and counterparties. You will see allow, deny with a reason, escalation and the receipt.

Path 1 · Mandact signs

The agent's key is stored encrypted at Mandact. Two calls before the action, one after — in any language.

scope agents:present

Path 2 · Agent signs itself

The agent holds its own Ed25519 key; Mandact only knows the public key. Cryptographically the stronger model.

own did:key in the wizard

Path 3 · Gateway

The agent no longer talks to the target system directly. Mandact checks every request and forwards only on allow — the check cannot be skipped.

token gwt_… per instance

  1. 1 · Create an account

    Sign up and set up the second factor. Without it, API keys cannot be created.

  2. 2 · Create agent and mandate

    New mandate in the wizard. In the «Agent» step, leave the identifier empty (path 1) or enter your agent's did:key (path 2). Set the limits: action type, amount per action and per month, counterparty, escalation threshold. Review the bound wording and activate the mandate.

  3. 3 · Create an API key

    Settings → API keys. Scopes: agents:present, verify, consumptions:write, executions:write, mandates:read, evidence:read. The key is shown exactly once.

  4. 4 · Add the three calls

    Before the action, have it signed and verified; afterwards, record the consumption and report the execution. The mandate ID is on the mandate's detail page.

The calls

Environment: MANDACT_API_KEY and MANDATE_ID. The action must be identical for present and verify — both sides bind the same context digest.

Have the intent signed (path 1)

curl -s -X POST https://mandact.com/api/v1/agents/present \
  -H "authorization: Bearer $MANDACT_API_KEY" \
  -H "content-type: application/json" \
  -d '{"mandate_id":"'$MANDATE_ID'",
       "action":{"type":"purchase.goods",
                 "amount":{"value":120,"currency":"CHF"},
                 "counterparty":"ACME AG"}}' > pres.json
# → { presentation, custody: "custodial", note }

Verify — act only on «allow»

curl -s -X POST https://mandact.com/api/v1/verify \
  -H "authorization: Bearer $MANDACT_API_KEY" \
  -H "content-type: application/json" \
  -H "idempotency-key: $(uuidgen)" \
  -d '{"presentation":'"$(jq .presentation pres.json)"',
       "action":{"type":"purchase.goods",
                 "amount":{"value":120,"currency":"CHF"},
                 "counterparty":"ACME AG"}}'
# → allow:    { decision, consumption_token, receipt, mat_compact }
# → deny:     { decision, reasons: ["MD-302"], reason_text, receipt }
# → escalate: { decision, escalation: { id, expires_at } }

After the action: record consumption and report execution

curl -s -X POST https://mandact.com/api/v1/consumptions \
  -H "authorization: Bearer $MANDACT_API_KEY" -H "content-type: application/json" \
  -d '{"consumption_token":"<token>","final_amount":120,"currency":"CHF",
       "action_type":"purchase.goods","counterparty":"ACME AG",
       "context_digest":"'"$(jq -r .presentation.ctx pres.json)"'"}'
curl -s -X POST https://mandact.com/api/v1/executions \
  -H "authorization: Bearer $MANDACT_API_KEY" -H "content-type: application/json" \
  -d '{"reservation_token":"<token>","outcome":"erfolgreich",
       "executed_amount":120,"executed_currency":"CHF","external_ref":"PO-4711"}'

Fetch the receipt as a dispute dossier and check it without Mandact

curl -s "https://mandact.com/api/v1/verifications/<rcp_…>/dossier?mat=<mat_compact>" \
  -H "authorization: Bearer $MANDACT_API_KEY" > dossier.json
node verify-dossier.mjs dossier.json --mat <mat_compact>
# → VALID … MAT: gueltig (ES256, kid …)

Path 2: the agent signs itself

The example agent creates an Ed25519 key and shows its did:key. Enter it in the wizard under «Identifier»; Mandact stores only the public key and never signs for this agent. The presentation must also sign the organisation that verifies it — the status endpoint returns that value as presentation_verifier.

node beispiel-agent.mjs schluessel
# → agent-key.pem + did:key:z6Mk…
curl -s https://mandact.com/api/v1/mandates/$MANDATE_ID/status -H "authorization: Bearer $MANDACT_API_KEY"
# → { limits, agent_did, presentation_verifier }
AGENT_KEY_FILE=agent-key.pem node beispiel-agent.mjs allow

Path 3: through the gateway

To try it without a target system of your own, there is a neutral echo target that only reports what arrived — with no side effects.

  1. Connectors → new instance → set up gateway. Target: https://mandact.com/api/sandbox/echo, choose the mandate, optionally store a target credential. The token gwt_… is shown once.
  2. Send a first POST. Mandact creates the tool «http.post:/bestellung» and denies until it is mapped to an action of your mandate.
  3. In the instance, map the tool to the action, for example purchase.goods. From then on the mandate decides.
curl -s -X POST https://mandact.com/api/gateway/http/bestellung \
  -H "authorization: Bearer $GATEWAY_TOKEN" -H "content-type: application/json" \
  -d '{"amount":120,"currency":"CHF","counterparty":"ACME AG"}' -i
# allow → X-Mandact-Decision: allow, X-Mandact-Receipt: rcp_…
# deny  → HTTP 403 { error: "mandact_deny", reasons, receipt_id }

The example agent

One file, Node ≥ 20 only, no dependencies. It reads your mandate's limits and runs all three paths: pruefen, schluessel, allow, ablehnungen, gateway. Configure it with MANDACT_API_KEY, MANDATE_ID and optionally AGENT_KEY_FILE or GATEWAY_TOKEN. Put the dossier checker next to the agent and it checks every dossier offline.

Where things honestly stand

At level 1 the principal's identity is self-declared, and the credential says so. The timestamp comes from Mandact's own database; a qualified anchor is in preparation. Decisions are made only by POST /v1/verify — the example agent and the SDK decide nothing.