Die HTTP-API¶
Alles, was die Konsole tut, tut sie über diese API. Es gibt keine private Oberfläche: dieselben Endpunkte stehen Ihnen offen.
Die interaktive Referenz wird aus dem Code erzeugt und vom Deployment selbst
unter /docs ausgeliefert, das Schema unter /api/v1/openapi.json. Beides
ist in der Entwicklung an und in der Produktion aus — ENVIRONMENT entscheidet
darüber, damit ein Produktions-Deployment nicht seine eigene Routenliste
veröffentlicht.
Authentifizierung¶
Drei Wege hinein, für drei verschiedene Aufrufer.
| Header | Für | |
|---|---|---|
| JWT | Authorization: Bearer <access token> |
Eine Person oder etwas, das als eine handelt. Kurzlebig, wird mit einem Refresh-Token erneuert |
| API-Key | X-API-Key: <key> |
Dienst zu Dienst. Kein Nutzer dahinter |
| Session-Cookie | von der Konsole gesetzt | Nur der Browser — das Token ist HttpOnly und erreicht JavaScript nie |
Keys werden mit secrets.compare_digest verglichen, nie mit ==, und ein Key
wird so gespeichert wie alle anderen Zugangsdaten auch.
Sessions und Widerruf¶
Ein JWT-Access-Token ist an die Session gebunden, die seine Anmeldung geöffnet
hat — die Id der Session reist im Token mit. Eine Abmeldung überall
(DELETE /sessions) deaktiviert diese Sessions, und ein gebundenes Token wird
danach bei der nächsten Verwendung abgelehnt, statt seine wenigen verbleibenden
Minuten auszuleben. Das erreicht auch ein offenes Chat-WebSocket: der nächste
Frame auf einer widerrufenen Session schließt den Socket, nicht erst die nächste
HTTP-Anfrage.
Ein Refresh startet keine neue Session — das Refresh-Token rotiert an Ort und Stelle und das Access-Token nennt weiterhin dieselbe —, sodass eine langlebige Verbindung von einem routinemäßigen Refresh nicht gekappt wird.
Der Organisations-Header¶
X-Organization-Id reist auf jeder Anfrage mit, und es ist keine optionale
Verzierung: er entscheidet, in welchem Tenant der Aufruf handelt.
Ein Aufrufer, der zu drei Organisationen gehört, ist in jeder ein anderer Principal, mit einer anderen Rolle und anderen Grants. Lassen Sie den Header weg, hat die Anfrage keinen Tenant, in dem sie handeln könnte; senden Sie den falschen, bekommen Sie eine Ablehnung, die genau so aussieht, als gäbe es die Ressource nicht — absichtlich, damit Ids nicht abtastbar werden.
Einen Agent ausführen¶
curl -X POST "$BASE/api/v1/agents/$AGENT_ID/run" \
-H "Authorization: Bearer $TOKEN" \
-H "X-Organization-Id: $ORG_ID" \
-H "Content-Type: application/json" \
-d '{"prompt": "How do I rotate a provider key?"}'
Die Antwort trägt die Id des Runs, die Ausgabe und den Status. Zwei optionale
Felder im Body sind wissenswert: conversation_id setzt einen bestehenden
Thread fort, und environment_id wählt, welche Umgebung
antwortet.
Ein API-Aufrufer kann die Governance nicht umgehen
Dieser Endpunkt geht durch denselben Runner wie die Konsole, Slack und das Widget. Der Run wird aufgezeichnet, das Budget wird vor der Modellanfrage geprüft, das Tor für Freigaben gilt, und die Kosten landen im selben Dashboard.
Das ist der Sinn eines einzigen Runners, und deshalb gibt es keinen "schnellen Weg", der ihn überspringt.
Die Route trägt eine Rate-Limitierung und kein Berechtigungs-Tor. Über die Berechtigung entscheidet der Service, anhand der Grants genau dieses Agents — ein Rollen-Tor auf einer Route für eine einzelne Ressource kann sie nicht sehen.
Streaming¶
Zwei WebSocket-Endpunkte, für zwei Zielgruppen.
/api/v1/ws/agent— der authentifizierte, den die Konsole verwendet. Ein Frame mitagent_idführt diesen veröffentlichten Agent aus; ein Frame ohne sie bekommt den allgemeinen Assistenten./api/v1/embed/{public_key}/ws— der öffentliche hinter einem Embed, für einen Besucher, der kein Konto hat.
Beide streamen Token, sobald sie eintreffen, und beide erzeugen einen gewöhnlichen Run, mit derselben Buchführung wie alles andere.
Fehler¶
Überall ein Umschlag:
{
"error": {
"code": "NOT_FOUND",
"message": "Agent not found",
"details": { "agent_id": "..." }
}
}
details trägt Werte statt Zeilen, nennt also das Feld, das eine Ablehnung
erklärt, und nie einen Datenbankeintrag. Geht eine Ablehnung um etwas, das der
Aufrufer eingereicht hat, ist details.fields eine Liste aus
{field, message} — und genau das lässt ein Formular die Eingabe markieren,
statt einen Satz zu zeigen, für den jemand die Seite erneut absuchen muss.
Ein 401 trägt WWW-Authenticate: Bearer. Ein Lesezugriff über Tenant-Grenzen
hinweg antwortet mit 404, nicht mit 403, aus dem oben genannten Grund.
Konventionen¶
| Präfix | /api/v1 |
| Anlegen | POST, 201 |
| Teilweises Aktualisieren | PATCH |
| Löschen | DELETE, 204, kein Body |
| Seitenaufteilung | Query-Parameter skip (≥ 0) und limit (1–100); Listenantworten tragen items und total |
| Pfade | kebab-case |
Stabilität, ehrlich gesagt¶
Es gibt noch keine veröffentlichte Kompatibilitätszusage und keine Client-Bibliothek. Die API ist seit dem ersten Commit öffentlich, und der Vertrag über die Versionierung ist Arbeit auf der Roadmap (R10).
In der Praxis sind die Formen stabil geblieben, und das Präfix /api/v1
bedeutet, dass eine brechende Änderung neben der jetzigen landen würde statt auf
ihr — aber bis das aufgeschrieben ist, behandeln Sie sie als das, was sie ist:
eine API, gegen die Sie die Tests Ihrer Integration festnageln sollten.
Das eine Format, das sehr wohl eine Zusage trägt, ist der Spec des Agents: er ist versioniert und bewegt sich nur vorwärts.
Zusammenfassung¶
/docsauf dem Deployment ist die erzeugte Referenz; in der Produktion ist sie bewusst aus.- Drei Wege hinein: JWT,
X-API-Keyoder das Cookie der Konsole. X-Organization-Identscheidet über den Tenant bei jeder Anfrage, und der falsche sieht aus wie eine fehlende Ressource.- Einen Agent über HTTP auszuführen ist derselbe Runner — Budget, Freigabe und Audit gelten alle.
- Noch keine Kompatibilitätszusage und kein SDK (R10); der Spec des Agents ist das eine versionierte Format.