VXPay

VXPay Partner-API

Mit der Partner-API verbinden Sie Praxen mit VXPay, legen Rechnungen mit Zahlungslink an, verfolgen Zahlungen und werten Ihre Provisionen aus. REST über HTTPS, JSON, Beträge immer in Cent (Integer), Zeitpunkte im ISO-8601-Format (UTC).

OpenAPI herunterladen

Schnellstart

  1. Partnerkonto anlegen und im Partnerportal unter „Entwickler & API“ einen API-Schlüssel mit den benötigten Berechtigungen erstellen.
  2. Verbindung testen: GET /api/partner/v1/me (funktioniert schon vor der Freischaltung).
  3. Praxis verbinden: POST /api/partner/v1/links mit kind: "connect" bzw. "onboarding" – die Praxis bestätigt über den Link.
  4. Rechnung anlegen und versenden: POST /api/partner/v1/practices/{practice_id}/invoices mit issue: true, send_email: true.
  5. Webhook für invoice.paid einrichten – Ihr System erfährt Zahlungseingänge wenige Sekunden nach dem Eingang.
curl https://vxpay.de/api/partner/v1/me \
  -H "Authorization: Bearer vxpk_…"

Authentifizierung & Berechtigungen

Jede Anfrage trägt Ihren API-Schlüssel als Bearer-Token: Authorization: Bearer vxpk_…. Schlüssel werden nur einmal angezeigt und bei VXPay nur als Hash gespeichert. Jeder Schlüssel hat eigene Berechtigungen (Scopes) – vergeben Sie nur, was die Anwendung braucht:

practices:readVerbundene Praxen lesen
practices:writeRegistrierungs- und Verbindungslinks erzeugen
invoices:readRechnungen und Zahlungsstatus freigegebener Praxen lesen
invoices:writeRechnungen für freigegebene Praxen anlegen und versenden
commissions:readProvisionen, Gutschriften und Statistik lesen
webhooks:writeTest-Ereignisse an Webhooks senden

Basis-URL: https://vxpay.de/api/partner/v1 · Nur HTTPS · Die API-Version steht im Pfad (v1); rückwärtskompatible Erweiterungen (neue Felder, neue Ereignisse) erfolgen ohne Versionswechsel.

Fehler & Rate-Limits

Fehler liefern einen passenden HTTP-Status und ein einheitliches Objekt; request_id hilft dem Support bei der Zuordnung.

{
  "error": {
    "type": "invalid_request_error",
    "code": "unknown_catalog_code",
    "message": "Unbekannte Ziffer 9999 in GOAE – eigene Leistungen bitte als CUSTOM mit unit_price_cents",
    "param": "items.0.code"
  },
  "request_id": "req_1a2b3c4d5e6f7a8b9c0d"
}
Statustype / codeBedeutung
400invalid_request_error
parameter_invalid, invalid_json, invalid_content_type, invalid_cursor, unknown_catalog_code, validation_failed, send_failed, zero_total, no_items, required_fields_missing, invalid_expense_type
Anfrage oder fachliche Prüfung (param nennt das Feld, wo möglich)
401authentication_error
missing_api_key, invalid_api_key, api_key_revoked, api_key_expired
Schlüssel fehlt, ist ungültig, widerrufen oder abgelaufen
403permission_error
missing_scope, practice_access_missing, partner_not_active, partner_inactive, api_disabled
Scope fehlt, Praxis hat die Freigabe nicht erteilt, Konto nicht freigeschaltet oder gesperrt
404not_found_error
resource_missing, unknown_endpoint
Objekt existiert nicht bzw. gehört nicht zu Ihnen; unbekannter Pfad
405–
–
Methode für diesen Pfad nicht unterstützt (ohne JSON-Körper)
409conflict_error
idempotency_key_reused, idempotency_in_progress, number_taken, possible_duplicate, invoice_not_issued, receipt_required, igel_consent_required, invalid_state, conflict, archive_unavailable
Konflikt mit dem aktuellen Zustand oder einer gleichzeitigen Anfrage (receipt_required: Pflichtbeleg fehlt als Anlage, fehlende Belege in error.missing; igel_consent_required: IGeL-Rechnung ohne igel_consent_confirmed)
413invalid_request_error
body_too_large
Anfrage größer als 256 KB
429rate_limit_error
rate_limited, send_limit
120 Anfragen je Minute und Schlüssel (Header Retry-After) bzw. Versandgrenze je Rechnung
500api_error
internal_error
Interner Fehler – später erneut versuchen (mit Idempotency-Key ohne Doppelbuchung)
503api_error
program_disabled
Partnerprogramm vorübergehend pausiert

Listen & Idempotenz

Listen liefern { object: "list", data: [...], has_more }, neueste zuerst. Für die nächste Seite die ID des letzten Objekts als starting_after übergeben; limit 1–100 (Standard 25).

POST-Anfragen akzeptieren den Header Idempotency-Key (bis 255 Zeichen): Wiederholen Sie eine Anfrage nach einem Netzwerkfehler mit demselben Schlüssel, liefert VXPay 24 Stunden lang dieselbe Antwort (Header Idempotent-Replayed: true) – ohne doppelte Rechnung. Fehlgeschlagene Anfragen (4xx/5xx) werden nicht gespeichert.

Freigaben der Praxis

Praxisdaten sind geschützt. Rechnungen einer Praxis lesen oder anlegen Sie nur, wenn die Praxis es beim Verbinden ausdrücklich freigegeben hat (jederzeit widerrufbar):

invoices:readRechnungen lesen – Alle Rechnungen Ihrer Praxis (alle Standorte, auch frühere) mit Empfängerdaten (Name, Anschrift, E-Mail), abgerechneten Leistungen, Beträgen, Zahlungsstatus, Zahlungslinks und PDFs
invoices:writeRechnungen erstellen und versenden – Rechnungen im Namen Ihrer Praxis anlegen, erstellen und per E-Mail mit Zahlungslink an die Empfänger senden

