Pfeffersack REST API
Programmatischer Zugriff auf Rechnungen, Dokumente, Belege und Buchungen Ihres Pfeffersack-Accounts. Die Endpoints sind nach Sektion getrennt — GmbHs nutzen die doppelte Buchführung (Journal), Einzelunternehmen die vereinfachte Buchungs-Tabelle. Neben Lesezugriff unterstützt die API Write-Endpoints für Agents und Integrationen: Entwürfe für Journal, Rechnungen und Offerten, Kontakte, Anlagen und Beleg-Uploads — abgesichert über Scopes und Idempotenz.
Quickstart
- API-Key erstellen unter Einstellungen → Integrationen → API-Keys. Der Key wird nur einmal angezeigt.
- Den Key im
Authorization-Header jedes Requests mitsenden. - Alle Responses sind JSON mit
Content-Type: application/json; charset=utf-8.
Erster Request
curl -H "Authorization: Bearer pfs_live_IHR_KEY" \
"https://pfeffersack.ch/api/v1/gmbh/invoices?limit=5"Authentifizierung
Die API nutzt Bearer-Token-Authentifizierung. Der Key wird einmalig bei der Erstellung angezeigt — speichern Sie ihn an einem sicheren Ort (Passwort-Manager oder Secrets- Store Ihres Deployment-Systems).
Authorization: Bearer pfs_live_AbCdEf0123456789...Key-Format
| Prefix | Typ | Beschreibung |
|---|---|---|
| pfs_live_ | string | Produktiv-Key. Lesezugriff immer; Schreibzugriff nur mit expliziten Scopes (siehe Schreibzugriff). |
| pfs_test_ | string | Reserviert für zukünftigen Test-Mode (noch nicht aktiv). |
Zeichensatz nach Prefix: A–Z a–z 0–9 _ (Base62 + Underscore). Länge: 32+ Zeichen. Keys werden serverseitig nur als SHA-256-Hash gespeichert.
Fehlgeschlagene Authentifizierung
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="Pfeffersack API"
Content-Type: application/json
{ "success": false, "error": "Unauthorized" }Antworten sind bewusst uniform (gleicher Shape, gleiche Latenz) — das verhindert User-Enumeration und Timing-Attacks.
Konventionen
- Base-URL:
https://pfeffersack.ch/api/v1— ausschließlich HTTPS. Plain-HTTP-Requests werden abgewiesen. - Timestamps: ISO-8601 in UTC mit
Z-Suffix (z. B.2026-04-02T14:21:00.000Z) für Datetime-Felder,YYYY-MM-DDfür reine Datumsfelder. - Geldbeträge: JSON
numbermit bis zu 2 Nachkommastellen (z. B.1621.50). Keine Minor-Units. Null = kein Wert,0= explizit null. - Währung: ISO-4217 (meist
CHF, optionalEUR,USD). - IDs: Integer, auto-incrementiert pro User- Scope. IDs sind innerhalb Ihres Accounts eindeutig, aber nicht global.
- Sortierung: Listen sind absteigend nach Datum + ID sortiert (neueste zuerst). Reihenfolge ist stabil über Pages hinweg.
- CORS: Die API ist für Server-to-Server gedacht. Browser-Aufrufe von Drittdomains sind nicht erlaubt — der API-Key würde sonst im Client-Bundle leaken.
- Unbekannte Felder: Ignorieren. Neue Felder können additiv hinzukommen und gelten nicht als Breaking Change.
Rate Limits
Pro API-Key: 120 Requests pro Minute (rollendes Fenster). Bei Überschreitung 429 Too Many Requests mit Retry-After (Sekunden bis Reset).
HTTP/1.1 429 Too Many Requests
Retry-After: 42
X-RateLimit-Reset: 1713540000
Content-Type: application/json
{ "success": false, "error": "Zu viele Anfragen. Bitte versuchen Sie es später erneut." }Write-Endpoints sind strenger gedrosselt: 30 Requests pro Minute für alle POSTs (Journal, Rechnungen, Buchungen, Kontakte, Anlagen), 10 Requests pro Minute für Datei-Uploads (Belege). Der Dry-Run /journal/validate erlaubt 60/min.
Zusätzlich gilt ein IP-basierter Vorab-Limit von 30 Requests pro Minute für unautorisierte Anfragen (fehlender/ungültiger Key). Das schützt vor Brute-Force auf den Key-Lookup.
Empfehlung: Implementieren Sie exponentielles Backoff mit Jitter. Respektieren Sie Retry-After strikt — Requests während der Sperrzeit zählen in den nächsten Window- Zähler.
Pagination
Alle List-Endpoints sind paginiert. Response-Body enthält strukturierte Pagination-Metadaten im Feld pagination.
| Parameter | Typ | Default | Beschreibung |
|---|---|---|---|
| page | integer | 1 | 1-basierte Seitennummer. |
| limit | integer (1–100) | 25 | Einträge pro Seite. Werte außerhalb des Bereichs werden automatisch geclampt. |
{
"success": true,
"data": [ /* ... */ ],
"pagination": {
"page": 1,
"limit": 25,
"total": 142,
"total_pages": 6,
"has_next": true,
"has_prev": false
}
}Fehler-Format
Alle Fehler folgen demselben JSON-Envelope — success=false plus lokalisierte error-Meldung.
{ "success": false, "error": "Rechnung nicht gefunden" }Fehlermeldungen sind menschenlesbar (Deutsch) und können sich ändern. Unterscheiden Sie Fehlerfälle über den HTTP-Status, nicht über den Text.
Status Codes
| Code | Typ | Beschreibung |
|---|---|---|
| 200 OK | success | Request erfolgreich, Response-Body enthält Daten. |
| 400 Bad Request | client | Ungültige Eingabe (z. B. Route-Parameter keine positive Ganzzahl). |
| 401 Unauthorized | client | Kein, ungültiger oder widerrufener API-Key. Header prüfen. |
| 403 Forbidden | client | Key existiert, hat aber keinen Zugriff auf diese Ressource (z. B. GmbH-Endpoint mit Einzelfirma-Key). |
| 404 Not Found | client | Ressource existiert nicht oder gehört nicht zu Ihrem Account. |
| 409 Conflict | client | Duplikat (z. B. Rechnungsnummer bereits vergeben) oder Idempotency-Key mit abweichendem Request wiederverwendet. |
| 422 Unprocessable Entity | client | Syntaktisch gültig, aber Geschäftsregel verletzt (z. B. Soll ≠ Haben, Konto nicht bebuchbar, kein offenes Geschäftsjahr). |
| 429 Too Many Requests | client | Rate-Limit überschritten. Retry-After respektieren. |
| 500 Internal Server Error | server | Unerwarteter Serverfehler. Mit exponentiellem Backoff retrien. |
Schreibzugriff (Write-Endpoints)
Die API erstellt konsequent Entwürfe: Journal-Einträge entstehen mit status="draft" und können nur in der App geprüft und gebucht werden. So können Agents vorbereiten, ohne dass ungeprüfte Daten im Hauptbuch landen. Buchungen der Einzelfirma kennen keinen Entwurfsstatus und sind sofort final (dokumentierte Ausnahme).
Scopes
Schreiben erfordert einen expliziten Write-Scope am API-Key (wählbar beim Erstellen des Keys). Keys ohne Scopes sind read-only — bestehende Keys erhalten also nie ungewollt Schreibrechte. Fehlt der Scope, antwortet der Endpoint mit 403 Forbidden.
| Scope | Typ | Beschreibung |
|---|---|---|
| write:journal | scope | GmbH-Journalentwürfe erstellen und validieren. |
| write:invoices | scope | Rechnungs- und Offertenentwürfe (GmbH + Einzelfirma). |
| write:bookings | scope | Einzelbuchungen der Einzelfirma erstellen. |
| write:contacts | scope | Debitoren, Kreditoren und Kunden anlegen. |
| write:assets | scope | Anlagen erfassen (GmbH). |
| write:documents | scope | Belege hochladen (GmbH-Dokumente + Standard-Belege). |
| write:* | scope | Alle Schreibrechte (Convenience-Wildcard). |
Idempotenz
Alle JSON-POSTs unterstützen den Header Idempotency-Key (Client- generierte UUID, dringend empfohlen). Netzwerk-Retries erzeugen damit garantiert keine doppelten Buchungen oder Rechnungen:
- Gleicher Key + gleicher Request → die gespeicherte Response wird 1:1 wiederholt (Header
Idempotency-Replayed: true). - Gleicher Key + abweichender Request →
409 Conflict. - Serverfehler (5xx) werden nie gespeichert — Retries bleiben möglich.
- Dedup-Fenster: 24 Stunden.
curl -X POST \
-H "Authorization: Bearer pfs_live_IHR_KEY" \
-H "Idempotency-Key: 4c1f9e64-2b7a-4f30-9a51-8f4a1c2d3e5f" \
-H "Content-Type: application/json" \
-d '{
"entry_date": "2026-07-08",
"description": "Abschreibung Lieferwagen 2026",
"lines": [
{ "account_id": 6800, "debit": 5000, "credit": 0 },
{ "account_id": 1530, "debit": 0, "credit": 5000 }
]
}' \
"https://pfeffersack.ch/api/v1/gmbh/journal"Write-Endpoints im Überblick
| Endpoint | Typ | Beschreibung |
|---|---|---|
| POST /v1/gmbh/journal | write:journal | Journalentwurf (Soll/Haben, min. 2 Zeilen, ausgeglichen, CHF). Status immer 'draft'. |
| POST /v1/gmbh/journal/validate | write:journal | Dry-Run: identische Validierung ohne Insert. Liefert { valid, balanced, totals, errors[] }. |
| POST /v1/gmbh/invoices | write:invoices | Rechnungs-/Offertenentwurf. Offerte = ohne Ledger-Wirkung; Rechnung erzeugt wie in der App Journal-Eintrag + Offenen Posten (Draft, stornierbar). |
| POST /v1/standard/invoices | write:invoices | Rechnung der Einzelfirma (startet als 'Unbezahlt'; kein PDF-Upload). |
| POST /v1/standard/bookings | write:bookings | Einzelbuchung (sofort final). FX-Umrechnung + MWST-Split wie in der App. |
| POST /v1/gmbh/debtors · /v1/gmbh/creditors | write:contacts | Debitor/Kreditor anlegen (Nummer wird autogeneriert). |
| POST /v1/standard/customers | write:contacts | Kunde der Einzelfirma anlegen. |
| POST /v1/gmbh/assets | write:assets | Anlage erfassen. Abschreibungs-Vorschau: GET /v1/gmbh/assets/{id}/depreciation-schedule (kein Buchen — das bleibt dem Jahresabschluss vorbehalten). |
| POST /v1/gmbh/documents · /v1/standard/belege | write:documents | Beleg hochladen (multipart/form-data, Feld 'file', max. 25 MB). OCR startet automatisch; Status via GET pollen (processing_status). |
Alle Writes validieren referenzierte IDs (Konten, Debitoren, Kreditoren, MWST-Sätze) gegen Ihren Account — fremde IDs führen zu 422. Periodensperren und geschlossene Geschäftsjahre werden mit 403 abgewiesen. Jeder GmbH-Write wird im Audit-Log festgehalten.
GmbH Endpoints
Erfordern einen API-Key eines Accounts mit business_type = "gmbh". Andernfalls 403 Forbidden. Sensible interne Felder (PDF-Blobs, S3-Keys, Creator-IDs) werden grundsätzlich nicht exponiert.
/v1/gmbh/invoices
business_type =gmbhPaginierte Liste von GmbH-Rechnungen (eingehend und ausgehend).
Query-Parameter
| Parameter | Typ | Default | Beschreibung |
|---|---|---|---|
| page | integer | 1 | Siehe Pagination. |
| limit | integer (1–100) | 25 | Siehe Pagination. |
| status | enum | — | draft, sent, paid, partial, overdue, void, cancelled |
| document_type | enum | — | invoice, offerte, proforma, credit_note |
| from | date (YYYY-MM-DD) | — | Filter: invoice_date ≥ from |
| to | date (YYYY-MM-DD) | — | Filter: invoice_date ≤ to |
curl -H "Authorization: Bearer $PFS_KEY" \
"https://pfeffersack.ch/api/v1/gmbh/invoices?status=paid&from=2026-01-01"{
"success": true,
"data": [
{
"id": 142,
"invoice_number": "R-2026-001",
"invoice_type": "outgoing",
"document_type": "invoice",
"invoice_date": "2026-03-15",
"due_date": "2026-04-14",
"valid_until": null,
"debtor_id": 23,
"creditor_id": null,
"net_amount": 1500.00,
"vat_amount": 121.50,
"gross_amount": 1621.50,
"paid_amount": 1621.50,
"currency": "CHF",
"vat_treatment": "normal",
"price_type": "net",
"discount_percentage": 0,
"status": "paid",
"reference": "RF18 5390 0754 7034",
"notes": null,
"items": [
{ "description": "Beratung März", "quantity": 10, "unit_price": 150.00, "vat_rate": 8.1 }
],
"sent_at": "2026-03-15T09:12:00.000Z",
"paid_at": "2026-04-02T14:21:00.000Z",
"created_at": "2026-03-15T09:10:00.000Z",
"updated_at": "2026-04-02T14:21:00.000Z"
}
],
"pagination": { "page": 1, "limit": 25, "total": 1, "total_pages": 1, "has_next": false, "has_prev": false }
}/v1/gmbh/invoices/{id}
business_type =gmbhEinzelne Rechnung. 404 wenn die ID nicht existiert oder nicht zu Ihrem Account gehört. 400 wenn {id} keine positive Ganzzahl ist.
/v1/gmbh/documents
business_type =gmbhPaginierte Liste hochgeladener Dokumente (Belegerfassung mit OCR) — inklusive Dokumenten, die noch nicht verbucht sind. Solange kein Journaleintrag existiert, ist journal_entry_id null. Damit lässt sich z. B. programmatisch prüfen, welche Belege bereits erfasst bzw. verarbeitet sind. OCR-Rohtext und S3-Keys werden nicht exponiert.
Query-Parameter
| Parameter | Typ | Default | Beschreibung |
|---|---|---|---|
| page | integer | 1 | Siehe Pagination. |
| limit | integer (1–100) | 25 | Siehe Pagination. |
| document_type | enum | — | invoice_in, invoice_out, receipt, bank_statement, contract, expense_report, other |
| processing_status | enum | — | OCR-Status: pending, processing, completed, failed |
| has_journal_entry | true | false | — | Nur verbuchte (true) bzw. noch unverbuchte (false) Dokumente. |
| from | date (YYYY-MM-DD) | — | Filter: document_date ≥ from |
| to | date (YYYY-MM-DD) | — | Filter: document_date ≤ to |
curl -H "Authorization: Bearer $PFS_KEY" \
"https://pfeffersack.ch/api/v1/gmbh/documents?has_journal_entry=false&processing_status=completed"{
"success": true,
"data": [
{
"id": 3104,
"filename": "rechnung-hosting-april.pdf",
"mime_type": "application/pdf",
"file_size": 92144,
"document_type": "invoice_in",
"vendor_name": "Hostpoint AG",
"document_date": "2026-04-03",
"total_amount": 29.00,
"currency": "CHF",
"vat_amount": 2.17,
"processing_status": "completed",
"journal_entry_id": null,
"created_at": "2026-04-05T10:18:42.000Z",
"updated_at": "2026-04-05T10:18:55.000Z"
}
],
"pagination": { "page": 1, "limit": 25, "total": 1, "total_pages": 1, "has_next": false, "has_prev": false }
}/v1/gmbh/documents/{id}
business_type =gmbhEinzelnes Dokument. 404 wenn die ID nicht existiert oder nicht zu Ihrem Account gehört.
/v1/gmbh/belege
business_type =gmbhPaginierte Liste von Belegen, die als Anhang an Buchungen hochgeladen wurden. Soft-gelöschte Belege sind standardmäßig ausgeblendet. Die Original-Datei (PDF/Bild) in S3 wird über diese API nicht bereitgestellt.
Hinweis: Dokumente aus der Belegerfassung (Upload mit OCR, Verbuchung erfolgt später) finden Sie unter /v1/gmbh/documents — dieser Endpoint hier liefert nur Buchungs-Anhänge.
Query-Parameter
| Parameter | Typ | Default | Beschreibung |
|---|---|---|---|
| page | integer | 1 | Siehe Pagination. |
| limit | integer (1–100) | 25 | Siehe Pagination. |
| kategorie | string (≤50) | — | Belegkategorie (z. B. büromaterial, reisekosten). |
| from | date (YYYY-MM-DD) | — | Filter: beleg_datum ≥ from |
| to | date (YYYY-MM-DD) | — | Filter: beleg_datum ≤ to |
| include_deleted | 1 | omit | — | Bei '1' werden soft-gelöschte Belege mitgeliefert. |
{
"success": true,
"data": [
{
"id": 812,
"beleg_datum": "2026-04-10",
"kategorie": "büromaterial",
"beschreibung": "Toner HP LaserJet",
"betrag": 129.00,
"waehrung": "CHF",
"file_type": "application/pdf",
"file_size_bytes": 184320,
"original_filename": "rechnung-toner.pdf",
"buchung_id": null,
"journal_entry_id": 4421,
"asset_id": null,
"is_deleted": false,
"uploaded_at": "2026-04-10T08:44:11.000Z",
"created_at": "2026-04-10T08:44:11.000Z",
"updated_at": "2026-04-10T08:44:11.000Z"
}
],
"pagination": { "page": 1, "limit": 25, "total": 1, "total_pages": 1, "has_next": false, "has_prev": false }
}/v1/gmbh/belege/{id}
business_type =gmbhEinzelner Beleg. Keine Original-Datei in der Response.
/v1/gmbh/journal
business_type =gmbhPaginierte Liste der Journal-Einträge (Hauptbuch). In der GmbH- Sektion ersetzt das Journal die einfache Buchungstabelle — jeder Eintrag repräsentiert eine doppelte Buchung mit Referenz auf das Geschäftsjahr.
Query-Parameter
| Parameter | Typ | Default | Beschreibung |
|---|---|---|---|
| page | integer | 1 | Siehe Pagination. |
| limit | integer (1–100) | 25 | Siehe Pagination. |
| fiscal_year_id | integer | — | Auf ein Geschäftsjahr filtern. |
| status | enum | — | draft, posted, voided |
| source | string (≤30) | — | Quelle der Buchung, z. B. invoice, bank_import, manual, payroll. |
| from | date (YYYY-MM-DD) | — | Filter: entry_date ≥ from |
| to | date (YYYY-MM-DD) | — | Filter: entry_date ≤ to |
{
"success": true,
"data": [
{
"id": 4421,
"fiscal_year_id": 15,
"entry_number": "2026-00321",
"entry_date": "2026-04-10",
"description": "Toner HP — Bürobedarf",
"reference": null,
"source": "bank_import",
"source_id": 9877,
"status": "posted",
"payment_method": "bank_transfer",
"template_id": null,
"voided_at": null,
"void_reason": null,
"created_at": "2026-04-10T08:44:12.000Z",
"updated_at": "2026-04-10T08:44:12.000Z"
}
],
"pagination": { "page": 1, "limit": 25, "total": 1, "total_pages": 1, "has_next": false, "has_prev": false }
}/v1/gmbh/journal/{id}
business_type =gmbhEinzelner Journal-Eintrag. Interne User-Referenzen (created_by, voided_by) werden nicht exponiert.
Referenzdaten (für Writes)
Diese Reads liefern die IDs, die Write-Endpoints benötigen (Konten, MWST-Sätze, offene Geschäftsjahre):
| Endpoint | Typ | Beschreibung |
|---|---|---|
| GET /v1/gmbh/accounts | read | Kontenplan (paginiert). Filter: is_active, is_postable, account_type, search. Nur is_postable-Konten sind bebuchbar. |
| GET /v1/gmbh/vat-rates | read | MWST-Codes (Systemtabelle). Filter: active_on=YYYY-MM-DD. |
| GET /v1/gmbh/fiscal-years | read | Geschäftsjahre inkl. eingebetteter aktiver Periodensperren (locked_periods). |
Kontakte & Anlagen
| Endpoint | Typ | Beschreibung |
|---|---|---|
| GET /v1/gmbh/debtors · GET /v1/gmbh/creditors | read | Paginierte Kontaktlisten. Filter: is_active, search. |
| GET /v1/gmbh/assets | read | Anlagenliste. Filter: status, category. |
| GET /v1/gmbh/assets/{id}/depreciation-schedule | read | Abschreibungs-Vorschau (Jahresplan, linear/degressiv) — read-only. |
Die zugehörigen POSTs sind unter Schreibzugriff dokumentiert.
Standard (Einzelunternehmen) Endpoints
Für Accounts ohne GmbH-Business-Type. Kein expliziter business_type-Check — jeder gültige Key darf zugreifen, die Daten sind aber user-scoped (kein Cross-Account- Leak).
/v1/standard/invoices
Paginierte Liste von Rechnungen im Einzelunternehmen-Format.
Query-Parameter
| Parameter | Typ | Default | Beschreibung |
|---|---|---|---|
| page | integer | 1 | Siehe Pagination. |
| limit | integer (1–100) | 25 | Siehe Pagination. |
| status | string (≤30) | — | Rechnungsstatus (z. B. draft, sent, paid, overdue). |
| from | date (YYYY-MM-DD) | — | Filter: invoice_date ≥ from |
| to | date (YYYY-MM-DD) | — | Filter: invoice_date ≤ to |
{
"success": true,
"data": [
{
"id": 88,
"invoice_number": "2026-042",
"invoice_date": "2026-04-10",
"due_date": "2026-05-10",
"customer": {
"name": "Muster AG",
"email": "[email protected]",
"address": "Bahnhofstrasse 1",
"zip": "8001",
"city": "Zürich",
"country": "CH",
"vat_number": "CHE-123.456.789 MWST",
"registration_code": null
},
"amount": 950.00,
"net_amount": 881.59,
"vat_amount": 68.41,
"gross_amount": 950.00,
"currency": "CHF",
"vat_enabled": true,
"status": "paid",
"payment_method": "bank_transfer",
"reference": null,
"notes": null,
"items": [ /* ... */ ],
"booking_id": 512,
"created_at": "2026-04-10T10:02:11.000Z",
"updated_at": "2026-04-12T08:30:00.000Z",
"uploaded_at": null
}
],
"pagination": { "page": 1, "limit": 25, "total": 1, "total_pages": 1, "has_next": false, "has_prev": false }
}/v1/standard/invoices/{id}
Einzelne Rechnung. PDF-Binärdaten werden aus Bandbreiten- und Sicherheitsgründen nie in der Response geliefert.
/v1/standard/belege
Paginierte Liste von Belegen. OCR-Rohtext und S3-Keys werden nicht exponiert.
Query-Parameter
| Parameter | Typ | Default | Beschreibung |
|---|---|---|---|
| page | integer | 1 | Siehe Pagination. |
| limit | integer (1–100) | 25 | Siehe Pagination. |
| from | date (YYYY-MM-DD) | — | Filter: beleg_datum ≥ from |
| to | date (YYYY-MM-DD) | — | Filter: beleg_datum ≤ to |
{
"success": true,
"data": [
{
"id": 201,
"beleg_datum": "2026-04-08",
"beschreibung": "SBB Billet Zürich – Bern",
"vendor_name": "SBB",
"betrag": 51.00,
"waehrung": "CHF",
"vat_amount": 3.98,
"vat_rate": 8.1,
"mime_type": "image/jpeg",
"file_size_bytes": 284112,
"original_filename": "sbb-billet.jpg",
"booking_id": 488,
"processing_status": "done",
"created_at": "2026-04-08T19:02:00.000Z",
"updated_at": "2026-04-08T19:02:30.000Z"
}
],
"pagination": { "page": 1, "limit": 25, "total": 1, "total_pages": 1, "has_next": false, "has_prev": false }
}/v1/standard/belege/{id}
/v1/standard/bookings
Paginierte Liste von Buchungen (einfache Kassenbuch-Einträge). Für GmbHs existiert stattdessen das Journal. Interne Matching-Felder (Bank-Fingerprint, Session-ID, presigned Receipt-URL) werden nicht exponiert.
Query-Parameter
| Parameter | Typ | Default | Beschreibung |
|---|---|---|---|
| page | integer | 1 | Siehe Pagination. |
| limit | integer (1–100) | 25 | Siehe Pagination. |
| type | string (≤30) | — | Buchungstyp (z. B. income, expense). |
| status | string (≤30) | — | Status (z. B. confirmed, draft). |
| category | string (≤50) | — | Kategorie (z. B. bürokosten, honorar). |
| from | date (YYYY-MM-DD) | — | Filter: date ≥ from |
| to | date (YYYY-MM-DD) | — | Filter: date ≤ to |
{
"success": true,
"data": [
{
"id": 488,
"date": "2026-04-08",
"amount": 51.00,
"net_amount": 47.02,
"vat_amount": 3.98,
"vat_rate": 8.1,
"vat_category": "normal",
"currency": "CHF",
"method": "card",
"type": "expense",
"status": "confirmed",
"category": "reisekosten",
"remark": "Kundentermin Bern",
"invoice_id": null,
"beleg_nummer": "B-201",
"source": "manual",
"created_at": "2026-04-08T19:02:00.000Z",
"updated_at": "2026-04-08T19:02:00.000Z"
}
],
"pagination": { "page": 1, "limit": 25, "total": 1, "total_pages": 1, "has_next": false, "has_prev": false }
}/v1/standard/bookings/{id}
Einzelne Buchung inklusive MWST-Felder und Kategorie.
Kategorien & Kunden
| Endpoint | Typ | Beschreibung |
|---|---|---|
| GET /v1/standard/categories | read | Buchungskategorien (statisch). Filter: type (Einnahme | Ausgabe | Saldovortrag). Liefert die gültigen category-Werte für POST /v1/standard/bookings. |
| GET /v1/standard/customers | read | Paginierte Kundenliste. Filter: search (Name/E-Mail/Ort). |
Die zugehörigen POSTs sind unter Schreibzugriff dokumentiert.
Versionierung & Stabilität
Die API-Version ist im Pfad enthalten (/v1/). Breaking Changes erhalten eine neue Major-Version. Wir folgen dieser Policy:
- Additiv = kein Breaking Change: Neue Felder in Responses, neue optionale Query-Parameter, neue Endpoints, neue Enum-Werte.
- Breaking = neue Major-Version: Entfernen/ Umbenennen von Feldern, Pflicht-Parameter ändern, Typ-Wechsel, neue Auth-Anforderungen.
- Deprecation: Deprecated Endpoints liefern zusätzlich einen
Deprecation- undSunset-Header (RFC 9745/8594). Mindestens 6 Monate Vorlauf. - Public Preview: Während
v1als Public Preview läuft, behalten wir uns geringfügige Änderungen an Response-Shapes vor. Breaking Changes werden im Changelog dokumentiert.
Sicherheit & Best Practices
- Key wie Passwort behandeln. Nicht in öffentlichen Repos, Client-seitigem Code, Chat-Nachrichten oder Logs exponieren. Bei Push-to-Git mit versehentlichem Key gilt der Key als kompromittiert — sofort widerrufen.
- Pro Integration ein Key. Kompromittierung eines Keys betrifft nicht Ihre anderen Integrationen und erlaubt gezieltes Widerrufen.
- Sofortiger Widerruf. Verdachtsfall → Key in den Einstellungen widerrufen. Tritt sofort in Kraft (keine Cache-Verzögerung).
- Nur HTTPS. Plain-HTTP-Requests werden abgewiesen. TLS 1.2 Minimum.
- Server-to-Server. Keys gehören in ein Secrets-Management (Vault, 1Password, Doppler, DO App Platform Env-Vars). Niemals im Browser oder Mobile-App.
- Logging. Masken Sie das Bearer-Token in eigenen Logs (nur
pfs_live_***zeigen).
Changelog
- v1.2 — Juli 2026: Write-Endpoints (Entwürfe für GmbH-Journal inkl.
/validate-Dry-Run, Rechnungen/Offerten, Standard- Buchungen/-Rechnungen, Debitoren/Kreditoren/Kunden, Anlagen, Beleg-Upload mit OCR) — abgesichert über Key-Scopes (leere Scopes = read-only),Idempotency-Key-Deduplizierung und strengere Write-Rate-Limits. Neue Referenz-Reads: Kontenplan, MWST-Codes, Geschäftsjahre + Periodensperren, Buchungskategorien, Kunden, Anlagen inkl. Abschreibungs-Vorschau. Neue Status-Codes 409/422. Additiv, kein Breaking Change. - v1.1 — Juni 2026: Neue Endpoints
GET /v1/gmbh/documentsundGET /v1/gmbh/documents/{id}— hochgeladene Dokumente (Belegerfassung mit OCR) inklusive noch nicht verbuchter Belege, mit Filternprocessing_statusundhas_journal_entry. Additiv, kein Breaking Change. - v1.0 — April 2026: Erstveröffentlichung (Public Preview). Lesezugriff auf Rechnungen, Belege und Buchungen für GmbH und Einzelunternehmen. Bearer-Token-Auth, 120 req/min Rate Limit, paginierte JSON-Responses.
Roadmap
- Reports lesen (Bilanz, Erfolgsrechnung, MWST-Report) für Agents
- Bank-Import (CSV/CAMT) über die API
- Webhooks:
invoice.paid,beleg.created,journal.posted, … - Granulare Read-Scopes (
read:invoices, …) — Writes sind bereits feingranular gescoped - OpenAPI 3.1 Spec + interaktiver Playground
- SDK-Wrapper für TypeScript/Node, Python, PHP