# webRichtung Agent Gateway Linear runbook for an external agent. Follow it top to bottom; do not skip OTP, operator approval or budget gates. Use dry_run when the contract requires it or when a paid action needs a guard estimate. ## 1. What, Base URL, Auth - What: machine entrypoint for webRichtung capabilities: discover, describe, dry-run and invoke platform work. - Base URL: use the origin that served this file. Production examples use `https://connect.webrichtung.de`; relative links remain valid elsewhere. - Auth header for protected calls: `Authorization: Bearer wr_ak_.`. - Idempotency rule: plain HTTP mutations use header `Idempotency-Key`; `/mcp/invoke` uses body field `idempotency_key`; the value is agent-generated, 16-128 chars, unique per mutation, and may be reused only with identical params. - Store each cleartext key immediately. Keys are returned once and must not appear in logs. ## 2. Discovery and Meta-Tools - `GET /health`: service health. - `GET /mcp/manifest`: transport, auth shape, resources and tool schemas. - `GET /mcp/tools`: compact definitions for the four meta-tools. - `GET /capabilities`: generated catalog summary and invocable entries. - `GET /openapi.json`: OpenAPI 3.1 surface generated from the same contracts. - Human-readable mirror and recovery: `https://docs.webrichtung.de/agents-betreiber/`. - Catalog snapshot 147jae3: 272 visible capabilities, 206 invocable contracts. Do not inline the catalog into your prompt; query it. The only MCP meta-tools are: 1. `search_capabilities`: find a compact shortlist by query, domain and risk. 2. `describe`: fetch the full runtime contract for one capability. 3. `dry_run`: validate and preview effects without state changes. 4. `invoke`: execute one capability through auth, scope, policy, idempotency, audit, billing and egress guards. ## 3. Register: bootstrap_key `POST /register` is public and creates the Agent App plus a bootstrap key. Required body fields are `owner_email`, `app.slug` and `app.name`. `agent_email` is optional; set it only when this agent must verify access to its own mailbox. Send the mutation header from step 1. ```json { "owner_email": "owner@example.com", "agent_email": "agent@example.com", "app": { "slug": "example-agent", "name": "Example Agent" } } ``` Use only `bootstrap_key.token` from the response for step 4. It is not a working installation key and cannot run operational capabilities. ## 4. Create Organization: installation_key `POST /organizations` uses the bootstrap key and creates the owner organization, the agent installation and the first installation key. Send the same `owner_email`; include `agent_email` only if it was used at registration. Send the mutation header from step 1. ```json { "owner_email": "owner@example.com", "agent_email": "agent@example.com", "organization": { "name": "Example GmbH" } } ``` Key rule: use the bootstrap key only up to this call. Store `installation_key.token` from the response and use the installation key for all following OTP, status, discovery-with-auth, dry-run and invoke calls. The agent acts in the owner organization and spends from the owner wallet. If the installation key is lost, do not bootstrap a replacement organization; stop and hand over for recovery. ## 5. Owner-OTP and Agent-OTP Owner-OTP and Agent-OTP may be completed in either order. If `agent_email` was supplied, both OTPs are mandatory. Operational work stays blocked until all required OTPs have succeeded. The response includes `owner_otp_delivery.next_action`. Treat it as a quoted field value for German owner handover: ```txt Der Owner erhaelt einen Code per E-Mail. Sobald er ihn dir mitteilt, reiche ihn an POST /owner-otp/claim ein (Body {"otp":""}) mit deinem Installation-Key. ``` ### Owner-OTP-Claim Owner mails may point to `https://connect.webrichtung.de/llms.txt#owner-otp-claim`; this is the claim instruction block for that link. - Owner-OTP: wait for the human owner to share the Owner-Code from the copyable mail briefing. Accept either `Owner-Code: ` or `Code: `; parse with `^(?:Owner-)?Code:\s*(?\S+)\s*$`. Then call `POST /owner-otp/claim` with the installation key and body `{ "otp": "" }`. - Agent-OTP: when `agent_email` was supplied, read `agent_otp_delivery.correlation_id`, search the agent mailbox for an IMAP subject containing `Ref: `, and parse the standalone body line with `^Code:\s*(?\S+)\s*$`. Then call `POST /agent-otp/verify` with the installation key and body `{ "otp": "" }`. - Resend: if an owner or agent OTP is missing, expired, invalidated or exhausted after too many attempts, call `POST /owner-otp/resend` or `POST /agent-otp/resend` with the installation key, then retry with the new code. ## 6. operator_approval Polling Until all required OTPs are verified, `GET /org/current`, `/keys/rotate` and operational `/mcp/dry_run` or `/mcp/invoke` return HTTP 412 `precondition_failed`. This covers the Owner-OTP and, when `agent_email` was supplied, the Agent-OTP. It is expected legitimation state, not a platform outage. After required OTPs, `GET /org/current` is the status-read exception: it can show pending operator approval while operational `/mcp/dry_run` and `/mcp/invoke` still return HTTP 412. Poll it with the installation key. First poll after 30 seconds, then at most once every 60 seconds. Poll for at most 15 minutes. - `installation.operator_approval_status = approved`: continue to step 7. - `pending`: keep polling inside the window. - `blocked`: stop and hand over to the owner/operator. - Window exceeded while still pending: stop polling, hand over, and do not run operational capabilities yet. ## 7. First Paid Action: documents.document.upload Use one continuous example: upload a small text document. Do not guess parameters; always read the contract first. Vocabulary: `dry_run` is required exactly when `describe.dry_run` equals `required`. For this paid upload, `describe.dry_run` is `supported`, but you still run it to obtain the cost estimate for the guard. 1. Describe the capability. ```json { "id": "documents.document.upload" } ``` 2. Prepare params from `describe.input_schema`. ```json { "file_name": "note.txt", "mime_type": "text/plain", "content_base64": "SGVsbG8gd2VicmljaHR1bmc=" } ``` 3. Call `POST /mcp/dry_run` for `documents.document.upload` with these params. Do not send `idempotency_key` or approval tokens to `dry_run`. Read `estimated_cost.amount_max`; its currency must be `credits`. 4. Call `POST /mcp/invoke` for the free guard `wallet.agent_budget.guard.check` before the paid invoke. ```json { "id": "wallet.agent_budget.guard.check", "params": { "capability_id": "documents.document.upload", "estimated_cost_credits": 0.09, "window": "task" } } ``` Continue only when the normal invoke envelope has `status=succeeded` and `result.allowed=true`. Cap values constrain only when greater than 0; cap 0 means no active cap. Details: `https://docs.webrichtung.de/agents-betreiber/konzepte/budgets-und-kill-switch/`. On `budget_exceeded`, `precondition_failed` or `capability_disabled`, fail closed: do not invoke the paid capability. 5. Invoke `documents.document.upload` only after the guard allows it. ```json { "id": "documents.document.upload", "params": { "file_name": "note.txt", "mime_type": "text/plain", "content_base64": "SGVsbG8gd2VicmljaHR1bmc=" }, "idempotency_key": "idem-doc-upload-0001" } ``` A normal upload returns `status=succeeded` and result fields such as `outcome`, `job_id`, `status`, `content_hash` and `link_context`. Upload may return `pending`; the first paid action does not require waiting for document processing completion. Successful invoke envelope: ```json { "interface_version": "1.0", "status": "succeeded", "capability_id": "documents.document.upload", "resolved_version": "1.0.0", "audit_id": "audit-id", "result": { "...": "capability-specific output" }, "cost": { "...": "present only when charged or reported" } } ``` ## 8. Error Envelope Every gateway or capability error is structured JSON. Read `error.code` first, use `error.hint`, retry only when `error.retryable=true`, and keep `error.audit_id` for handover or support. ```json { "error": { "code": "validation_error", "message": "Params violate the capability input schema.", "hint": "Call describe(id) and resend params matching input_schema.", "retryable": false, "audit_id": "audit-id" } } ``` ## Catalog and Detail Reference Do not use an inline capability dump from this file. Use `GET /capabilities` for the generated catalog, `POST /mcp/search_capabilities` for search, `POST /mcp/describe` for exact schemas, `/docs/agent-docs/` for the generated runtime detail reference, and `https://docs.webrichtung.de/agents-betreiber/` for human-readable recovery and operator handover.