Projekte über die API
Die Einheit einer Unterhaltung in BasePeak.AI ist das Projekt, nicht der
nackte Agent. Ein Projekt bündelt einen geteilten Arbeitsbereich, eigene
Aufgaben, projektbezogene Anmeldedaten und eigene Modelleinstellungen – all
das, was eine Unterhaltung im Browser von einer über
/api/invoke/{id} gestarteten unterscheidet.
POST /api/projects/{project_id}/invoke hängt den neuen Thread an das
Projekt: derselbe Elternbezug, den auch die Oberfläche setzt. Ab diesem
Zeitpunkt ist es dieselbe Unterhaltung, die auch im Browser erscheint –
nicht nur eine ähnliche.
| Kommt vom Projekt | Bei /api/invoke/{id} |
|---|---|
| Geteilter Arbeitsbereich (Dateien) | fehlt – nur der Arbeitsbereich des Agenten oder des Threads ist erreichbar |
| Aufgaben des Projekts als Werkzeuge | fehlen vollständig |
| Projektbezogene Anmeldedaten-Kontexte | fehlen |
| Modelleinstellungen und Wissenssammlungen des Projekts | fehlen |
agent:chat auf /api/invoke/{id} bleibt die schlankere OberflächeDas ist kein Fehler, den diese Seite behebt – /api/invoke/{id} bleibt der
richtige Weg für Kopfstand-Agenten (headless) und System-Agenten, die an
keinem Projekt hängen. Er ist nur dünner: er spricht mit dem nackten
Agenten, nicht mit der Unterhaltung, die der Browser führt. Adressieren Sie
das Projekt, wenn Sie dessen Kontext brauchen; adressieren Sie den Agenten,
wenn Sie ihn nicht brauchen.
Ein Projekt wird über seine Projekt-ID angesprochen, an der einheitlichen
Kennung p1… zu erkennen – überall, für die Unterhaltung wie für jeden
Inhalt des Projekts. Die im Browser verwendete Adressierung über den
Agenten-Pfad (/api/assistants/{id}/projects/{project_id}/…) bleibt der
Browser-Sitzung vorbehalten; ein Schlüssel erreicht ausschließlich
/api/projects/{project_id}/….
# Konversation in einem Projekt starten.
curl -X POST https://<host>/api/projects/p1abc/invoke \
-H "Authorization: Bearer $BPAI_TOKEN" \
-H "Content-Type: text/plain" \
--data 'Summarise the files in this project'
# Eine Datei hochladen, die die nächste Unterhaltung in diesem Projekt lesen kann.
curl -X POST https://<host>/api/projects/p1abc/files/report.csv \
-H "Authorization: Bearer $BPAI_TOKEN" \
--data-binary @report.csv
Eine Konversation im Projekt führen
POST /api/projects/{project_id}/invoke
POST /api/projects/{project_id}/invoke/thread/{thread_id}
POST /api/projects/{project_id}/invoke/threads/{thread_id}
Scope: project:chat:<project_id>.
Beide Routen verhalten sich wie ihr Gegenstück auf
/api/invoke/{id}: derselbe Antwortkörper, derselbe
Antwort-Header X-BPAI-Thread-Id, dieselben Accept-Varianten
(Streaming/JSON/reiner Text) und dasselbe ?async=true. Ein Client, der
bereits gegen /api/invoke/{id} spricht, ändert nur die URL.
# Neue Unterhaltung im Projekt.
curl -sS -X POST \
https://your-instance.platform.basepeak.ai/api/projects/p1abc/invoke \
-H "Authorization: Bearer sk-bpai-1-42-…" \
-H "Accept: application/json" \
-H "Content-Type: text/plain" \
--data 'Fasse die Dateien in diesem Projekt zusammen.'
# Dieselbe Unterhaltung fortsetzen.
curl -sS -X POST \
https://your-instance.platform.basepeak.ai/api/projects/p1abc/invoke/thread/t1abcde \
-H "Authorization: Bearer sk-bpai-1-42-…" \
-H "Accept: application/json" \
--data 'Und was folgt daraus für Q4?'
# Ohne auf die Antwort zu warten.
curl -sS -X POST \
'https://your-instance.platform.basepeak.ai/api/projects/p1abc/invoke?async=true' \
-H "Authorization: Bearer sk-bpai-1-42-…" \
--data 'Erstelle den Monatsbericht.'
Der Agent kommt dabei aus dem Projekt – Sie nennen ihn nirgends. Ein
{thread_id}, das zu einem anderen Projekt gehört, wird nie stillschweigend
übernommen: die Route antwortet dann mit 403. Siehe
Threads für Fortsetzen, Abfragen und async im Detail;
das gilt hier unverändert.
Ein über diese Route erzeugter Thread ist ein ganz normaler Chat-Thread. Die
bestehenden flachen Thread-Routen (GET /api/threads/{id}, /events,
/files, POST .../abort) funktionieren für ihn unverändert. Nur das
Projekt selbst – der Eltern-Thread – bleibt dort unerreichbar; sein eigener
Arbeitsbereich hat eigene Scopes, siehe unten.
Zum Zurücklesen dieser Unterhaltungen ist project:threads:<p> die erste
Wahl: Der Scope deckt Auflisten, Abrufen und erneutes Abspielen ausschließlich
für die Unterhaltungen dieses Projekts ab. Der agentengebundene
thread:read:<agent> funktioniert ebenfalls, erlaubt aber jede Unterhaltung des
Projekt-Agenten – über alle Projekte hinweg, die er bedient, und damit weiter
als das Projekt, an das Sie anbinden. /files bleibt an den Agenten gebunden
(thread:files), POST .../abort an agent:chat. Siehe
Unterhaltungen.
Dateien: der geteilte Arbeitsbereich des Projekts
GET /api/projects/{project_id}/files
GET /api/projects/{project_id}/file[s]/{file}
POST /api/projects/{project_id}/file[s]/{file}
DELETE /api/projects/{project_id}/file[s]/{file}
Scope: project:files:<project_id> (lesend) oder
project:files-write:<project_id> (lesend und schreibend –
-write ist eine strikte Erweiterung von project:files, ein Schlüssel
braucht nie beide).
Das ist derselbe geteilte Arbeitsbereich, den die Oberfläche als Projekt-Dateien zeigt – nicht der Arbeitsbereich eines einzelnen Threads. Jede Unterhaltung, die im Projekt beginnt, sieht ihn.
# Auflisten.
curl -sS https://your-instance.platform.basepeak.ai/api/projects/p1abc/files \
-H "Authorization: Bearer sk-bpai-1-42-…"
# Hochladen – rohe Bytes, kein Multipart-Formular.
curl -sS -X POST \
https://your-instance.platform.basepeak.ai/api/projects/p1abc/files/report.csv \
-H "Authorization: Bearer sk-bpai-1-42-…" \
--data-binary @report.csv
# Herunterladen.
curl -sS https://your-instance.platform.basepeak.ai/api/projects/p1abc/files/report.csv \
-H "Authorization: Bearer sk-bpai-1-42-…" -o report.csv
# Löschen.
curl -sS -X DELETE \
https://your-instance.platform.basepeak.ai/api/projects/p1abc/files/report.csv \
-H "Authorization: Bearer sk-bpai-1-42-…"
Ein mit project:files-write hochgeladenes Dokument liest die nächste
Unterhaltung im selben Projekt automatisch mit – dieselbe Zusicherung, die
Dateien für den geteilten Arbeitsbereich beschreibt.
Wissen: Hochladen, dann auf den Status pollen
GET /api/projects/{project_id}/knowledge
GET /api/projects/{project_id}/knowledge/{file}
POST /api/projects/{project_id}/knowledge/{file}
DELETE /api/projects/{project_id}/knowledge/{file}
Scope: project:knowledge:<project_id> (lesend) oder
project:knowledge-write:<project_id> (lesend und schreibend – ebenfalls
eine strikte Erweiterung).
Es gibt keine separate Status-Route. GET /api/projects/{project_id}/knowledge
liefert für jede Datei state, error sowie Start- und Endzeit der
Einlese-Verarbeitung – derselbe Weg, den die Oberfläche selbst geht:
hochladen, dann die Liste abfragen, bis state den Endzustand erreicht.
# Hochladen.
curl -sS -X POST \
https://your-instance.platform.basepeak.ai/api/projects/p1abc/knowledge/handbuch.pdf \
-H "Authorization: Bearer sk-bpai-1-42-…" \
-H "Content-Type: application/pdf" \
--data-binary @handbuch.pdf
# Pollen, bis die Datei eingelesen ist.
curl -sS https://your-instance.platform.basepeak.ai/api/projects/p1abc/knowledge \
-H "Authorization: Bearer sk-bpai-1-42-…"
import httpx, time
http = httpx.Client(base_url=BASE, headers={"Authorization": f"Bearer {TOKEN}"})
with open("handbuch.pdf", "rb") as f:
http.post("/api/projects/p1abc/knowledge/handbuch.pdf",
headers={"Content-Type": "application/pdf"},
content=f.read())
while True:
files = http.get("/api/projects/p1abc/knowledge").json()["items"]
entry = next((f for f in files if f["fileName"] == "handbuch.pdf"), None)
if entry and entry["state"] not in ("pending", "ingesting"):
break
time.sleep(2)
425 Too Early, solange die Wissenssammlung noch nicht existiertEin neu angelegtes Projekt erhält seine Wissenssammlung asynchron. Laden Sie
unmittelbar danach hoch, antworten POST und DELETE auf dieser Route mit
425 Too Early, bis sie bereitsteht. Ein Client, der ein Skript unmittelbar
nach dem Anlegen des Projekts startet, trifft das zuverlässig – behandeln Sie
425 wie ein "noch nicht", nicht wie einen Fehler, und wiederholen Sie den
Aufruf.
Aufgaben: Erstellen existiert nur hier
POST /api/projects/{project_id}/tasks
GET /api/projects/{project_id}/tasks
GET /api/projects/{project_id}/tasks/{id}
PUT /api/projects/{project_id}/tasks/{id}
DELETE /api/projects/{project_id}/tasks/{id}
POST /api/projects/{project_id}/tasks/{id}/run
POST /api/projects/{project_id}/tasks/{id}/runs/{run_id}/steps/{step_id}/run
GET /api/projects/{project_id}/tasks/{id}/runs
GET /api/projects/{project_id}/tasks/{id}/runs/{run_id}
DELETE /api/projects/{project_id}/tasks/{id}/runs/{run_id}
POST /api/projects/{project_id}/tasks/{id}/runs/{run_id}/abort
Scope: project:tasks:<project_id> – unaufgeteilt, deckt Lesen, Schreiben
und Ausführen gleichermaßen ab.
# Aufgabe anlegen.
curl -sS -X POST https://your-instance.platform.basepeak.ai/api/projects/p1abc/tasks \
-H "Authorization: Bearer sk-bpai-1-42-…" \
-H "Content-Type: application/json" \
-d '{"name": "Wöchentlicher Report", "steps": [{"step": "Fasse die Woche zusammen"}]}'
# Ausführen.
curl -sS -X POST \
https://your-instance.platform.basepeak.ai/api/projects/p1abc/tasks/w1abc123/run \
-H "Authorization: Bearer sk-bpai-1-42-…" \
-H "Content-Type: application/json" -d '{}'
# Läufe lesen.
curl -sS https://your-instance.platform.basepeak.ai/api/projects/p1abc/tasks/w1abc123/runs \
-H "Authorization: Bearer sk-bpai-1-42-…"
POST /api/tasksAufgaben anlegen funktioniert ausschließlich über diese projektbezogene
Route. Die flache Oberfläche unter Management-API
kann eine bestehende Aufgabe lesen, ändern und ausführen (Scope
task:manage), aber nie eine neue erzeugen.
task:manage und project:tasks sind kein Ober-/Unterbegriff
Die beiden überschneiden sich, ohne dass einer den anderen enthält:
| Scope | Reichweite |
|---|---|
task:manage (nur *) | Jede Aufgabe, die dem Konto des Schlüssels gehört – über alle Projekte hinweg, ohne Möglichkeit, auf ein einzelnes Projekt zu pinnen. Lesen, Ändern, Ausführen; kein Anlegen. |
project:tasks:<p> | Jede Aufgabe in genau diesem Projekt – auch eine, die dem Konto nur über eine ThreadAuthorization-Freigabe zugänglich ist, nicht über Eigentum. Genau dieser Fall bleibt für die flache Oberfläche (GET /api/tasks) unsichtbar, da sie ausschließlich nach Eigentum filtert. |
Ein Schlüssel mit project:tasks:p1abc erreicht also Aufgaben eines
Projekts, das dem Konto nur freigegeben wurde – etwas, das kein
task:manage-Schlüssel je könnte, unabhängig von dessen Reichweite.
Umgebungsvariablen: zwei unabhängige Hälften
GET /api/projects/{project_id}/env
PUT /api/projects/{project_id}/env
| Route | Scope |
|---|---|
GET | project:env:<project_id> |
PUT | project:env-write:<project_id> |
project:env und project:env-write implizieren sich nicht gegenseitigAnders als bei Dateien und Wissen ist die Schreib-Hälfte hier keine
Erweiterung der Lese-Hälfte. GET /api/projects/{project_id}/env liefert die
gespeicherten Werte der Umgebungsvariablen zurück – das sind die
Geheimnisse, die die Werkzeuge des Projekts benutzen. Ein Schlüssel mit nur
project:env-write kann Werte setzen, aber nicht lesen; ein Schlüssel mit
nur project:env kann sie lesen, aber nicht ändern. Wer beides braucht,
muss beide Scopes ausstellen.
Der Grund für den Bruch mit dem sonstigen Muster: Ein Schlüssel, der nur ein Geheimnis rotieren soll, muss es nie lesen können.
# Werte setzen.
curl -sS -X PUT https://your-instance.platform.basepeak.ai/api/projects/p1abc/env \
-H "Authorization: Bearer sk-bpai-1-42-…" \
-H "Content-Type: application/json" \
-d '{"API_TOKEN": "sk-live-…"}'
# Werte lesen – erfordert project:env, nicht project:env-write.
curl -sS https://your-instance.platform.basepeak.ai/api/projects/p1abc/env \
-H "Authorization: Bearer sk-bpai-1-42-…"
Scopes im Überblick
| Route | Benötigter Scope |
|---|---|
POST /api/projects/{p}/invoke, .../invoke/thread(s)/{thread} | project:chat:<p> |
GET /api/projects/{p}/files, .../file(s)/{file} | project:files:<p> oder project:files-write:<p> |
POST/DELETE /api/projects/{p}/file(s)/{file} | project:files-write:<p> |
GET /api/projects/{p}/knowledge, .../knowledge/{file} | project:knowledge:<p> oder project:knowledge-write:<p> |
POST/DELETE /api/projects/{p}/knowledge/{file} | project:knowledge-write:<p> |
POST/GET/PUT/DELETE /api/projects/{p}/tasks… | project:tasks:<p> |
GET /api/projects/{p}/env | project:env:<p> |
PUT /api/projects/{p}/env | project:env-write:<p> |
Alle acht Scopes gründen ausschließlich auf Projektmitgliedschaft – keine
Management-Berechtigung ist nötig, um irgendeinen von ihnen auszustellen,
auch nicht die schreibenden Hälften: Die Browser-Sitzung verlangt für
dieselben Mutationen ebenfalls nichts weiter als Zugriff auf das Projekt, und
diese Scopes verlangen nicht mehr. Das unterscheidet sie von
agent:files-write, dessen Ausstellung zusätzlich agent:update
voraussetzt – siehe API-Schlüssel.
Ein Schlüssel mit Platzhalter (project:chat:* etc.) wird bei jedem Aufruf
gegen die aktuelle Projektmitgliedschaft des zugehörigen Kontos geprüft –
verliert das Konto den Zugriff auf ein Projekt, greift ein solcher Scope für
dieses Projekt sofort nicht mehr.
Was über die API weiterhin nicht erreichbar ist
Drei Projekt-Oberflächen bleiben Schlüsseln verschlossen, nicht aus Versehen, sondern weil sie wer ein Projekt erreichen darf ändern statt es zu bedienen:
| Nicht erreichbar | Warum |
|---|---|
| Projekt-Mitglieder | Ändert, wer das Projekt erreicht – Browser-Sitzung, nicht Integrationsrecht. |
| Projekt-Einladungen | Dasselbe: ein kompromittierter Schlüssel soll niemanden ins Projekt einladen können. |
| Projekt-Anmeldedaten (Verwaltung) | Gibt gespeicherte Geheimnisse heraus – anders als project:env, das gezielt für genau diesen Zweck existiert. |
Fehlerbehebung
| Antwort | Bedeutung |
|---|---|
400 | Die Projekt-ID ist keine p1…-ID (oder das Alias default, das sich pro Konto auflöst und sich daher nicht mit einem Scope abgleichen lässt). |
403 | Die Scopes decken das Projekt nicht ab, oder das Konto des Schlüssels hat keinen Zugriff mehr auf dieses Projekt. Ein Scope für Projekt A greift nie für Projekt B. |
425 | Die Wissenssammlung des Projekts existiert noch nicht – siehe oben, erneut versuchen. |
Weiterführende Dokumentation
- API-Schlüssel – die neun Projekt-Scopes im Kontext aller Scopes
- Invoke-API – der Ereignisstrom, den beide Routen teilen
- Threads – Fortsetzen, Pollen,
async - Dateien – der Arbeitsbereich des Projekts neben Agent und Thread