Hold-API & SDK
Fachlich: Ein Hold ist die Frage „Darf ich das?" — gestellt von einem Agenten, beantwortet von einem Menschen. Die Hold-API ist der Weg für alles, was nicht über den MCP-Proxy läuft: LangGraph-Knoten, n8n-Workflows, Eigenbau-Agenten. Ihre Regeln, Approver, Fristen und die Evidenz sind identisch mit dem Proxy-Pfad; die Tools der Hold-API erscheinen unter der impliziten Connection „SDK" im Rule Builder.
Das typische Muster: hold() → auf Entscheidung warten → selbst ausführen. Anders als beim MCP-Proxy führt tokyn.trail hier nichts aus — Ihr Code bleibt Herr der Ausführung und ruft die Aktion erst nach approved auf.
POST /api/v1/holds#
Reicht eine Aktion ein. Die Decision Engine antwortet sofort mit einem von drei Ergebnissen.
Request-Body#
| Feld | Typ | Pflicht | Bedeutung |
|---|---|---|---|
tool_name | string | ja | Fachlicher Name der Aktion, z. B. send_payment. Erstverwendung registriert das Tool fail-closed als „neu" — klassifizieren Sie es im Rule Builder. |
arguments | object | nein | Die Parameter der Aktion. Erscheinen dem Approver und in der Evidenz; Regeln können darauf zugreifen (args.amount …). |
agent_identity | { id, label? } | ja | Wer handelt. Taucht in Inbox, Reports und Audit-Events auf. |
context | object | nein | Zusatzinfos für den Approver (Ticket-Nr., Workflow-Run …). |
idempotency_key | string | ja | Eindeutig pro logischer Aktion (z. B. order-4711-payment). Retries liefern denselben Hold — nie Duplikate. |
Response 201 (bzw. 200 bei Idempotenz-Replay)#
{ "id": "tt_pa_019f7c08…", "status": "pending", "status_url": "https://app.tokyn-trail.eu/a/tt_pa_…" }status | Bedeutung | Ihr nächster Schritt |
|---|---|---|
allowed | Eine Allow-Regel hat gegriffen; protokolliert als auto_allowed. | Aktion sofort ausführen. |
pending | Ein Mensch muss entscheiden; Approver sind benachrichtigt. | Warten: /wait, Polling oder Webhook. |
denied | Eine Deny-Regel hat geblockt; als abgelehnt protokolliert. | Aktion nicht ausführen; Grund steht in der Decision. |
Die status_url ist eine öffentliche Statusseite zur Referenz — sie zeigt ohne Login nur Status und Zeitstempel (plus Branding Ihrer Organisation), nie Tool-Namen, Argumente oder Agent-Identität. Sie können sie gefahrlos in Tickets oder Chat-Nachrichten verlinken.
Beispiel (curl)#
curl -X POST https://app.tokyn-trail.eu/api/v1/holds \
-H "Authorization: Bearer $TOKYN_TRAIL_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"tool_name": "send_payment",
"arguments": { "amount": 4900, "currency": "EUR", "to": "ACME Supplies" },
"agent_identity": { "id": "billing-agent" },
"idempotency_key": "order-4711-payment"
}'GET /api/v1/holds/:id#
Einmaliges Status-Polling. :id ist die tt_pa_…-Referenz.
{ "id": "tt_pa_…", "status": "approved", "expires_at": "…",
"decision": { "verdict": "approve", "reason": null, "channel": "inbox", "decided_at": "…" } }Statuswerte: pending · approved · rejected · expired (Frist abgelaufen) · auto_allowed · executed/failed (nur Proxy-Aktionen, die tokyn.trail selbst ausführt).
Status-Vokabular auf einen Blick#
POST-Antwort status | Gespeicherter Status (GET) | decision.verdict | Webhook-Event |
|---|---|---|---|
allowed | auto_allowed | — | — (kein Event) |
pending → freigegeben | approved | approve | hold.approved |
pending → abgelehnt | rejected | reject | hold.rejected |
pending → Frist abgelaufen | expired | — | hold.expired |
denied | rejected | reject (Regel) | hold.rejected |
Fristen & Eskalation#
Jede Freigaberegel trägt eine Frist (timeout_minutes): Default 240 Minuten, Minimum 1 Minute, kein Maximum. Läuft sie ab, greift on_timeout: reject (Default — der Hold wird expired) oder escalate (die nächste Approver-Gruppe der Eskalationskette übernimmt, die Frist beginnt neu).
POST /api/v1/holds/:id/wait#
Long-Poll: antwortet, sobald entschieden wurde, spätestens nach 60 Sekunden mit dem aktuellen Stand. Für längere Wartezeiten in Schleife aufrufen — oder Webhooks nutzen, wenn Ihr Prozess nicht blockieren soll (Empfehlung für n8n & Co.).
SDK: @tokyn-trail/sdk#
Das TypeScript-SDK kapselt genau diese drei Endpoints — dependency-frei.
npm install @tokyn-trail/sdkimport { TokynTrail } from '@tokyn-trail/sdk';
const trail = new TokynTrail({ apiKey: process.env.TOKYN_TRAIL_API_KEY! });
const decision = await trail.hold({
tool_name: 'send_payment',
arguments: { amount: 4900, currency: 'EUR' },
agent_identity: { id: 'billing-agent' },
idempotency_key: 'order-4711-payment', // Pflicht
});
// hold() wartet per Long-Poll auf die Entscheidung (Default-Timeout 15 min).
// wait: false → sofort zurück, Ergebnis per Webhook.
if (decision.approved) {
await executePayment(); // Ausführung bleibt bei Ihnen
} else if (decision.rejected) {
log.warn('abgelehnt', decision.decision?.reason);
}Weitere Methoden: trail.getHold(id) (einmaliges Polling), trail.waitForDecision(id, { timeoutMs }), verifyWebhookSignature(rawBody, signature, secret).
Statuscodes#
| Code | Wann |
|---|---|
201 / 200 | Hold angelegt / Idempotenz-Replay |
400 | Body ist kein gültiges JSON (invalid_json) |
401 | API-Key fehlt, unbekannt oder widerrufen |
402 | Monatliches Hold-Limit erreicht (Free-Plan) |
404 | Unbekannte Referenz (oder fremde Organisation) |
422 | Validierung fehlgeschlagen — detail nennt das erste Feld, issues alle betroffenen |