Die drei anderen Anleitungen gehen Abläufe durch; diese sammelt die Regeln, die für jeden Aufruf gelten, zum Bereithalten, während Sie Ihren Client schreiben.
BASE=https://<deployment>.convex.site/api/v1
KEY=ba_...1. Die data-Hülle
Jede erfolgreiche Antwort packt ihre Nutzlast in data:
{ "data": [ ... ] }Zwei Ausnahmen ohne Body: 204 No Content beim Löschen und 302 beim Herunterladen von PDFs, Belegen und Logos, mit -L zu folgen.
2. Paginierung
Listen von Firmenressourcen sind paginiert, das Neueste zuerst:
{ "data": [ ... ], "nextCursor": "..." }Drei Listen sind nicht paginiert und antworten mit einem einfachen Array unter data: GET /companies, GET /mandates und GET /companies/{companyId}/invoice-templates.
3. Daten und Kennungen
Datumsangaben werden als Epoch-Millisekunden oder YYYY-MM-DD angenommen und als Epoch-Millisekunden zurückgegeben. Die Filter from/to begrenzen das Ausstellungsdatum, beide Enden eingeschlossen. Eine Ausnahme: Abwesenheiten speichern und liefern YYYY-MM-DD-Strings, weil das ihre Sortierreihenfolge ist.
Kennungen sind undurchsichtige Strings. Eine unbekannte, eine fehlerhafte und eine Kennung einer anderen Firma antworten alle 404 NOT_FOUND: Die drei Fälle sind bewusst nicht unterscheidbar, damit keine Kennung eines anderen Kontos durchsickert.
4. Strikte Bodies
- Die Bodies der Routen für Konto, Firma, Kunde, Rechnung, Offerte, Wiederholung und Vorlage sind strikt: Jeder unbekannte Schlüssel wird abgelehnt.
- Ein Body ist bei
POSTundPATCHimmer nötig ({}ist ein gültiger leerer Patch; ein leerer Body istINVALID_JSON), ausser bei Aktionen ohne Body wiePOST …/default;DELETEnimmt keinen. - Über 1 MB wird der Body abgelehnt:
413 PAYLOAD_TOO_LARGE. - Fünf Routen nehmen rohe Bytes statt JSON: den Beleg einer Ausgabe, die KI-Extraktion und den KI-Anhang (PDF, PNG, JPEG oder WebP, 10 MB), dazu
PUT …/logoauf einer Firma und auf einer Rechnungsvorlage (nur PNG oder JPEG, 2 MB). DerContent-Typenennt das Format, der Body ist die Datei; alles Übrige reist im Query-String. - Ein unbekannter Query-Schlüssel ist ein
400 VALIDATION_ERRORauf jeder Route, die einen Query-Vertrag deklariert;GET /meundGET /companiesignorieren den Rest, ohne dass das ein Versprechen wäre. PUTauf einer Abwesenheit ist ein vollständiges Ersetzen: ausgelassene optionale Felder werden gelöscht.
Ein Validierungsfehler listet jedes fehlerhafte Feld unter issues, als gepunktete Pfade, Array-Indizes eingeschlossen:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request body",
"issues": [
{ "path": "subtotal", "message": "Unrecognized key \"subtotal\"" },
{ "path": "vatRates.0.rate", "message": "Too big: expected number to be <=100" }
]
}
}5. Fehler
Fehler tragen einen stabilen, maschinenlesbaren Code:
{ "error": { "code": "PAYSLIP_ALREADY_EXISTS" } }Nur VALIDATION_ERROR (mit issues) und INVALID_TRANSITION fügen eine message hinzu; jeder andere Code antwortet allein. Verzweigen Sie auf error.code, nie auf Prosa. Diese Hülle gilt auf jedem Pfad unter /api/v1/, auch bei unbekanntem Pfad (404 NOT_FOUND) und nicht unterstützter Methode (405 METHOD_NOT_ALLOWED, mit Allow-Header).
Die Prüfungen laufen in dieser Reihenfolge: Schlüssel und Inhaber (401), Sichtbarkeit der Firma (404), dann archivierte FirmaPlanModulBerechtigung (403). Eine Firma, bei der der Schlüsselinhaber nicht Mitglied ist, ist von einer nicht existierenden nicht zu unterscheiden.
401 INVALID_API_KEY: Schlüssel fehlt, fehlerhaft, unbekannt oder widerrufen; Inhaber gesperrt oder gelöscht.403 PLAN_NOT_ALLOWED,403 COMPANY_ARCHIVED,403 ACCESS_DENIED: Plan, Archivierung, fehlende Berechtigung oder Rolle.403 INVOICING_MODULE_DISABLED,403 PAYROLL_MODULE_DISABLED,403 EXPENSES_MODULE_DISABLED,403 FINANCIAL_STATEMENTS_MODULE_DISABLED: Modul auf der Firma deaktiviert.403 *_LIMIT_REACHED: Plankontingent erreicht (Kunden, Rechnungen, Wiederholungen, Mitarbeitende, Abrechnungen, Ausgaben).400 BATCH_SIZE_INVALID: Stapel ausserhalb von 1 bis 100 Elementen.409 INVALID_TRANSITION,409 PAYSLIP_ALREADY_EXISTS,409 ABSENCE_OVERLAPS_EXISTING,409 QUOTE_NOT_EDITABLE: Konflikt mit dem aktuellen Zustand.422 MODIFICATION_REASON_REQUIRED,422 CLIENT_EMAIL_MISSING,422 PERIOD_FISCAL_YEAR_MISMATCH: gültige Anfrage, in diesem Kontext unzulässig.503 PDF_UNAVAILABLE,502 EMAIL_SEND_FAILED,500 INTERNAL_ERROR: erneut versuchen.
Die vollständige Liste der Codes ist die Aufzählung in der Swagger UI und in openapi.json Ihres Deployments.
6. Limiten und Nutzung
Es gibt noch keine Ratenbegrenzung. Aufrufe werden gezählt, nicht gebremst: ein Zähler pro Schlüssel, Endpunkt, Zielfirma und UTC-Tag, sichtbar unter KontoAPI-SchlüsselNutzung. Von der Authentifizierung abgelehnte Anfragen werden nicht gezählt; ein 400 auf einem autorisierten Aufruf schon.
Seien Sie trotzdem ein guter Nachbar: Blättern Sie durch Listen, statt sie in Schleife abzufragen, und ziehen Sie sich bei 500 oder 503 zurück. Die beiden Stapel verhalten sich nicht gleich: POST …/payslips/batch ist nicht atomar und liefert ein Ergebnis pro Element, POST …/bank-transactions/batch ist atomar, alles oder nichts.
7. Sandbox und Reset
Die Sandbox schreibt nie in die Produktion. E-Mails, Stripe und Clerk-Einladungen sind dort inert, die Registrierung ist geschlossen, Logos werden nicht kopiert. Alles andere läuft, auch die geplanten Jobs: Wiederkehrende Rechnungen werden erzeugt und Lohnabrechnungen auf den kopierten Daten berechnet.
Zurück zu den Abläufen: Erste Schritte mit der API, Rechnungen per API, Lohn, Ausgaben und Buchhaltung per API. Jedes Feld, jede Antwort und die vollständige Aufzählung der Codes stehen in der Swagger UI Ihres Deployments (…/api/docs).