Zurück zur Anleitung

Bill Alps API: Konventionen und Fehler

Was auf jeder Route gilt: Hülle, Paginierung, Daten, strikte Bodies, Fehler, Nutzung, und was ein Sandbox-Reset mit Ihren Schlüsseln macht.

Entwickler6 Min. Lesezeit
Inhaltsverzeichnis+

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 POST und PATCH immer nötig ({} ist ein gültiger leerer Patch; ein leerer Body ist INVALID_JSON), ausser bei Aktionen ohne Body wie POST …/default; DELETE nimmt 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 …/logo auf einer Firma und auf einer Rechnungsvorlage (nur PNG oder JPEG, 2 MB). Der Content-Type nennt das Format, der Body ist die Datei; alles Übrige reist im Query-String.
  • Ein unbekannter Query-Schlüssel ist ein 400 VALIDATION_ERROR auf jeder Route, die einen Query-Vertrag deklariert; GET /me und GET /companies ignorieren den Rest, ohne dass das ein Versprechen wäre.
  • PUT auf 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).