API-Referenz: Grundlagen
Die tokyn.trail-API besteht aus zwei Oberflächen: der Core-API (REST, Holds, Reports, Verify) und dem MCP-Proxy-Endpoint (streamable HTTP für MCP-Clients). Beide authentifizieren mit denselben API-Keys.
Base-URLs#
| Oberfläche | URL | Lokal (Dev) |
|---|---|---|
| Core-API | https://trail.tokyn.studio | http://localhost:3000 |
| MCP-Proxy | siehe Serverseite | http://localhost:3001 |
Authentifizierung#
Alle Requests tragen einen API-Key als Bearer-Token. Keys erstellen Sie unter Einstellungen → API-Keys; der Klartext wird genau einmal angezeigt, gespeichert wird nur ein SHA-256-Hash.
Authorization: Bearer tt_live_... # Produktion
Authorization: Bearer tt_test_... # Test/EntwicklungFachlich: Ein Key gehört zum Arbeitsbereich, nicht zu einer Person - Entscheidungen über die API werden deshalb nie einem menschlichen Approver zugeschrieben. Menschen entscheiden über Inbox, E-Mail-Link, SMS, Slack oder Widget; die API legt Holds an und fragt sie ab.
Zum Wort: Was die Oberfläche Arbeitsbereich nennt, heißt in der API und in den Belegen weiterhin org bzw. orgId - Server, Regeln, Freigaben und die Hash-Chain hängen daran. Organisation ist seit der Einführung der Vertragsebene die Klammer darüber (Plan, Rechnung, Kontingent) und taucht in der API nicht auf. Die alten Feldnamen bleiben, damit bestehende Integrationen und bereits ausgestellte Nachweise gültig bleiben.
Konventionen#
- Alle Bodies sind JSON (
Content-Type: application/json). - Zeitstempel sind UTC, ISO 8601 (
2026-07-20T08:21:45.000Z). - Öffentliche Referenzen für Aktionen beginnen mit
tt_pa_- sie sind die IDs der Hold-API und der Statusseiten. - Idempotenz:
POST /v1/holdsverlangt einenidempotency_key; Wiederholungen liefern den ursprünglichen Hold statt Duplikate. - Versionierung: Alle Endpoints tragen den Pfad-Prefix
/v1. Breaking Changes erscheinen nur unter einer neuen Version -/v1bleibt stabil.
Rate Limits#
120 Anfragen pro Minute, gezählt pro Arbeitsbereich und nicht pro API-Key - sonst umginge man das Limit, indem man sich weitere Schlüssel ausstellt. Teure Pfade haben eigene Töpfe, damit ein PDF-Export nicht das Kontingent Ihrer normalen Aufrufe frisst:
| Topf | Gilt für |
|---|---|
v1 | Hold-API (anlegen, abfragen) |
v1-wait | POST /v1/holds/:id/wait (Long-Poll) |
v1-reports | Oversight- und Activity-Report (CSV/PDF) |
v1-verify | Lauf durch die Hash-Chain |
Über dem Kontingent antwortet die API mit 429 und { "error": "rate_limited" }, dazu Retry-After und die RateLimit-*-Header mit Limit, Rest und Sekunden bis zum nächsten Fenster. Aufrufe ohne Anmeldung sind deutlich enger begrenzt: 20 pro Minute und IP.
Das Fenster ist fest, nicht gleitend - an der Fenstergrenze sind kurzzeitig bis zu zwei Kontingente möglich. Das ist bekannt und hinnehmbar: Es geht um Missbrauchsabwehr, nicht um exakte Zuteilung. Fällt die Zählung selbst aus, wird durchgelassen; ein Schutz, der bei einem Datenbankwackler die ganze API abschaltet, richtet mehr Schaden an als er verhindert.
Davon unabhängig gelten Ihre eigenen Frequenz-Regeln (z. B. „mehr als 20 Calls/Stunde → halten"). Für die Statusabfrage empfehlen wir POST /holds/:id/wait (Long-Poll) oder Webhooks statt engem Polling.
Fehlerformat#
Fehler enthalten nie Payload-Inhalte - nur einen Code, bei Validierungsfehlern die betroffenen Felder und bei internen Fehlern eine referenceId für den Support:
{ "error": "invalid_json" }
{ "error": "validation_failed", "detail": "tool_name: String must contain at least 1 character(s)",
"issues": [{ "field": "tool_name", "message": "String must contain at least 1 character(s)" }],
"docs": "https://trail.tokyn.studio/docs/api/holds" }
{ "error": "internal", "referenceId": "b3f6…" }Alle Statuscodes: Fehlercodes.
Endpoint-Übersicht#
| Methode & Pfad | Zweck |
|---|---|
POST /api/v1/holds | Aktion zur Freigabe einreichen (Regeln entscheiden: allowed / pending / denied) |
GET /api/v1/holds/:id | Status einer Aktion abfragen (Polling) |
POST /api/v1/holds/:id/wait | Long-Poll auf die Entscheidung (max. 60 s pro Runde) |
GET /api/v1/reports/oversight | Oversight-Report als CSV/PDF |
GET /api/v1/reports/activity | Activity-Report als CSV/PDF |
GET /api/v1/audit/verify | Hash-Chain des Arbeitsbereichs verifizieren |
Das ist die vollständige /v1-Oberfläche. MCP-Server, Regeln, Approver und Webhook-Konfiguration verwalten Sie derzeit in der App - es gibt bewusst noch keine Management-API, keine Hold-Liste und kein programmatisches Storno. Wenn Ihnen davon etwas fehlt, sagen Sie es uns.
MCP-Proxy-Endpoint#
Pro MCP-Server existiert ein Endpoint (streamable HTTP, kein stdio):
URL: <MCP-Server-URL aus der App>
Header: Authorization: Bearer tt_live_...
X-TokynTrail-Agent: <agent-id> # optional, erscheint in der Evidenz
X-TokynTrail-Session: <session-id> # optional, Bezugsrahmen der Idempotenztools/listwird durchgereicht und löst die Tool-Discovery aus; zusätzlich injiziert der Proxy das Tooltokyn_trail_check_status.tools/callwird je nach Regel durchgereicht, gehalten (sofortige Antwort mittt_pa_…-Referenz - die Verbindung blockiert nie) oder mit MCP-Error-32003abgelehnt.- Nach einer Freigabe führt der Proxy den Upstream-Call selbst aus.
Antwort auf einen gehaltenen Aufruf#
Die Antwort besteht aus zwei Textblöcken: einem Satz für den Menschen und demselben Stand als JSON. Sie trägt die Aktion mit sich - ein Agent, der nur diese Antwort hat, kann dem Nutzer sagen, was wartet:
{
"tokyn_trail": {
"status": "held",
"reference": "tt_pa_0198…",
"status_url": "https://trail.tokyn.studio/a/tt_pa_0198…",
"terminal": false,
"retryable": false,
"retry": "poll",
"message": "Action held for human approval: create_quote: customer=ACME GmbH, amount=1.295,00 €. …",
"action": {
"tool": "create_quote",
"arguments": { "customer": "ACME GmbH", "amount_cents": 129500 },
"summary": "create_quote: customer=ACME GmbH, amount=1.295,00 €"
},
"expires_at": "2026-09-03T16:00:00.000Z"
}
}Denselben Umschlag liefert tokyn_trail_check_status. status ist eines von held, approved, executed, denied, expired, error oder unknown_reference; retry sagt, was zu tun ist: poll (noch offen), none (fertig), never (abgelehnt - nicht erneut versuchen) oder new_request (abgelaufen oder fehlgeschlagen - eine neue Anfrage ist erlaubt). Bei executed folgt das Ergebnis des Upstream-Calls als weitere Inhaltsblöcke.
Idempotenz#
Ein identischer Aufruf - gleiches Werkzeug, gleiche Argumente, gleiche Sitzung - erzeugt genau einen Vorgang. Während der Hold offen ist, liefert jeder Wiederholungsaufruf denselben Hold zurück (gekennzeichnet mit "deduplicated": true); nach einer Freigabe das Ergebnis der bereits ausgeführten Aktion, ohne sie erneut auszuführen; nach einer Ablehnung dieselbe Ablehnung. Läuft ein Hold ab oder schlägt die Ausführung fehl, ist der nächste identische Aufruf wieder eine echte Anfrage. Ohne X-TokynTrail-Session gilt die Agent-Identität als Sitzung.