code, nicht die menschliche message (Messages können sich ändern; Codes sind
stabil).
Das Error-Envelope
Erfolgreiche Antworten enthalten nie ein
error-Objekt. Verzweige zuerst auf den
HTTP-Status, dann auf error.code.Error-Codes
invalid_api_key
HTTP 401. Der Authorization-Header fehlt oder ist fehlerhaft, oder der Key
ist unbekannt, abgelaufen oder widerrufen — oder der Key-Ersteller ist kein
Mitglied der Organisation mehr. Abgelaufene und unbekannte Keys sind absichtlich
nicht unterscheidbar.
Authorization: Bearer samsa_sk_….
Wenn der Key widerrufen oder abgelaufen wurde,
erstelle einen neuen. Wiederhole
nicht — das Ergebnis wird sich nicht ändern.
missing_scope
HTTP 403. Der Key ist gültig, aber ihm fehlt der
Scope, den der Endpoint erfordert. param ist
"scope" und die Message nennt den fehlenden Scope.
insufficient_credits
HTTP 402. Der ausgebbare Credit-Pool der Organisation (Abonnement plus gültige
Top-ups) liegt unter den Kosten der Aktion. Siehe Preise. Hat
die Organisation mindestens ein aktives Team, gibt die API stattdessen die
team-bezogenen Codes insufficient_team_credits oder
insufficient_unallocated_credits zurück — mit
zwei Ausnahmen. Eine vom Team-Budgeting ausgenommene System-Organisation bleibt auch
mit aktiven Teams im org-Regime und behält diesen generischen Code. Und ein
operativer Fehler in der Abbuchung selbst (etwa eine gestörte Saldo-Abfrage) fällt
unabhängig vom Budget-Regime auf diesen generischen Code zurück. Ein schlichtes
insufficient_credits in einem Team-Setup kann also vorübergehend sein; versuche es
erneut, bevor du es als erschöpften Stand behandelst.
GET /credits
ein available, das die Kosten abdeckt, hast du möglicherweise den operativen
Fallback getroffen — wiederhole einmal. Dieser Read ist allerdings eine
Momentaufnahme: auch eine Zustandsänderung seit der Ablehnung (ein Top-up, eine
Erstattung, ein geändertes Budget oder ein Periodenwechsel) kann das erklären, ein
echter Engpass ist also nicht ausgeschlossen. Andernfalls lade auf oder upgrade den
Plan der Organisation in der App, dann wiederhole. Es wurde nichts berechnet und kein
Job erstellt.
subscription_inactive
HTTP 402. Die Organisation hat kein nutzbares Abonnement (keines aktiv, in
Kulanzfrist oder mit ausgebbaren Top-ups). Unterscheidet sich von
insufficient_credits, wo ein Abonnement existiert, der Pool aber zu niedrig ist.
insufficient_team_credits
HTTP 402. Organisationen können ihre monatlichen Abonnement-Credits in
Budgets pro Team aufteilen, und jeder API-Key kann einem Team zugewiesen werden.
Ein Key, der einem Team mit Budget zugewiesen ist, gibt aus dem monatlichen Bucket
dieses Teams aus; alles, was keinem Team-Budget zugewiesen ist, bildet den nicht
zugewiesenen Pool der Organisation. Gekaufte Top-up-Credits sind von Budgets
ausgenommen und bleiben verfügbar, um den Rest zu decken. Dieser Code tritt auf, wenn der
Stand, den der Key ausgeben kann — der kleinere Wert aus dem verbleibenden Plan-Pool der
Organisation und dem verbleibenden Team-Budget, plus Top-ups —, unter den Kosten der Aktion
liegt. Genau das ist das available, das
GET /credits meldet.
Der Code nennt das Budget-Regime, nicht den erschöpften Stand. Ein Key, der einem Team
mit Budget zugewiesen ist, erhält diesen Code auch dann, wenn der Plan-Pool der
Organisation und nicht das Team-Budget aufgebraucht war — das Team kann also noch
Spielraum ausweisen. Vergleiche
plan_credits mit scope.remaining unter
GET /credits, um zu sehen, was begrenzt.insufficient_unallocated_credits
HTTP 402. Der Key ist keinem Team mit Budget zugewiesen (nicht zugewiesen, oder
sein Team hat kein Budget), daher gibt er aus dem nicht zugewiesenen Pool der
Organisation aus — den monatlichen Abonnement-Credits, die nach Abzug der Budgets
aller aktiven Teams übrig bleiben. Dieser Code tritt auf, wenn der Stand, den der Key
ausgeben kann — der kleinere Wert aus dem verbleibenden Plan-Pool der Organisation und dem
Spielraum dieses nicht zugewiesenen Pools, plus der von Budgets ausgenommenen Top-ups —,
unter den Kosten der Aktion liegt; derselbe Vorbehalt dazu, welcher Stand erschöpft war,
gilt wie oben. Organisationen ohne
aktives Team sehen die beiden team-bezogenen Codes nie — sie erhalten stattdessen
das einfache insufficient_credits.
not_found
HTTP 404. Die id ist unbekannt oder sie gehört einer anderen Organisation. Die
beiden Fälle sind per Design nicht unterscheidbar, sodass die Existenz nie über
Organisationen hinweg preisgegeben wird.
validation_error
HTTP 422. Der Request-Body oder ein Parameter hat die Validierung nicht
bestanden. param nennt das betreffende Feld; message erklärt die Einschränkung.
message und param, dann
reiche sie erneut ein. Das ist ein Client-Fehler — dieselbe Anfrage zu wiederholen
schlägt identisch fehl.
svg_acceptance_required
HTTP 422. Nur POST /images/vectorizations
liefert diesen Code. SVG ist ein dokumentierter EU-AI-Act-Art.-50(2)-Scope-out: Ein SVG
kann kein C2PA-Manifest und kein eingebettetes Wasserzeichen tragen, deshalb werden
Vektorausgaben unsigniert ausgeliefert. svg_acceptance muss daher der literale
Boolean true sein — ein fehlender, false oder anderer Wert wird mit diesem eigenen
Code abgelehnt, nie mit dem generischen
validation_error. Die Zustimmung ist Offenlegung /
Audit-Nachweis, kein Compliance-Verzicht. Es wird nichts berechnet und kein Job
erstellt.
"svg_acceptance": true, sobald
deine Integration den Hinweis auf die unsignierte Ausgabe denjenigen anzeigt, die mit dem
Ergebnis arbeiten.
svg_phase1_scope_out_required
HTTP 403. Ebenfalls nur für die Vektorisierung und verschieden von
missing_scope — mit dem Scope des Keys ist alles in Ordnung. Die
Auslieferung erfordert zusätzlich eine aktuelle, serverseitig verifizierte
ToS/AUP-Zustimmung; das Request-Flag svg_acceptance wird nie als dieser Fakt
gewertet. Beim Absenden wird eine fehlende oder veraltete Zustimmung abgelehnt, bevor
etwas berechnet wird. Die Prüfung läuft bei der Auslieferung erneut: Ein bereits
angenommener Job kann also von
GET /images/vectorizations/{id} oder
bei der Webhook-Auslieferung noch abgelehnt werden, wenn die Zustimmung zwischenzeitlich
abläuft — eine Ablehnung bei der Auslieferung kostet nichts zusätzlich, erstattet aber
auch nicht die Credits, die der abgeschlossene Job bereits verbraucht hat.
GET-Status neu ab — der Webhook wird nicht automatisch erneut gesendet. Das Akzeptieren
ist Offenlegung, kein Verzicht.
rate_limited
HTTP 429. Der Key hat sein Request-Rate-Fenster pro Key überschritten. Die
Antwort trägt die Header Retry-After und X-RateLimit-*. Siehe
Rate Limits.
Retry-After-Intervall ab und wiederhole
dann. Verwende exponentielles Backoff bei wiederholten 429ern.
too_many_active_jobs
HTTP 429. Die Organisation hat ihren Cap für gleichzeitige laufende Jobs
erreicht. Die Message enthält den aktuellen Stand und das Limit, und ein
Retry-After-Header ist gesetzt.
GET-Endpoint ab oder nutze einen
Webhook), bevor du weitere einreichst, dann wiederhole nach
Retry-After.
internal_error
HTTP 500. Ein unerwarteter Fehler auf Samsas Seite. Die Antwort verrät nie
interne Details — die request_id ist dein Ansatzpunkt für den Support.
request_id.
request_id für den Support nutzen
Jeder Fehler (und jeder Erfolg) trägt eine request_id, auch zurückgegeben als
X-Request-ID-Response-Header. Sie verbindet deinen clientseitigen Fehler, den
Response-Header und Samsas Server-Logs zu einer Anfrage. Logge sie und nenne sie,
wenn du support@samsa.ai kontaktierst — das ist der
schnellste Weg für uns, genau herauszufinden, was passiert ist.