Provisionen, Gutschriften und Statistik enthalten nie Patientendaten. Praxisnamen erscheinen nur mit Zustimmung der Praxis oder bei aktiver Freigabe, sonst als stabiles Kennzeichen (z. B. „Praxis #4F2K9Q“); ohne Zustimmung tragen Provisionen keine Zahlungs-ID und nur den Kalendertag der Zahlung. Zugriff auf Rechnungen geben nur Inhaber und Administratoren der Praxis frei.

Allgemein

GET /api/partner/v1/me

Eigenes Partnerkonto. Status, Konditionen, Empfehlungslink und Berechtigungen des verwendeten API-Schlüssels. Funktioniert schon vor der Freischaltung – ideal als Verbindungstest.

Jeder gültige Schlüssel

Antwort
{
  "object": "partner",
  "id": "P-7K3M9Q",
  "name": "Praxissoft GmbH",
  "status": "active",
  "referral_url": "https://vxpay.de/r/praxissoft-k2m4",
  "commission": {
    "model": "percent_of_platform_fee",
    "rate_bps": 2000,
    "fixed_cents": 0,
    "valid_from": null
  },
  "payouts": {
    "method": "sepa_file",
    "live": true
  },
  "api_key": {
    "id": "cmg3key0001",
    "name": "Produktion",
    "scopes": [
      "practices:read",
      "invoices:write"
    ]
  },
  "redirect_uris": [
    "https://app.praxissoft.example/vxpay/callback"
  ]
}

Praxen & Links

GET /api/partner/v1/practices

Zugeordnete Praxen. Eine Zeile je Praxis, die Ihnen zugeordnet ist oder war oder Ihnen API-Zugriff freigegeben hat – zuletzt verbundene zuerst; Cursor ist die ID der Praxis. Name und Ort nur mit Zustimmung der Praxis bzw. mit Freigabe, sonst ein stabiles Kennzeichen (display_name). payments_active nur, solange die Praxis Ihnen zugeordnet ist oder Zugriff gewährt (sonst null).

Scope practices:read

ParameterTypBeschreibung
limitinteger1–100, Standard 25
starting_afterstringID des letzten Objekts der vorigen Seite (Cursor)
status"active" | "ended"active: laufende Zuordnung oder Freigabe; ended: keins von beiden
Antwort
{
  "object": "list",
  "url": "/api/partner/v1/practices",
  "data": [
    {
      "object": "practice",
      "id": "org_8f2k1m9x",
      "name": "Praxis Dr. Muster",
      "display_name": "Praxis Dr. Muster",
      "city": "Berlin",
      "attribution": {
        "status": "active",
        "source": "connect_link",
        "connected_at": "2026-10-01T09:12:00.000Z",
        "ended_at": null,
        "external_reference": "kunde-4711",
        "data_sharing": true
      },
      "access": {
        "scopes": [
          "invoices:read",
          "invoices:write"
        ],
        "granted_at": "2026-10-01T09:12:00.000Z"
      },
      "payments_active": true
    }
  ],
  "has_more": false
}

GET /api/partner/v1/practices/{practice_id}

Einzelne Praxis. Zuordnung, Freigaben und ob VXPay Payments für die Praxis aktiv ist (payments_active).

Scope practices:read

Antwort
{
  "object": "practice",
  "id": "org_8f2k1m9x",
  "name": "Praxis Dr. Muster",
  "display_name": "Praxis Dr. Muster",
  "city": "Berlin",
  "attribution": {
    "status": "active",
    "source": "connect_link",
    "connected_at": "2026-10-01T09:12:00.000Z",
    "ended_at": null,
    "external_reference": "kunde-4711",
    "data_sharing": true
  },
  "access": {
    "scopes": [
      "invoices:read",
      "invoices:write"
    ],
    "granted_at": "2026-10-01T09:12:00.000Z"
  },
  "payments_active": true
}

Rechnungen

POST /api/partner/v1/practices/{practice_id}/invoices

Rechnung anlegen (und optional erstellen und versenden). Legt einen Entwurf an; mit issue: true wird die Rechnung erstellt (Nummer, PDF mit E-Rechnung, Zahlungslink) und mit send_email: true per E-Mail mit Zahlungslink versendet. Katalogpositionen (GOAE, GOZ, GOT, GEBUEH, HEILMITTEL) müssen Ziffern der aktuellen Gebührenordnung sein und werden wie im VXPay-Rechnungsassistenten geprüft und bepreist (Faktor, Begründung, § 2-Vereinbarung – agreement_date wird mit Ihrem Zugang im Verlauf der Rechnung festgehalten). Eigene Leistungen: kind CUSTOM mit unit_price_cents (netto). Scheitert das Erstellen (z. B. Rechnungsnummer vergeben oder 409 possible_duplicate – derselbe Patient hat am selben Behandlungstag schon eine Rechnung), bleibt kein Entwurf zurück. send_error ist gesetzt, wenn die Rechnung erstellt, die E-Mail aber nicht versendet wurde. Leistungen an mehreren Tagen: Datum je Position (items[].service_date, sonst gilt service_date der Rechnung); die Rechnung führt als Leistungsdatum das früheste Positionsdatum, Zuschläge tragen das Datum ihrer Bezugsleistung. Zahnarztrechnungen: Zahn bzw. Region je Position (region, § 10 Abs. 2 Nr. 2 GOZ) und Auslagen getrennt nach Art (expense_type an eigenen Leistungen). Belege: Die Rechnung des Dentallabors (LAB_EXTERNAL), der Praxislaborbeleg (LAB_OWN) und auf Rechnungen nach GOÄ der Beleg einer Auslage über 25,56 € (MATERIAL) müssen der Rechnung beiliegen (§ 10 Abs. 3 Satz 5 und 6 GOZ, § 12 Abs. 2 Nr. 5 GOÄ). Die Praxis hängt sie in VXPay an der Rechnung an – über die API lassen sich keine Anlagen hochladen. Fehlt ein solcher Beleg, wird mit send_email: true erstellt, aber weder per E-Mail noch per Post versendet (send_error: "receipt_required"; die Mahnungen der Rechnung sind bis zum ersten Versand angehalten) – außer mit receipts_external: true (die Praxis übergibt den Beleg selbst, VXPay sendet nur die Rechnung). Auch der automatische Postversand der Praxis sendet ohne einen solchen Beleg keinen Brief. Briefe: Hat die Praxis „Rechnungen immer auch per Post versenden“ eingeschaltet oder ist der Empfänger in VXPay als „Rechnungen immer per Post“ markiert, merkt VXPay nach dem Erstellen automatisch einen Brief vor (kostenpflichtig für die Praxis, Preise in VXPay unter Einstellungen → Postversand). postal_color legt fest, ob dieser Brief farbig (true) oder schwarz-weiß (false) gedruckt wird; ohne Angabe gilt die Einstellung der Praxis beim Versand. Die gewünschte Druckart wird beim Vormerken festgehalten – ist Farbdruck in diesem Moment nicht verfügbar, wird der Brief schwarz-weiß gedruckt, auch wenn Farbpreise später eintreffen. postal_color gehört zum Erstellen: jede Angabe (auch false) ohne issue: true → 400 parameter_invalid. Hinweise in warnings (Zeichenketten „code: Erklärung“): postal_auto_send_off (postal_color angegeben, für diese Rechnung ist kein automatischer Brief vorgesehen – weder Praxis-Einstellung noch Markierung des Empfängers), postal_color_unavailable (farbig gewünscht, Brief wird schwarz-weiß gedruckt), postal_not_scheduled: receipt_required | postal_not_ready | error (die Rechnung ginge automatisch per Post hinaus – Praxis-Einstellung oder Empfänger „immer per Post“ –, es wurde aber kein Brief vorgemerkt – Pflichtbeleg fehlt, Postversand der Praxis derzeit nicht möglich bzw. technischer Fehler; dann keine Hinweise zur Druckart), already_issued: … (Rechnung war bereits erstellt, auch durch eine gleichzeitige Anfrage – external_number, send_email, receipts_external bzw. postal_color nicht angewandt). Einen Brief auslösen kann die API nicht. Tierarztrechnungen: animal_species und consultation_reason sind zum Erstellen Pflicht (§ 7 Abs. 4 GOT) – fehlen sie, antwortet issue: true mit 400 required_fields_missing, noch vor der Dublettenprüfung (der Entwurf wird wieder entfernt). IGeL: Eine Rechnung mit igel: true wird nur mit igel_consent_confirmed: true erstellt – damit bestätigt die Praxis, dass die schriftliche Zustimmung zur IGeL vor der Behandlung vorlag (steht im Verlauf der Rechnung; ob sie vorliegt, prüft VXPay nicht). Ohne Bestätigung antwortet die API mit 409 igel_consent_required, noch vor der Dublettenprüfung. Ohne Freigabe „Rechnungen lesen“ enthält die Antwort keine Empfängerdaten, Positionen und Anlagen (recipient, patient_name, patient_birth_date, animal_*, consultation_reason, igel, items, attachments = null). Benötigt die Freigabe „Rechnungen erstellen“ der Praxis.

Scope invoices:write · Freigabe der Praxis invoices:write · Idempotency-Key unterstützt

FeldTypBeschreibung
recipient *objectsalutation, first_name*, last_name*, street, postal_code, city, country (ISO, Standard DE), email
service_date *JJJJ-MM-TTLeistungsdatum – gilt für alle Positionen ohne eigenes service_date
items *object[]kind*, code*, title, description, quantity (Standard 1), factor, unit_price_cents, vat_rate_bps, justification, analog, agreement_date (Datum der Vereinbarung nach § 2, nicht nach dem Leistungsdatum der Position), base_position, service_date (Leistungsdatum der Position, JJJJ-MM-TT, optional – Standard: service_date der Rechnung; Zuschläge und Teilleistungen erhalten immer das Datum ihrer Bezugsleistung), region (Zahn/Region, z. B. "16", "11-13", "OK"; max. 150 Zeichen nach Vereinheitlichung – nach jedem Komma steht ein Leerzeichen; längere Angaben: 400 parameter_invalid, nie gekürzt), on_request (Leistung auf Verlangen, Standard false), expense_type (nur CUSTOM: "" = Leistung, MATERIAL und TRAVEL – alle Berufsstände; zusätzlich LAB_OWN, LAB_EXTERNAL – Zahnarzt; MEDICATION – Tierarzt; nicht zusammen mit on_request – Auslagen sind keine Leistungen auf Verlangen, on_request wird dann ignoriert)
patient_namestringBehandelte Person, wenn sie vom Rechnungsempfänger abweicht (z. B. Kind); leer = Rechnungsempfänger (max. 200 Zeichen)
patient_birth_dateJJJJ-MM-TTGeburtsdatum der behandelten Person (optional; nicht in der Zukunft, nicht vor 1900). Ohne patient_name nur auf Zahnarztrechnungen (Geburtsdatum des Empfängers als behandelter Person), sonst wird es nicht gespeichert
animal_speciesstringTierarzt: Tierart, für die die Leistung erbracht wurde (z. B. "Hund") – Pflicht zum Erstellen (§ 7 Abs. 4 GOT). Tierangaben und consultation_reason werden nur auf Tierarztrechnungen gespeichert
animal_namestringTierarzt: Name des Tieres (optional)
consultation_reasonstringTierarzt: Diagnose oder Grund der Konsultation – Pflicht zum Erstellen (§ 7 Abs. 4 GOT), max. 500 Zeichen
professionstringBerufsstand der Rechnung (z. B. ARZT) – muss ein Berufsstand der Praxis sein; Standard: erster Berufsstand der Praxis
payment_term_daysintegerZahlungsziel 0–120 Tage (Standard der Praxis)
invoice_textstringHinweistext unter den Positionen
external_numberstringRechnungsnummer aus Ihrem System (statt Nummernkreis der Praxis, muss eindeutig sein) – nur zusammen mit issue: true
issuebooleanDirekt erstellen (Standard false = Entwurf)
send_emailbooleanNach dem Erstellen per E-Mail mit Zahlungslink versenden (recipient.email nötig)
allow_duplicatebooleanNur mit issue: Gibt es für denselben Patienten am selben Behandlungstag schon eine Rechnung (Entwurf, offen, bezahlt), antwortet die API mit 409 possible_duplicate und legt nichts an. true bestätigt, dass es eine andere Leistung ist (steht im Verlauf der Rechnung).
igelbooleanIGeL-Kennzeichen (Standard false): gesetzlich versichert, zahlt selbst – nur auf Rechnungen der Berufsstände ARZT und PSYCHOTHERAPEUT (sonst 400 parameter_invalid; maßgeblich ist profession, ohne Angabe der erste Berufsstand der Praxis). Erstellt wird die Rechnung nur mit igel_consent_confirmed: true
igel_consent_confirmedbooleanNur mit issue (Standard false): Die Praxis bestätigt, dass die schriftliche Zustimmung zur IGeL vor der Behandlung vorlag – Pflicht für igel: true, sonst 409 igel_consent_required. Steht im Verlauf der Rechnung; VXPay prüft die Zustimmung nicht. Ohne igel ohne Wirkung
receipts_externalbooleanNur mit issue (Standard false): Die Praxis übergibt Pflichtbelege (Rechnung des Dentallabors, Praxislaborbeleg, Beleg einer Auslage über 25,56 €) selbst – VXPay sendet nur die Rechnung und hält den Versand nicht zurück. Wird nur vermerkt, wenn beim Erstellen ein solcher Beleg fehlt (Feld receipts_external der Rechnung, Eintrag im Verlauf); nachträglich nicht änderbar.
postal_colorbooleanNur mit issue (optional; jede Angabe ohne issue: true, auch false → 400 parameter_invalid): Druckart des Briefs, den der automatische Postversand (Praxis-Einstellung bzw. Empfänger „immer per Post“) nach dem Erstellen sendet – true = farbig, false = schwarz-weiß; ohne Angabe die Einstellung der Praxis. Festgehalten beim Vormerken des Briefs. Ohne automatischen Brief für diese Rechnung ohne Wirkung (warnings: postal_auto_send_off); ist Farbdruck beim Vormerken nicht verfügbar, wird schwarz-weiß gedruckt (warnings: postal_color_unavailable); ohne vorgemerkten Brief warnings: postal_not_scheduled
Anfrage
{
  "recipient": {
    "salutation": "Frau",
    "first_name": "Erika",
    "last_name": "Mustermann",
    "street": "Musterweg 1",
    "postal_code": "10115",
    "city": "Berlin",
    "email": "erika@example.com"
  },
  "service_date": "2026-10-02",
  "items": [
    {
      "kind": "GOAE",
      "code": "1",
      "factor": 2.3
    },
    {
      "kind": "CUSTOM",
      "code": "ATT",
      "title": "Attest",
      "unit_price_cents": 1500
    },
    {
      "kind": "GOAE",
      "code": "1",
      "factor": 2.3,
      "service_date": "2026-10-09"
    }
  ],
  "issue": true,
  "send_email": true
}
Antwort
{
  "object": "invoice",
  "id": "cmg3inv0001",
  "practice_id": "org_8f2k1m9x",
  "number": "RE-2026-0142",
  "type": "invoice",
  "status": "open",
  "display_status": "sent",
  "cancelled": false,
  "currency": "EUR",
  "net_cents": 3644,
  "vat_cents": 0,
  "total_cents": 3644,
  "amount_due_cents": 3644,
  "service_date": "2026-10-02",
  "issue_date": "2026-10-02",
  "due_date": "2026-10-16",
  "paid_at": null,
  "payment_url": "https://vxpay.de/pay/9QkZ…",
  "delivery_status": "sent",
  "receipts_external": false,
  "created_at": "2026-10-02T10:00:00.000Z",
  "updated_at": "2026-10-02T10:00:01.000Z",
  "recipient": {
    "salutation": "Frau",
    "first_name": "Erika",
    "last_name": "Mustermann",
    "street": "Musterweg 1",
    "postal_code": "10115",
    "city": "Berlin",
    "country": "DE",
    "email": "erika@example.com"
  },
  "patient_name": null,
  "patient_birth_date": null,
  "animal_species": null,
  "animal_name": null,
  "consultation_reason": null,
  "igel": false,
  "items": [
    {
      "position": 1,
      "kind": "GOAE",
      "code": "1",
      "title": "Beratung, auch mittels Fernsprecher",
      "description": null,
      "service_date": "2026-10-02",
      "quantity": 1,
      "factor": 2.3,
      "unit_price_cents": 1072,
      "total_cents": 1072,
      "vat_rate_bps": 0,
      "region": null,
      "on_request": false,
      "expense_type": null
    },
    {
      "position": 2,
      "kind": "CUSTOM",
      "code": "ATT",
      "title": "Attest",
      "description": null,
      "service_date": "2026-10-02",
      "quantity": 1,
      "factor": null,
      "unit_price_cents": 1500,
      "total_cents": 1500,
      "vat_rate_bps": 0,
      "region": null,
      "on_request": false,
      "expense_type": null
    },
    {
      "position": 3,
      "kind": "GOAE",
      "code": "1",
      "title": "Beratung, auch mittels Fernsprecher",
      "description": null,
      "service_date": "2026-10-09",
      "quantity": 1,
      "factor": 2.3,
      "unit_price_cents": 1072,
      "total_cents": 1072,
      "vat_rate_bps": 0,
      "region": null,
      "on_request": false,
      "expense_type": null
    }
  ],
  "attachments": [],
  "send_error": null,
  "warnings": []
}

GET /api/partner/v1/practices/{practice_id}/invoices

Rechnungen einer Praxis. Neueste zuerst, inklusive Stornorechnungen und Gutschriften (type). Mit updated_since lassen sich Änderungen seit dem letzten Abgleich abrufen (z. B. Zahlungseingänge) – als Merkpunkt eignet sich das größte updated_at der Antwort. status=cancelled liefert alle stornierten Rechnungen (auch bezahlte, die mit status paid und cancelled true geführt werden). payment_url nur, wenn die Praxis Onlinezahlung eingerichtet hat. Benötigt die Freigabe „Rechnungen lesen“.

Scope invoices:read · Freigabe der Praxis invoices:read

ParameterTypBeschreibung
limitinteger1–100, Standard 25
starting_afterstringID des letzten Objekts der vorigen Seite (Cursor)
status"draft" | "open" | "paid" | "cancelled"Filter
updated_sinceISO-ZeitpunktNur seit diesem Zeitpunkt geänderte Rechnungen
Antwort
{
  "object": "list",
  "url": "/api/partner/v1/practices/org_8f2k1m9x/invoices",
  "data": [
    {
      "object": "invoice",
      "id": "cmg3inv0001",
      "practice_id": "org_8f2k1m9x",
      "number": "RE-2026-0142",
      "type": "invoice",
      "status": "open",
      "display_status": "sent",
      "cancelled": false,
      "currency": "EUR",
      "net_cents": 8684,
      "vat_cents": 0,
      "total_cents": 8684,
      "amount_due_cents": 8684,
      "service_date": "2026-10-02",
      "issue_date": "2026-10-02",
      "due_date": "2026-10-16",
      "paid_at": null,
      "payment_url": "https://vxpay.de/pay/9QkZ…",
      "delivery_status": "sent",
      "receipts_external": false,
      "created_at": "2026-10-02T10:00:00.000Z",
      "updated_at": "2026-10-02T10:00:01.000Z",
      "recipient": {
        "salutation": "Frau",
        "first_name": "Erika",
        "last_name": "Mustermann",
        "street": "Musterweg 1",
        "postal_code": "10115",
        "city": "Berlin",
        "country": "DE",
        "email": "erika@example.com"
      },
      "patient_name": null,
      "patient_birth_date": null,
      "animal_species": null,
      "animal_name": null,
      "consultation_reason": null,
      "igel": false,
      "items": [
        {
          "position": 1,
          "kind": "GOAE",
          "code": "1",
          "title": "Beratung, auch mittels Fernsprecher",
          "description": null,
          "service_date": "2026-10-02",
          "quantity": 1,
          "factor": 2.3,
          "unit_price_cents": 1072,
          "total_cents": 1072,
          "vat_rate_bps": 0,
          "region": null,
          "on_request": false,
          "expense_type": null
        }
      ],
      "attachments": []
    }
  ],
  "has_more": false
}

GET /api/partner/v1/practices/{practice_id}/invoices/{invoice_id}

Einzelne Rechnung. Status, offener Betrag und Zahlungslink (payment_url) einer Rechnung. attachments nennt die Belege, die die Praxis in VXPay als Anlage angehängt hat (id, kind: LAB_INVOICE | LAB_OWN_RECEIPT | EXPENSE_RECEIPT | TRAVEL_PROOF | CONFORMITY, number, pages, withdrawn) – nur Angaben, keine Dateien; zurückgezogene Anlagen (withdrawn: true) werden nicht mehr versendet. receipts_external: beim Erstellen angegeben, dass die Praxis Pflichtbelege selbst übergibt. igel: IGeL-Kennzeichen der Rechnung (gesetzlich versichert, zahlt selbst; Stornorechnung und Gutschrift übernehmen es).

Scope invoices:read · Freigabe der Praxis invoices:read

Antwort
{
  "object": "invoice",
  "id": "cmg3inv0001",
  "practice_id": "org_8f2k1m9x",
  "number": "RE-2026-0142",
  "type": "invoice",
  "status": "open",
  "display_status": "sent",
  "cancelled": false,
  "currency": "EUR",
  "net_cents": 8684,
  "vat_cents": 0,
  "total_cents": 8684,
  "amount_due_cents": 8684,
  "service_date": "2026-10-02",
  "issue_date": "2026-10-02",
  "due_date": "2026-10-16",
  "paid_at": null,
  "payment_url": "https://vxpay.de/pay/9QkZ…",
  "delivery_status": "sent",
  "receipts_external": false,
  "created_at": "2026-10-02T10:00:00.000Z",
  "updated_at": "2026-10-02T10:00:01.000Z",
  "recipient": {
    "salutation": "Frau",
    "first_name": "Erika",
    "last_name": "Mustermann",
    "street": "Musterweg 1",
    "postal_code": "10115",
    "city": "Berlin",
    "country": "DE",
    "email": "erika@example.com"
  },
  "patient_name": null,
  "patient_birth_date": null,
  "animal_species": null,
  "animal_name": null,
  "consultation_reason": null,
  "igel": false,
  "items": [
    {
      "position": 1,
      "kind": "GOAE",
      "code": "1",
      "title": "Beratung, auch mittels Fernsprecher",
      "description": null,
      "service_date": "2026-10-02",
      "quantity": 1,
      "factor": 2.3,
      "unit_price_cents": 1072,
      "total_cents": 1072,
      "vat_rate_bps": 0,
      "region": null,
      "on_request": false,
      "expense_type": null
    }
  ],
  "attachments": []
}

POST /api/partner/v1/practices/{practice_id}/invoices/{invoice_id}/issue

Entwurf erstellen. Vergibt die Rechnungsnummer (oder übernimmt external_number), erstellt PDF und Zahlungslink. Wiederholte Aufrufe liefern die bereits erstellte Rechnung unverändert – nicht angewandte Optionen nennt warnings (already_issued: …; ebenso, wenn eine gleichzeitige Anfrage die Rechnung erstellt hat). Optional send_email: true (recipient.email nötig) und postal_color (Druckart des automatischen Briefs, siehe „Rechnung anlegen“; Hinweise in warnings: postal_auto_send_off, postal_color_unavailable, postal_not_scheduled). Tierarztrechnungen ohne Tierart bzw. Diagnose/Grund der Konsultation (§ 7 Abs. 4 GOT) werden nicht erstellt (400 required_fields_missing) – die Angaben bitte beim Anlegen mitgeben oder im Entwurf in VXPay ergänzen. Fehlt ein Pflichtbeleg als Anlage (siehe „Rechnung anlegen“), wird mit send_email: true erstellt, aber nicht versendet (send_error: "receipt_required", Mahnungen bis zum ersten Versand angehalten) – außer mit receipts_external: true. Positionen über dem Höchstsatz ohne bestätigte Vereinbarung nach § 2 (z. B. ein in VXPay nur für die Kosteninformation gespeicherter Entwurf) werden nicht erstellt (400 validation_failed) – die Vereinbarung bestätigt die Praxis im Entwurf in VXPay. IGeL: Eine Rechnung mit igel: true wird nur mit igel_consent_confirmed: true erstellt – damit bestätigt die Praxis, dass die schriftliche Zustimmung zur IGeL vor der Behandlung vorlag (steht im Verlauf der Rechnung; ob sie vorliegt, prüft VXPay nicht). Ohne Bestätigung antwortet die API mit 409 igel_consent_required, noch vor der Dublettenprüfung. Der Entwurf bleibt dann unverändert bestehen. Ohne Freigabe „Rechnungen lesen“ enthält die Antwort keine Empfängerdaten, Positionen und Anlagen (recipient, patient_name, patient_birth_date, animal_*, consultation_reason, igel, items, attachments = null).

Scope invoices:write · Freigabe der Praxis invoices:write · Idempotency-Key unterstützt

FeldTypBeschreibung
send_emailbooleanNach dem Erstellen per E-Mail versenden
allow_duplicatebooleanMögliche doppelte Abrechnung (gleicher Patient, gleicher Behandlungstag) ausdrücklich bestätigen – sonst 409 possible_duplicate
external_numberstringRechnungsnummer aus Ihrem System
receipts_externalbooleanDie Praxis übergibt Pflichtbelege selbst (Standard false) – VXPay sendet nur die Rechnung. Wird nur vermerkt, wenn beim Erstellen ein solcher Beleg fehlt; bei einer bereits erstellten Rechnung ohne Wirkung (warnings).
igel_consent_confirmedbooleanPflicht, wenn der Entwurf das IGeL-Kennzeichen trägt (Standard false): Die Praxis bestätigt, dass die schriftliche Zustimmung zur IGeL vor der Behandlung vorlag – sonst 409 igel_consent_required. VXPay prüft die Zustimmung nicht
postal_colorbooleanOptional: Druckart des automatischen Briefs (true = farbig, false = schwarz-weiß; ohne Angabe Einstellung der Praxis) – siehe „Rechnung anlegen“. Bei einer bereits erstellten Rechnung ohne Wirkung (warnings)
Anfrage
{
  "send_email": true
}
Antwort
{
  "object": "invoice",
  "id": "cmg3inv0001",
  "practice_id": "org_8f2k1m9x",
  "number": "RE-2026-0142",
  "type": "invoice",
  "status": "open",
  "display_status": "sent",
  "cancelled": false,
  "currency": "EUR",
  "net_cents": 8684,
  "vat_cents": 0,
  "total_cents": 8684,
  "amount_due_cents": 8684,
  "service_date": "2026-10-02",
  "issue_date": "2026-10-02",
  "due_date": "2026-10-16",
  "paid_at": null,
  "payment_url": "https://vxpay.de/pay/9QkZ…",
  "delivery_status": "sent",
  "receipts_external": false,
  "created_at": "2026-10-02T10:00:00.000Z",
  "updated_at": "2026-10-02T10:00:01.000Z",
  "recipient": {
    "salutation": "Frau",
    "first_name": "Erika",
    "last_name": "Mustermann",
    "street": "Musterweg 1",
    "postal_code": "10115",
    "city": "Berlin",
    "country": "DE",
    "email": "erika@example.com"
  },
  "patient_name": null,
  "patient_birth_date": null,
  "animal_species": null,
  "animal_name": null,
  "consultation_reason": null,
  "igel": false,
  "items": [
    {
      "position": 1,
      "kind": "GOAE",
      "code": "1",
      "title": "Beratung, auch mittels Fernsprecher",
      "description": null,
      "service_date": "2026-10-02",
      "quantity": 1,
      "factor": 2.3,
      "unit_price_cents": 1072,
      "total_cents": 1072,
      "vat_rate_bps": 0,
      "region": null,
      "on_request": false,
      "expense_type": null
    }
  ],
  "attachments": [],
  "send_error": null,
  "warnings": []
}

POST /api/partner/v1/practices/{practice_id}/invoices/{invoice_id}/send

Rechnung per E-Mail versenden. Sendet eine erstellte Rechnung an recipient.email – mit Zahlungslink und Links zum Herunterladen des PDFs und der angehängten Belege (attachments, ohne zurückgezogene); standardmäßig ohne Dateianhänge, mit der Praxis-Einstellung „Link + PDF-Anhang“ zusätzlich die Rechnung und – bis zur Größengrenze von 10 MB je E-Mail – die Belege als PDF. Höchstens 3 Sendungen derselben Rechnung je 24 Stunden über die API (danach 429 send_limit). Fehlt ein Pflichtbeleg als Anlage (Rechnung des Dentallabors, Praxislaborbeleg, Beleg einer Auslage über 25,56 €) und hat die Praxis beim Erstellen nicht angegeben, ihn selbst zu übergeben (receipts_external), antwortet die API mit 409 receipt_required und nennt die fehlenden Belege in error.missing (kind, law) – nichts wird versendet. Die Praxis hängt den Beleg in VXPay an; danach erneut senden. Ohne Freigabe „Rechnungen lesen“ enthält die Antwort keine Empfängerdaten, Positionen und Anlagen (recipient, patient_name, patient_birth_date, animal_*, consultation_reason, igel, items, attachments = null).

Scope invoices:write · Freigabe der Praxis invoices:write · Idempotency-Key unterstützt

Anfrage
{}
Antwort
{
  "object": "invoice",
  "id": "cmg3inv0001",
  "practice_id": "org_8f2k1m9x",
  "number": "RE-2026-0142",
  "type": "invoice",
  "status": "open",
  "display_status": "sent",
  "cancelled": false,
  "currency": "EUR",
  "net_cents": 8684,
  "vat_cents": 0,
  "total_cents": 8684,
  "amount_due_cents": 8684,
  "service_date": "2026-10-02",
  "issue_date": "2026-10-02",
  "due_date": "2026-10-16",
  "paid_at": null,
  "payment_url": "https://vxpay.de/pay/9QkZ…",
  "delivery_status": "sent",
  "receipts_external": false,
  "created_at": "2026-10-02T10:00:00.000Z",
  "updated_at": "2026-10-02T10:00:01.000Z",
  "recipient": {
    "salutation": "Frau",
    "first_name": "Erika",
    "last_name": "Mustermann",
    "street": "Musterweg 1",
    "postal_code": "10115",
    "city": "Berlin",
    "country": "DE",
    "email": "erika@example.com"
  },
  "patient_name": null,
  "patient_birth_date": null,
  "animal_species": null,
  "animal_name": null,
  "consultation_reason": null,
  "igel": false,
  "items": [
    {
      "position": 1,
      "kind": "GOAE",
      "code": "1",
      "title": "Beratung, auch mittels Fernsprecher",
      "description": null,
      "service_date": "2026-10-02",
      "quantity": 1,
      "factor": 2.3,
      "unit_price_cents": 1072,
      "total_cents": 1072,
      "vat_rate_bps": 0,
      "region": null,
      "on_request": false,
      "expense_type": null
    }
  ],
  "attachments": []
}
Fehler 409 · receipt_required – Pflichtbeleg fehlt als Anlage (error.missing), bzw. invoice_not_issued
{
  "error": {
    "type": "conflict_error",
    "code": "receipt_required",
    "message": "Beleg fehlt: Rechnung des Dentallabors (§ 10 Abs. 3 Satz 6 GOZ) – bitte den Beleg in VXPay an der Rechnung anhängen und danach erneut senden",
    "missing": [
      {
        "kind": "LAB_INVOICE",
        "law": "§ 10 Abs. 3 Satz 6 GOZ"
      }
    ]
  },
  "request_id": "req_1a2b3c4d5e6f7a8b9c0d"
}

GET /api/partner/v1/practices/{practice_id}/invoices/{invoice_id}/pdf

Rechnungs-PDF. PDF (application/pdf) der erstellten Rechnung inkl. eingebetteter E-Rechnung (ZUGFeRD).

Scope invoices:read · Freigabe der Praxis invoices:read

Antwort
(PDF)

Provisionen & Gutschriften

GET /api/partner/v1/commissions

Provisionen. Eine Provision je Zahlung über VXPay Payments (mit Plattformgebühr) einer zugeordneten Praxis: Zahlungsbetrag, VXPay-Gebühr netto, Anteil, Provision und Status (pending → payable → settled → paid; blocked, reversed). Keine Patientendaten. Ohne Zustimmung bzw. Freigabe der Praxis nur mit Kennzeichen, Kalendertag (paid_date) und ohne payment_id/paid_at; held_reason dann allgemein „held“. Ohne livemode-Filter kommen echte und Test-Provisionen (Feld livemode).

Scope commissions:read

ParameterTypBeschreibung
limitinteger1–100, Standard 25
starting_afterstringID des letzten Objekts der vorigen Seite (Cursor)
status"pending" | "payable" | "settled" | "paid" | "blocked" | "reversed"Status
monthJJJJ-MMAbrechnungsmonat
practice_idstringNur eine Praxis
livemodebooleantrue = nur echte, false = nur Testzahlungen
Antwort
{
  "object": "list",
  "url": "/api/partner/v1/commissions",
  "data": [
    {
      "object": "commission",
      "id": "cmg3com0001",
      "practice_id": "org_8f2k1m9x",
      "practice_display_name": "Praxis Dr. Muster",
      "payment_id": "cmg3pay0001",
      "status": "pending",
      "held_reason": null,
      "livemode": true,
      "currency": "EUR",
      "payment_amount_cents": 10000,
      "platform_fee_net_cents": 179,
      "model": "percent_of_platform_fee",
      "rate_bps": 2000,
      "fixed_cents": 0,
      "commission_cents": 36,
      "reversed_cents": 0,
      "net_commission_cents": 36,
      "paid_at": "2026-10-02T10:05:00.000Z",
      "paid_date": "2026-10-02",
      "accrual_month": "2026-10",
      "available_at": "2026-10-31T23:00:00.000Z",
      "statement_id": null
    }
  ],
  "has_more": false
}

GET /api/partner/v1/commissions/{commission_id}

Einzelne Provision. Snapshot der Berechnung (Satz und Grundlage zum Zeitpunkt der Zahlung).

Scope commissions:read

Antwort
{
  "object": "commission",
  "id": "cmg3com0001",
  "practice_id": "org_8f2k1m9x",
  "practice_display_name": "Praxis Dr. Muster",
  "payment_id": "cmg3pay0001",
  "status": "pending",
  "held_reason": null,
  "livemode": true,
  "currency": "EUR",
  "payment_amount_cents": 10000,
  "platform_fee_net_cents": 179,
  "model": "percent_of_platform_fee",
  "rate_bps": 2000,
  "fixed_cents": 0,
  "commission_cents": 36,
  "reversed_cents": 0,
  "net_commission_cents": 36,
  "paid_at": "2026-10-02T10:05:00.000Z",
  "paid_date": "2026-10-02",
  "accrual_month": "2026-10",
  "available_at": "2026-10-31T23:00:00.000Z",
  "statement_id": null
}

GET /api/partner/v1/statements

Gutschriften und Auszahlungen. Monatliche Gutschriften (Abrechnung im Gutschriftverfahren) mit Auszahlungsstatus.

Scope commissions:read

ParameterTypBeschreibung
limitinteger1–100, Standard 25
starting_afterstringID des letzten Objekts der vorigen Seite (Cursor)
Antwort
{
  "object": "list",
  "url": "/api/partner/v1/statements",
  "data": [
    {
      "object": "statement",
      "id": "cmg3gs0001",
      "number": "GS-2026-00012",
      "livemode": true,
      "month": "2026-10",
      "period_start": "2026-10-01",
      "period_end": "2026-10-31",
      "commission_count": 412,
      "commission_net_cents": 14832,
      "adjustment_net_cents": 0,
      "net_cents": 14832,
      "vat_mode": "standard",
      "vat_rate_bps": 1900,
      "vat_cents": 2818,
      "gross_cents": 17650,
      "currency": "EUR",
      "storno_of": null,
      "cancelled_at": null,
      "payout": {
        "status": "paid",
        "method": "sepa_file",
        "wait_reason": null,
        "paid_at": "2026-11-01T06:00:00.000Z",
        "reversed_cents": 0
      },
      "created_at": "2026-11-01T05:00:00.000Z"
    }
  ],
  "has_more": false
}

GET /api/partner/v1/statements/{statement_id}

Einzelne Gutschrift. Beträge, Steuerbehandlung, Auszahlungsstatus. PDF unter …/pdf.

Scope commissions:read

Antwort
{
  "object": "statement",
  "id": "cmg3gs0001",
  "number": "GS-2026-00012",
  "livemode": true,
  "month": "2026-10",
  "period_start": "2026-10-01",
  "period_end": "2026-10-31",
  "commission_count": 412,
  "commission_net_cents": 14832,
  "adjustment_net_cents": 0,
  "net_cents": 14832,
  "vat_mode": "standard",
  "vat_rate_bps": 1900,
  "vat_cents": 2818,
  "gross_cents": 17650,
  "currency": "EUR",
  "storno_of": null,
  "cancelled_at": null,
  "payout": {
    "status": "paid",
    "method": "sepa_file",
    "wait_reason": null,
    "paid_at": "2026-11-01T06:00:00.000Z",
    "reversed_cents": 0
  },
  "created_at": "2026-11-01T05:00:00.000Z"
}

GET /api/partner/v1/statements/{statement_id}/pdf

Gutschrift als PDF. Archivierte Gutschrift (application/pdf).

Scope commissions:read

Antwort
(PDF)

GET /api/partner/v1/stats

Kennzahlen. Aggregierte Kennzahlen: Praxen, Provisionen je Status, letzte 12 Monate, Auszahlungen.

Scope commissions:read

Antwort
{
  "object": "stats",
  "currency": "EUR",
  "practices": {
    "active": 42,
    "total": 45,
    "with_payments": 38
  },
  "commissions": {
    "pending_cents": 14832,
    "payable_cents": 0,
    "settled_cents": 0,
    "paid_cents": 188240,
    "blocked_cents": 0,
    "count": 5210
  },
  "current_month_cents": 14832,
  "months": [
    {
      "month": "2026-10",
      "commission_cents": 14832,
      "payments": 412,
      "volume_cents": 4120000
    }
  ],
  "payouts": {
    "paid_gross_cents": 224006,
    "open_gross_cents": 0,
    "last_paid_at": "2026-10-01T06:00:00.000Z"
  }
}

Webhooks

POST /api/partner/v1/webhooks/test

Test-Ereignis senden. Sendet test.ping sofort an einen (endpoint_id) oder alle aktiven Endpunkte und liefert das Ergebnis.

Scope webhooks:write

FeldTypBeschreibung
endpoint_idstringOptional: nur dieser Endpunkt
Anfrage
{}
Antwort
{
  "object": "list",
  "data": [
    {
      "endpoint_id": "cmg3wh0001",
      "url": "https://app.praxissoft.example/webhooks/vxpay",
      "event_id": "evt_4f9…",
      "delivered": true,
      "status": 200,
      "error": null
    }
  ],
  "has_more": false
}

Webhooks & Signaturprüfung

Legen Sie im Partnerportal Endpunkte (https) an und wählen Sie Ereignisse. VXPay sendet ein POST mit JSON und den Headern VXPay-Signature, VXPay-Event-Id und VXPay-Event-Type. Die Zustellung erfolgt wenige Sekunden nach dem Ereignis; data.object hat dieselben Felder wie in der API (Rechnungen nur mit technischen Angaben, ohne Empfängerdaten). Rechnungsereignisse erhalten Sie nur, solange die Praxis Ihnen Zugriff freigegeben hat. Antworten Sie innerhalb von 10 Sekunden mit 2xx (der Antworttext wird nicht ausgewertet); sonst wiederholt VXPay die Zustellung mit wachsendem Abstand (1 Minute bis 24 Stunden, insgesamt 8 Versuche). Verarbeiten Sie jede Ereignis-ID nur einmal (Deduplizierung) und prüfen Sie die Signatur:

VXPay-Signature: t=<Unix-Sekunden>,v1=<HMAC-SHA256(secret, "t.body")> – Zeitstempel älter als 5 Minuten ablehnen (Replay-Schutz). Nach einer Secret-Rotation enthält der Header 24 Stunden lang zusätzlich die Signatur mit dem vorigen Secret.

import { createHmac, timingSafeEqual } from "node:crypto";

// Express/Next: den ROHEN Body verwenden (nicht erneut serialisieren)
export function verifyVxpaySignature(rawBody, header, secret, toleranceSec = 300) {
  const parts = header.split(",").map((p) => p.trim());
  const t = Number(parts.find((p) => p.startsWith("t="))?.slice(2));
  if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
  const expected = createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
  return parts
    .filter((p) => p.startsWith("v1="))
    .some((p) => {
      const given = Buffer.from(p.slice(3), "hex");
      return given.length === expected.length && timingSafeEqual(given, expected);
    });
}
Beispiel
{
  "id": "evt_2b7c9e0d4a1f3e5b6c7d8e9f",
  "type": "invoice.paid",
  "created": "2026-10-02T10:05:02.000Z",
  "livemode": true,
  "data": {
    "object": {
      "object": "invoice",
      "id": "cmg3inv0001",
      "practice_id": "org_8f2k1m9x",
      "number": "RE-2026-0142",
      "status": "paid",
      "cancelled": false,
      "total_cents": 8684,
      "currency": "EUR",
      "paid_at": "2026-10-02T10:05:00.000Z",
      "updated_at": "2026-10-02T10:05:00.000Z"
    }
  }
}
Ereignisdata.objectWann
practice.connectedpracticePraxis zugeordnet (Registrierung, Verbindung oder Plattform)
practice.disconnectedpracticeZuordnung einer Praxis beendet
access.grantedaccess_grantPraxis hat API-Zugriff freigegeben
access.revokedaccess_grantPraxis hat API-Zugriff widerrufen
invoice.createdinvoiceRechnung über die API angelegt
invoice.paidinvoiceRechnung einer freigegebenen Praxis bezahlt
invoice.cancelledinvoiceRechnung einer freigegebenen Praxis storniert
commission.createdcommissionProvision entstanden
commission.payablecommissionProvision fällig (Monat abgeschlossen)
commission.blockedcommissionProvision angehalten (z.B. Rückbuchung in Klärung)
commission.reversedcommissionProvision korrigiert
statement.issuedstatementGutschrift erstellt
statement.cancelledstatementGutschrift storniert
payout.paidstatementGutschrift ausgezahlt (Überweisung auf Ihr Geschäftskonto veranlasst)
payout.failedstatementAuszahlung nicht ausgeführt (wird erneut angewiesen)
payout.reversedstatementAuszahlung zurückgeholt