tokyntraildocs

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#

FeldTypPflichtBedeutung
tool_namestringjaFachlicher Name der Aktion, z. B. send_payment. Erstverwendung registriert das Tool fail-closed als „neu" — klassifizieren Sie es im Rule Builder.
argumentsobjectneinDie Parameter der Aktion. Erscheinen dem Approver und in der Evidenz; Regeln können darauf zugreifen (args.amount …).
agent_identity{ id, label? }jaWer handelt. Taucht in Inbox, Reports und Audit-Events auf.
contextobjectneinZusatzinfos für den Approver (Ticket-Nr., Workflow-Run …).
idempotency_keystringjaEindeutig 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_…" }
statusBedeutungIhr nächster Schritt
allowedEine Allow-Regel hat gegriffen; protokolliert als auto_allowed.Aktion sofort ausführen.
pendingEin Mensch muss entscheiden; Approver sind benachrichtigt.Warten: /wait, Polling oder Webhook.
deniedEine 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 statusGespeicherter Status (GET)decision.verdictWebhook-Event
allowedauto_allowed— (kein Event)
pending → freigegebenapprovedapprovehold.approved
pending → abgelehntrejectedrejecthold.rejected
pending → Frist abgelaufenexpiredhold.expired
deniedrejectedreject (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/sdk
import { 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#

CodeWann
201 / 200Hold angelegt / Idempotenz-Replay
400Body ist kein gültiges JSON (invalid_json)
401API-Key fehlt, unbekannt oder widerrufen
402Monatliches Hold-Limit erreicht (Free-Plan)
404Unbekannte Referenz (oder fremde Organisation)
422Validierung fehlgeschlagen — detail nennt das erste Feld, issues alle betroffenen