Alles, was Teculiar kann, erreichbar von einer Website, die wir nicht hosten: den Katalog durchsuchen, Domains suchen, bezahlen, Rechnungen begleichen, Guthaben aufladen, Dienste verwalten, Tickets eröffnen und beantworten, Inhalte lesen, ein Konto verwalten.

Basis-URL: https://api.<your-domain>/api/v1/public/… (oder https://<tenant>.teculiar.net/api/v1/public/…). Erzeugte Referenz: openapi/public-v1.json.

Du willstLies
Ein JS-SDK oder Widgets zum Einbauensdk-widgets.md
Ereignis-Benachrichtigungen an deinen Serverwebhooks.md
Jede Route, auch die internenapi-endpoints.md
Wie der Vertrag in der CI durchgesetzt wirdapi-contract.md

Eine Projektion, keine Kopie

Die öffentliche API ist keine von Hand gepflegte Parallelimplementierung. Jede Route im Produkt trägt eine ausdrückliche Entscheidung, @PublicRoute oder @InternalRoute, **die API startet nicht, wenn eine fehlt**, und /api/v1/public/* ist eine zweite Tür zu genau denselben Handlern. Die OpenAPI-Spezifikation, das SDK und diese Dokumentation werden aus diesen Entscheidungen erzeugt.

Für dich als Nutzer heißt das: Es gibt keine zweite Implementierung, die zurückfallen könnte. Eine Korrektur am Produkt ist am selben Tag eine Korrektur an der öffentlichen API.

Authentifizierung

Zwei Dinge authentifizieren einen Aufruf, und sie beantworten verschiedene Fragen.

Was es belegtWo es liegt
API-Schlüssel (Teculiar-Key)welche Website aufruftpk_ im Quelltext der Seite, sk_ auf deinem Server
Kunden-Token (Authorization: Bearer)zu welcher Person die Daten gehörenvon /public/auth/login

Ein Schlüssel autorisiert die Website, ein Token die Person. Ein öffentlicher Schlüssel darf invoices:read halten, aber jede Route mit diesem Scope verlangt zusätzlich ein Kunden-Token. Der Schlüssel allein liest also nie Daten von jemandem.

pk_ öffentlich

Eine Kennung, kein Geheimnis. Sie steht absichtlich im Quelltext der Seite, deshalb ist ihre Kontrolle die Origin-Allowlist: Eine Anfrage von einer Origin, die du nicht eingetragen hast, ist ein 403. Origins trägst du unter Admin → Einstellungen → API-Schlüssel ein.

sk_ geheim

Nur serverseitig. Eine Anfrage mit einem Origin-Header wird sofort abgelehnt: Ein Origin heißt, dass ein Browser sie gesendet hat, und ein geheimer Schlüssel in einem Browser ist bereits abgeflossen. Tausche ihn aus, wenn du diesen Fehler siehst.

Unbekannte, widerrufene und abgelaufene Schlüssel schlagen alle gleich fehl, Ausprobieren verrät also nichts.

Scopes

Drei Gruppen. Entscheidend ist nicht, ob die Daten öffentlich sind, sondern ob der Zugriff durch etwas anderes als den Schlüssel geschützt ist.

Anonym: Der Schlüssel ist die einzige Hürde, die Daten dahinter sind also öffentlich: catalog:read, content:read, domains:search, inquiries:write, auth:write, checkout:write, payment-methods:read.

Kunde: Ein pk_ darf diese halten, aber jede Route mit einem davon verlangt auch ein Kunden-Token: customers:read|write, invoices:read|pay, orders:read, payments:write, services:read|manage, domains:manage, tickets:read|write.

Geheim: Nie in einem pk_ erlaubt: webhooks:manage.

**Zugangsdaten bekommt nie ein pk_.** Eine Route, die ein Zugangsdatum herausgibt (den Transfer-Code einer Domain, GET /services/:id/transfer-code, und POST /services/:id/domain-lock, wenn sie entsperrt, oder eine Modulaktion der Klasse „geheim“), braucht das Kunden-Token, von deinem Server zusammen mit einem sk_-Schlüssel gesendet. Mit einem pk_ ist es ein 403, egal welche Scopes der Schlüssel hält: Ein in eine Seite eingeschleustes Script, die ein pk_ trägt, hätte sonst auch das Token des Kunden.

Idempotenz

Sende einen Idempotency-Key-Header (1 bis 200 Zeichen aus A-Z a-z 0-9 - _, für jede beabsichtigte Anfrage ein neuer Wert) bei POST /orders/checkout, POST /orders/{id}/pay, POST /billing/invoices/{id}/pay, POST /billing/add-funds, POST /storefront/inquiries und POST /tickets. Die Operationen, die ihn annehmen, sind in der Spezifikation mit x-idempotent markiert.

  • Derselbe Schlüssel mit demselben Body liefert noch einmal die erste Antwort, mit demselben Status und dem Header Idempotent-Replayed: true. Nichts läuft doppelt: keine zweite Bestellung, kein zweites Konto, keine zweite Rechnung, keine zweite Abbuchung.
  • Derselbe Schlüssel mit einem anderen Body (oder von einem anderen Kunden) ergibt 422 IDEMPOTENCY_KEY_REUSED.
  • Derselbe Schlüssel, solange die erste Anfrage noch bearbeitet wird, ergibt 409 IDEMPOTENCY_IN_PROGRESS; versuche es nach einem Moment erneut. Eine Anfrage, die nie abgeschlossen wurde (der Server wurde neu gestartet), wird nach fünf Minuten vergessen.
  • Eine Antwort mit 2xx oder 4xx wird 24 Stunden aufbewahrt. Bei einer 5xx-Antwort oder einem Absturz bleibt nichts zurück, die Wiederholung läuft also. Gespeichert wird nur ein Hash deines Request-Bodys, nie der Body selbst; die gespeicherte Antwort ist die, die du bekommen hast.
  • Ohne den Header ändert sich nichts. Ein fehlerhafter Schlüssel ergibt 400 IDEMPOTENCY_KEY_INVALID.

Das SDK sendet bei jedem Aufruf dieser Operationen einen neuen Schlüssel und wiederholt den Aufruf nach einem Timeout oder einer 5xx-Antwort mit demselben Schlüssel. Andere Schreibzugriffe wiederholt es nie.

Rate Limits

Pro Schlüssel und pro IP, global durchgesetzt. Die Werte für Tenants sind Einstellungen unter Admin → Einstellungen → Integrationen, jeweils durch eine Obergrenze der Plattform begrenzt, die die Plattform schützt und nicht den Tenant. Die Domainsuche ist die strengste, weil jede Suche ausgehende Abfragen bei den Registrierungsstellen von unserer IP-Adresse aus kostet.

Die Obergrenze selbst liegt auf der Betreiberseite, im Control-Plane gespeichert und von keiner Route für Tenants erreichbar. Dein Admin zeigt dir die Obergrenze neben jedem Feld, damit ein begrenzter Wert erklärt wird statt rätselhaft zu bleiben; sie anzuheben ist ein Gespräch mit uns, keine Einstellung.

Ein 429 enthält Retry-After. Das SDK gibt es als typisierten Fehler weiter, statt blind zu wiederholen.

Kompatibilität

Die veröffentlichte Oberfläche wird nur erweitert. Felder kommen hinzu und werden ohne Versionssprung nie entfernt oder umbenannt, und das wird durchgesetzt, nicht nur versprochen: scripts/api-contract-check.ts vergleicht die erzeugte Spezifikation mit dem letzten veröffentlichten Stand und lässt den Build scheitern, wenn ein veröffentlichtes Feld verschwindet.

Ein Teculiar-Version-Datumsheader ist für eine künftige inkompatible Änderung reserviert. Solange es keine gibt, ist nichts festzulegen.

Eingaben. docs/openapi/public-v1.json enthält den requestBody jeder schreibenden Operation und die Query-parameters jeder Operation, geschrieben in public-inputs.ts und abgeglichen mit dem, was die Handler tatsächlich lesen. Das Gate lehnt gegenüber der letzten Version außerdem ab: eine entfernte Eingabe, eine optionale Eingabe, die Pflicht wurde, eine neue Pflichteingabe und einen entfernten erlaubten Wert (ein enum). Felder, die in der Spezifikation als required markiert sind, sind die, ohne die die API eine Anfrage ablehnt.

Antworttypen. Ein Feld in der Spezifikation trägt einen JSON-Typ, wo die Form einen vorgibt (string, integer, number, boolean, object, array und null für Felder, die leer sein können); die Typen des SDK folgen daraus, ein Aufruf kommt also typisiert zurück. Der Katalog, der Bootstrap, die Bestellvorschau und der Checkout, die Statusroute, Beiträge, die Modulbibliothek, die Namensprüfung und die Tarife sind vollständig typisiert. Jedes veröffentlichte Feld ist im Typ des SDK trotzdem optional, und ein Typ ist wie ein Name Teil des Vertrags.

Caching. Anonyme Lesezugriffe tragen einen ETag und Cache-Control: public, max-age=60. Alles, was ein Kunden-Token verlangt, ist private, no-store: Eine Antwort für einen einzelnen Kunden darf nie in einem gemeinsamen Cache liegen.

Bootstrap: GET /storefront/i18n

Ein kleiner, gecachter Aufruf sagt einer Website alles, was sie für Sprachen und Geld braucht. Scope catalog:read, ohne Anmeldung; der öffentliche Cache behält ihn 60 Sekunden.

{
  "main": "en",
  "languages": ["en", "de"],
  "installed": ["de", "en", "tr"],
  "fallback": "en",
  "meta": {
    "en": { "bcp47": "en-US", "nativeName": "English", "flag": "us", "defaultCurrency": "USD" },
    "de": { "bcp47": "de-DE", "nativeName": "Deutsch", "flag": "de", "defaultCurrency": "EUR" }
  },
  "currencies": {
    "main": "USD",
    "list": ["USD", "EUR"],
    "rates": { "EUR": { "rate": 0.92, "buffer": 0, "bufferEnabled": false } }
  },
  "serverTime": 1760520000000,
  "cookieDomain": null
}
  • languages ist, was der Shop ausliefert, die Hauptsprache zuerst. meta hat einen Eintrag pro ausgelieferter Sprache (nicht pro installiertem Sprachpaket). defaultCurrency ist die Währung, die die Sprache nahelegt, so ermittelt wie bei den Teculiar-Themes: die des Sprachpakets, sonst die der Hauptsprache. Eine Website nutzt sie nur, wenn der Shop diese Währung führt.
  • currencies.list hat die Hauptwährung zuerst. Eine Währung ohne gültigen Wechselkurs (eine Zahl über 0) wird nie aufgeführt. rates enthält einen Eintrag für jede aufgeführte Währung außer der Hauptwährung. Kurse und Puffer sind Anzeigedaten wie in /storefront/settings; den Preis jeder Bestellung berechnet weiterhin der Server.
  • serverTime ist die Uhr des Servers in Millisekunden, als die Antwort gebaut wurde. Die Antwort wird gecacht und kann deshalb einige Minuten alt sein; das SDK gleicht das aus.
  • cookieDomain ist null, bis ein Tenant den Host seiner eigenen Website einträgt (Parity L4).

Nur auf der Plattform verfügbare Routen

Diese antworten auf teculiar.com (und im eigenen Labor der Plattform); in jedem anderen Shop sind sie ein 404.

  • GET /platform/names/check?name= (Scope catalog:read, 30 pro Minute) sagt, ob ein Systemname (<name>.teculiar.net) vergeben werden kann: { name, available, reason, suggestions, rules }. reason ist null, invalid, reserved, not_for_sale, taken (ein System hat ihn) oder held (eine andere unbezahlte Bestellung bezahlt gerade dafür). suggestions (höchstens 3) erscheinen bei einem gültigen Namen, der nicht verfügbar ist. rules ist die Grammatik: 1 bis 31 Zeichen, Buchstaben, Ziffern und Bindestriche, kein Bindestrich am Anfang oder Ende.
  • POST /orders/preview und POST /orders/checkout lehnen einen nicht verfügbaren Namen mit 409 und { code: "NAME_UNAVAILABLE", name, reason } ab. Der Checkout hält den Namen, bis die Rechnung fällig ist, damit der Name, den ein Käufer bezahlt, auch der ist, den er bekommt. Die Reservierung endet, wenn die Rechnung gelöscht wird oder das System existiert.
  • GET /storefront/modules?locale= und GET /storefront/modules/{name}?locale= listen die Modulbibliothek: name, label und description in dieser Sprache (Englisch, wenn es keine Übersetzung gibt), kind, families, capabilities, pricing (free oder paid) und hasPanel. Die einzelne Route ergänzt doc: die Anleitung des Moduls, { language, markdown }, oder null. Module nur für die Plattform werden nie aufgeführt.
  • GET /platform/plans?locale= (Scope catalog:read) listet die vier Teculiar-Tarife aus dem gemeinsamen Register: key, name, summary (gewünschte Sprache, Englisch als Rückfall), currency, monthlyCents, yearlyCents, limits, features, ceilingProfile. Die Preisseite liest sie; kein Limit wird von Hand eingetippt.

Zusatzoptionen im Katalog

GET /storefront/products gibt jedem Produkt eine addOns-Liste (nur aktive): id, slug, name, nameTranslations, description, descriptionTranslations, amountCents, setupFeeCents, billingCycle, recurring und maxQuantity (99). Sende die id und eine Menge in addOns: [{ addOnId, quantity }] einer Bestellposition bei Vorschau und Checkout. Was der Kauf am Dienst ändert, ist intern und wird nicht veröffentlicht.

Bestellfelder

Ein Produkt, dessen Modul vom Käufer etwas verlangt (ein Teculiar-Tarif: Systemname, Marke, Name und E-Mail des Administrators), führt es in configurationFields bei GET /storefront/products auf: key, type (text, email oder subdomain), required, maxLength, pattern, label und help für jede ausgelieferte Sprache sowie checkUrl (die Route, die sagt, ob eine Subdomain frei ist, sonst null). Sende die Werte unter diesen Schlüsseln in der configuration der Bestellposition. Vorschau und Checkout lehnen eine Position, der ein Pflichtfeld fehlt oder die pattern oder maxLength verletzt, mit 400 und { code: "ORDER_FIELD_INVALID", field: "<key>", reason } ab (required, invalid oder too_long).

Checkout-Status

POST /orders/checkout antwortet mit dem Checkout als der Rechnung dahinter und einem Status-Token:

{ "invoice": { "id": "…", "invoiceNumber": "N-100821", "status": "PENDING", "currency": "EUR", "totalCents": 741, "dueAt": "…" },
  "order": { "id": "…" },
  "statusToken": "…" }

order.id ist die Checkout-ID (die Rechnungs-ID), bis die Zahlung die Bestellung anlegt. Das statusToken wird einmal angezeigt; nur sein Hash wird aufbewahrt. Bezahle mit POST /orders/{id}/pay (die Antwort enthält invoice.paymentRedirectUrl, wenn das Gateway den Käufer braucht, und invoice.attemptStatus, wenn ein Versuch fehlgeschlagen ist, abgebrochen wurde oder abgelaufen ist).

Die Seite eines Gastes verfolgt den Checkout ohne Anmeldung: GET /orders/{id}/status?token=<statusToken> (Scope checkout:write, 60 Anfragen pro Minute).

{ "status": "provisioning",
  "items": [ { "type": "MANAGED_SERVICE", "productName": "Branded", "status": "pending", "externalId": null, "links": [] } ] }

status ist awaiting_payment, provisioning (bezahlt, etwas ist noch nicht aktiv), ready (jede Position aktiv), failed oder cancelled. Eine Position ist pending, active oder failed; ist sie aktiv, trägt sie die externalId des Dienstes und einen portal-Link zu seiner Seite. In dieser Antwort stehen keine Namen, E-Mails, Adressen oder Beträge. Ein falsches Token und ein unbekannter Checkout ergeben dasselbe 404.

Rückkehr-URLs

returnUrl und cancelUrl bei Zahlungsaufrufen werden gegen die Hosts geprüft, die du verifiziert hast, plus die Origins, die am aufrufenden Schlüssel eingetragen sind. Alles andere ist ein hartes 400, nie ein stiller Rückfall.

Das ist mit Absicht streng. Ein offener Redirect in einem Bezahlablauf ist ein Geschenk an Phisher: Er erlaubt es einem Angreifer, deinen Kunden direkt nach einer echten Zahlung auf einen täuschend echten Checkout zu schicken, über einen Link, der wirklich von dir stammt. Verglichen wird der Host einer geparsten URL: shop.example.com.evil.test und shop.example.com@evil.test werden beide abgelehnt.

Wohin der Käufer geht, und wann. returnUrl und cancelUrl werden von POST /orders/{id}/pay und POST /billing/invoices/{id}/pay akzeptiert. Keine von beiden wird an das Zahlungs-Gateway gegeben: Das Gateway kehrt immer zur eigenen Zahlungsrückkehr-Seite des Shops zurück, die die Zahlung erfasst (PayPal, Mollie) und die Bestellung anlegt, und erst dann den Käufer weiterschickt. Deine Adresse wird mit dem Checkout gespeichert und danach verwendet. Es werden zwei Query-Parameter angehängt, order (die Checkout-ID) und result, einer von paid, pending (zum Beispiel SEPA, wird noch verarbeitet), failed oder cancelled. Eine abgebrochene Zahlung geht an cancelUrl, wenn du eine angegeben hast, sonst an returnUrl. Die Adresse trägt nie einen Login-Code oder ein Token: Die Anmeldung des Käufers bleibt auf der Seite des Shops. Ohne returnUrl bleibt der Käufer wie bisher auf den eigenen Seiten des Shops. returnUrl und cancelUrl werden gleich geprüft.

Der eingebettete Checkout hat keinen Schlüssel. <teculiar-checkout> bezahlt aus dem eigenen Frame des Shops, daher gelten keine Origins eines Schlüssels: Seine return-url und cancel-url müssen auf einem Host liegen, den du verifiziert hast (ein Eigene Website-Host unter Admin → Domains oder einer der eigenen Hosts des Shops). Eine nicht verifizierte Website bekommt dasselbe 400, im Frame angezeigt. Aufrufe mit einem Schlüssel (der Headless-Ablauf) dürfen auch die Origins des Schlüssels nutzen.

Ergebnisse je Anbieter (live gegen den Testmodus von Mollie und die Sandbox von PayPal geprüft): Eine bezahlte Zahlung kommt als result=paid an; eine Zahlung, die der Anbieter abgelehnt hat oder die abgelaufen ist, als failed; ein Käufer, der bei Mollie zurück drückt oder bei PayPal „Abbrechen und zurückkehren“, als cancelled. In den letzten beiden ist nichts bezahlt, und die Rechnung bleibt zahlbar.

Support: Tickets und Kontakt

Kontakt- und Anfrageformular

POST /storefront/inquiries: kein Kunden-Token und kein API-Schlüssel nötig (anonym). Legt ein Ticket in der Support-Warteschlange des Tenants an und gibt einen Ticket-Datensatz zurück.

POST /api/v1/public/storefront/inquiries
Content-Type: application/json

{
  "name": "Jane Smith",
  "email": "jane@example.com",
  "subject": "Pre-sales question",
  "message": "Does your Starter plan include email hosting?",
  "source": "contact"
}

Antwort 201:

{
  "id": "cmt8duoue04z5qr012wlvgu7v",
  "publicId": "MED8RD8L",
  "status": "OPEN",
  "priority": "NORMAL",
  "subject": "Pre-sales question",
  "createdAt": "2026-08-25T08:08:14.150Z",
  "updatedAt": "2026-08-25T08:08:14.150Z",
  "replies": [
    {
      "id": "cmt8duoue04z7qr01jfv92stu",
      "body": "Name: Jane Smith\nE-Mail: jane@example.com\n\nDoes your Starter plan include email hosting?",
      "internal": false,
      "createdAt": "2026-08-25T08:08:14.150Z"
    }
  ]
}

Felder: name (1–100 Zeichen), email, phone (optional, max. 30), subject (1–200), message (10–5000). source ist "contact" oder "inquiry" und leitet das Ticket in die Abteilung, die der Tenant für beides eingerichtet hat. _honey ist ein Honeypot-Feld; ein nicht leerer Wert verrät einen Bot, und die Einsendung wird stillschweigend verworfen.

Tickets von Kunden

Diese Routen verlangen ein Bearer-Token des Kunden (Authorization: Bearer <token>) und den Scope tickets:read oder tickets:write am API-Schlüssel.

MethodePfadScopeWas sie tut
GET/ticketstickets:readListet die Tickets des angemeldeten Kunden. Optional ?status= und ?department=.
POST/ticketstickets:writeEröffnet ein Ticket. Pflicht: subject, body, departmentId. Optional: priority, serviceId.
GET/tickets/departmentstickets:readListet die Abteilungen, bei denen ein Ticket eröffnet werden kann (id/slug/name/colour).
GET/tickets/{id}tickets:readLiest ein Ticket mit Antworten. Akzeptiert die Datenbank-ID oder die 8-stellige öffentliche ID. Interne Notizen und Felder nur für Mitarbeiter werden entfernt.
POST/tickets/{id}/repliestickets:writeAntwortet auf ein Ticket. Das Feld internal wird bei Kunden-Tokens ignoriert.
POST/tickets/{id}/attachmentstickets:writeHängt Dateien an (multipart, Feld files). PNG/JPG/WebP/GIF/AVIF/TIFF und PDF (wenn der Tenant es erlaubt), höchstens 5 Dateien, je nach Tarif 25–100 MB pro Datei. Jede Datei wird vor dem Speichern neu kodiert. Jeder Anhang in einer Antwort trägt url, einen signierten Link auf dem Datei-Host, 1 Stunde gültig (expiresAt), oder null mit removedReason. storageKey ist veraltet: Er enthält jetzt denselben absoluten Link. Gedrosselt auf 20/Min.
POST/tickets/{id}/closetickets:writeSchließt ein Ticket.

Werte für priority: LOW, NORMAL (Standard), HIGH, URGENT.

GET /tickets/departments ist veröffentlicht, weil departmentId ein Pflichtfeld von POST /tickets ist; ohne sie wäre das Schreiben über die öffentliche API nicht aufrufbar. Das Routing-Postfach der Abteilung ist in der Antwort nicht enthalten.

Interne Notizen (Antworten mit internal: true) werden serverseitig bei Kunden-Tokens entfernt. Ein Kunde kann auch mit internal: true im Body über ein Kunden-Token keine Mitarbeiternotiz absetzen.

Routen nur für Mitarbeiter (zuweisen, Status ändern, löschen, Textbausteine, die Admin-Ticket-Warteschlange) sind @InternalRoute und werden hier nicht veröffentlicht.

Was bewusst nicht veröffentlicht wird

  • Die Admin-Oberfläche. Diese Phase macht das kundenseitige Produkt headless. Das Admin-Dashboard headless zu betreiben ist ein anderes Produkt mit einem anderen Bedrohungsmodell.
  • Dienst-Panels der Module (services/:id/panel): das Hosting-Kontrollpanel, die Transfersperre einer Domain. Sie verwalten Postfächer und setzen Passwörter zurück. Sie werden im eingebetteten Portal auf unserer Origin dargestellt oder gar nicht. Was ein Modul von seinem Panel veröffentlicht, steht unter Modulaktionen.
  • **POST /services**: richtet einen Dienst ein, ohne dass eine Bestellung und eine Rechnung dahinterstehen. Der öffentliche Weg zu einem neuen Dienst ist orders/checkout, das ihn berechnet und die Rechnung erzeugt.
  • CRUD für Webhook-Endpunkte. Ein Schlüssel, der eine empfangende URL registrieren könnte, wäre ein Kanal zum Abfluss von Daten. Richte Endpunkte in deinem Admin ein.
  • Das SSO-Austausch/Einlöse-Paar. Ein intern an einen Host gebundenes Primitiv, kein Grant-Typ.

Modulaktionen

Das Modul eines Dienstes (sein Hosting-Panel, Server oder seine Registrierungsstelle) kann einige seiner Panel-Aktionen hier veröffentlichen. Jede Aktion hat eine Risikoklasse, und die Klasse entscheidet, wie weit sie gehen darf:

KlasseRouteScopeHinweise
lesenGET /services/:id/actions/:actionservices:read
umkehrbarPOST /services/:id/actions/:action (bei einer Registrierungsstelle: POST /services/:id/domain-actions/:action)services:manage (domains:manage)30 pro Minute
geheimwie umkehrbarwie umkehrbarnie mit einem pk_, siehe Scopes
zerstörend––nie veröffentlicht

GET /services/:id/actions listet, was das Modul des Dienstes veröffentlicht: id, label, module, risk, method, path, scope und die Body-fields, die es annimmt. Eine Aktion, die das Modul auf der aufgerufenen Route nicht veröffentlicht, ist ein 404, wie eine, die nicht existiert. Der Body besteht aus flachen Textfeldern; die Aktion wird durch den Pfad benannt.

ModulVeröffentlicht
Hetzner Cloudpower-on, reboot, shutdown, power-off (umkehrbar)
cPanelsso (Feld app: files, email, apps), webmail: Single-Use-Anmeldelinks (geheim)
Resell.biz, Dynadotlock-state (lesen), lock (umkehrbar), unlock (geheim: gibt den Transfer-Code zurück)
Virtualminnichts

Die Antwort ist die Quittung des Anbieters (accepted, message und bei einem Anmeldelink url) oder, bei einer Registrierungsstelle, die Sperre (domain, locked, lockCheckedAt, nach einem Entsperren transferCode). Jeder Lauf steht in den Logs des Tenants; die Antwort einer geheimen Aktion nie.

configuration einer Bestellposition

POST /orders/checkout und /orders/preview akzeptieren in jeder Position ein frei geformtes configuration-Objekt, aber nur diese Schlüssel bleiben erhalten: Alles andere wird ohne Fehler verworfen:

FürSchlüssel
Eine Domain-PositionbillingCycle, domainAction, extraAttributes, nameServers, transferAuthCode
Eine Hosting-Position und ihre DomainbundledDomain, domainName, domainUse
Ein Teculiar-TarifadminEmail, adminName, brand, subdomain

Alles, was entscheidet, was eingerichtet oder berechnet wird (der Hosting-Tarif, der Verlängerungspreis, eine kostenlose Domain, das Provisionierungsmodul), kommt aus dem Produkt und vom Server, nie aus der Anfrage. Die Liste steht in apps/api/src/common/order-line-configuration.ts.

Lebenszyklus eines Dienstes

Das Feld status eines Dienst-Objekts ist die Sicht des Kunden auf seinen Zustand. Die Werte bilden einen linearen Lebenszyklus; Verzweigungen gibt es nur bei ACTIVE:

StatusBedeutung
PENDINGAngelegt und wartet darauf, dass die Zahlung eingeht, oder darauf, dass das Hosting-Modul die Einrichtung bestätigt.
PROVISIONINGDie Zahlung ist eingegangen; das Hosting-Modul richtet das Konto gerade ein. (Virtualmin meldet intern „QUEUED“, solange seine Job-Warteschlange arbeitet; die Plattform ordnet das vor dem Speichern PROVISIONING zu.)
ACTIVELäuft normal.
SUSPENDEDWegen Nichtzahlung gesperrt. Wird automatisch wiederhergestellt, wenn die überfällige Rechnung beglichen ist.
CANCELLEDZur Beendigung zum Zeitpunkt cancelAt vorgemerkt oder bereits gekündigt. Verlängerungsrechnungen enden.
TERMINATEDDauerhaft abgebaut. Das Konto wurde beim Anbieter entfernt.
FAILEDDie Einrichtung lief und war nicht erfolgreich; ein Admin kann sie wiederholen.

Karenzzeit bei Sperrung. Eine Rechnung, die überfällig wird, sperrt den Dienst nicht sofort. Der Mahnlauf (einmal täglich per Cron) sperrt erst, wenn seit dem dueAt der Rechnung volle 24 Stunden vergangen sind. Ein Hosting-Dienst (Virtualmin) wird bei der Sperrung zusätzlich im Panel deaktiviert: Das Konto wird nicht gelöscht und bei Zahlung wieder aktiviert.

Kündigung wirkt nicht sofort. POST /services/:id/cancel plant das Ende des Dienstes zum Ende der bezahlten Laufzeit (renewsAt, ersatzweise nextDueAt). Das Feld cancelAt in der Antwort trägt dieses Datum; der Dienst bleibt ACTIVE, bis der Cron-Lauf es erreicht. POST /services/:id/resume macht die Kündigung rückgängig, wenn es vor diesem Datum aufgerufen wird.

Feldfilter auf der öffentlichen Oberfläche. Das Dienst-Objekt, das GET /services und GET /services/:id zurückgeben, lässt bewusst weg:

  • configuration / customFields: undurchsichtige Provisionierungsdaten, die Hosting-Zugangsdaten enthalten können.
  • moduleName / moduleReference / externalId: interne Kennungen der Infrastruktur.
  • suspendedReason: eine interne Admin-Notiz.
  • userId, orderId, orderItemId, initialInvoiceId: Fremdschlüssel.

Das Unterarray domainRecords[] lässt aus demselben Grund eppCode, authCodeHash, registrarProvider, registrarModule, externalId und dnsRecords weg.

CORS

/api/v1/public/* antwortet mit Access-Control-Allow-Origin: * und credentials: false und akzeptiert Content-Type, Authorization und Teculiar-Key.

credentials: false neben Authorization ist richtig und kein Widerspruch: Ein Bearer-Token in einem ausdrücklichen Header ist kein CORS-Credential, weil der Browser es nie von selbst anhängt. Das wäre nur ein Cookie, und diese Oberfläche liest keine.

CORS ist nicht die Zugriffskontrolle: Ein Aufrufer außerhalb des Browsers ignoriert es völlig. Die Kontrolle ist der Schlüssel-Guard.

Fehler

Standard-HTTP-Statuscodes mit einem JSON-Body.

StatusBedeutet
400Die Anfrage ist fehlerhaft, oder eine Rückkehr-URL hat die Prüfung nicht bestanden.
401Schlüssel fehlt oder ist ungültig, oder ein Kunden-Token ist nötig und fehlt.
403Dem Schlüssel fehlt der Scope, oder die Origin ist nicht erlaubt.
404Nicht veröffentlicht oder tatsächlich nicht vorhanden, bewusst nicht unterscheidbar.
409Eine Zahlung für diese Rechnung läuft bereits, oder die Rechnung wurde inzwischen bezahlt.
429Rate-limitiert. Siehe Retry-After.

409 kommt von POST /billing/invoices/{id}/pay und ist der eine Status, den du besonders behandeln solltest: Die Anfrage war in Ordnung und es gibt nichts zu korrigieren, ein Client darf sie also nicht mit einer anderen Zahlungsart wiederholen. Eine Rechnung wird einmal eingezogen, und eine Zahlung, die bei einem Gateway schon unterwegs ist (eine SEPA-Lastschrift braucht Tage), lehnt eine zweite ab, egal welche Zahlungsart sie nennt. Zeige dem Käufer die Meldung und höre auf; die Rechnung wechselt von selbst auf bezahlt, wenn das Geld eintrifft. Siehe docs/ordering-and-invoices.md.

Der Login gibt dasselbe 401 zurück, egal ob die Adresse existiert oder das Passwort falsch ist, und das Zurücksetzen des Passworts gibt immer {ok: true} zurück. Die Registrierung verrät weiterhin, dass eine Adresse vergeben ist: ein bewusster UX-Kompromiss, der im gehosteten Shop schon bestand und durch eine harte Drosselung abgemildert wird, ohne ihn als sicher auszugeben.

Operationen

auth

POST /auth/2fa/disable

Turn two-factor off

ScopeAnmeldungSeit
customers:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
passwordbodystringJa

GET /auth/2fa/setup

Begin two-factor setup

ScopeAnmeldungSeit
customers:writeKundenanmeldung2026-09

GET /auth/2fa/status

Whether two-factor is enabled

ScopeAnmeldungSeit
customers:readKundenanmeldung2026-09

POST /auth/2fa/verify

Confirm two-factor setup

ScopeAnmeldungSeit
customers:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
codebodystringJa

POST /auth/login

Sign a customer in and return bearer tokens

ScopeAnmeldungSeit
auth:writeNur Schlüssel2026-09
NameInTypErforderlichHinweise
emailbodystring emailJa
passwordbodystringJa
scopebodyadmin / client
totpCodebodystringThe six digits from the authenticator, when two-factor is on.

POST /auth/logout

Revoke a refresh token

ScopeAnmeldungSeit
auth:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
refreshTokenbodystringJa

POST /auth/password-reset/confirm

Complete a password reset

ScopeAnmeldungSeit
auth:writeNur Schlüssel2026-09
NameInTypErforderlichHinweise
passwordbodystringJa
tokenbodystringJa

POST /auth/password-reset/request

Request a password-reset email

ScopeAnmeldungSeit
auth:writeNur Schlüssel2026-09
NameInTypErforderlichHinweise
emailbodystring emailJa
scopebodyadmin / client

POST /auth/refresh

Exchange a refresh token for a new access token

ScopeAnmeldungSeit
auth:writeNur Schlüssel2026-09
NameInTypErforderlichHinweise
refreshTokenbodystringJa

POST /auth/register

Register a customer account

ScopeAnmeldungSeit
auth:writeNur Schlüssel2026-09
NameInTypErforderlichHinweise
addressbodyobject
companyNamebodystring
countryCodebodystring
customerTypebodyINDIVIDUAL / BUSINESS
emailbodystring emailJa
namebodystringJa
passwordbodystringJa
phonebodystring
vatIdbodystring

billing

POST /billing/add-funds

Top up the customer's account balance

ScopeAnmeldungSeit
payments:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
Idempotency-KeyheaderstringA unique value per intended request (1 to 200 of A-Z a-z 0-9 - _). A retry with the same key and body returns the first answer instead of repeating the work.
amountCentsbodyintegerJa
ibanbodystring
methodbodyCREDIT_CARD / PAYPAL / SEPA / CRYPTO / BANK_TRANSFER / IDEALJa

GET /billing/invoices

List the customer's invoices

ScopeAnmeldungSeit
invoices:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
statusquerystringOnly invoices in this status.

GET /billing/invoices/{id}

Read one invoice

ScopeAnmeldungSeit
invoices:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

GET /billing/invoices/{id}/html

Render an invoice as HTML

ScopeAnmeldungSeit
invoices:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

POST /billing/invoices/{id}/pay

Pay an invoice

ScopeAnmeldungSeit
invoices:payKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
Idempotency-KeyheaderstringA unique value per intended request (1 to 200 of A-Z a-z 0-9 - _). A retry with the same key and body returns the first answer instead of repeating the work.
cancelUrlbodystringWhere the buyer ends up after a cancelled payment. Same rule as returnUrl.
ibanbodystring
methodbodyCREDIT_CARD / PAYPAL / SEPA / CRYPTO / BANK_TRANSFER / IDEALJa
paymentMethodIdbodystring
returnUrlbodystringWhere the buyer ends up after the payment is confirmed, with order and result added. Must be a verified host of the store or an origin of the key.
useAccountBalancebodyboolean

GET /billing/invoices/{id}/payment-methods

List the payment methods that can pay one invoice

ScopeAnmeldungSeit
invoices:payKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

GET /billing/invoices/{id}/pdf

Download an invoice PDF

ScopeAnmeldungSeit
invoices:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

GET /billing/payment-methods

List the customer's saved payment methods

ScopeAnmeldungSeit
payment-methods:readKundenanmeldung2026-09

cms

GET /cms/announcements

List announcements for the signed-in customer

ScopeAnmeldungSeit
customers:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

POST /cms/announcements/{id}/hide

Hide an announcement for this customer

ScopeAnmeldungSeit
customers:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

POST /cms/announcements/{id}/read

Mark an announcement as read

ScopeAnmeldungSeit
customers:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

GET /cms/blog-categories

List blog categories

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

GET /cms/blog-tags

List blog tags

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

GET /cms/pages/{locale}/{slug}

Read a published CMS page

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
localepathstringJa
slugpathstringJa

GET /cms/post-tags

List blog post tags

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
limitquerystringHow many tags, most used first.
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

GET /cms/posts

List published blog posts

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
tagquerystringTag slug.
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

GET /cms/posts/{locale}/{slug}

Read a published blog post

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
localepathstringJa
slugpathstringJa

domains

Check domain availability and price

ScopeAnmeldungSeit
domains:searchNur Schlüssel2026-09
NameInTypErforderlichHinweise
domainquerystringJaThe name to look up, with its extension.
yearsquerystringRegistration years to price.
suggestionsquerytrue / falsefalse skips the other-extension suggestions (default true).

knowledgebase

GET /knowledgebase

Read published knowledgebase articles

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09

GET /knowledgebase/{slug}

Read published knowledgebase articles

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
slugpathstringJa

GET /knowledgebase/suggest

Read published knowledgebase articles

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
qquerystringWhat the visitor has typed so far.

orders

GET /orders/{id}

Read one order

ScopeAnmeldungSeit
orders:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

POST /orders/{id}/pay

Pay for an order

ScopeAnmeldungSeit
payments:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
Idempotency-KeyheaderstringA unique value per intended request (1 to 200 of A-Z a-z 0-9 - _). A retry with the same key and body returns the first answer instead of repeating the work.
cancelUrlbodystringWhere the buyer ends up after a cancelled payment. Same rule as returnUrl.
ibanbodystring
methodbodyCREDIT_CARD / PAYPAL / SEPA / CRYPTO / BANK_TRANSFER / IDEALJa
paymentMethodIdbodystringJa
returnUrlbodystringWhere the buyer ends up after the payment is confirmed, with order and result added. Must be a verified host of the store or an origin of the key.

GET /orders/{id}/status

Read a checkout's progress with its status token

ScopeAnmeldungSeit
checkout:writeNur Schlüssel2026-10
NameInTypErforderlichHinweise
idpathstringJa
tokenquerystringJaThe statusToken that checkout answered with.

POST /orders/checkout

Place an order (guest or signed-in)

ScopeAnmeldungSeit
checkout:writeNur Schlüssel2026-09
NameInTypErforderlichHinweise
Idempotency-KeyheaderstringA unique value per intended request (1 to 200 of A-Z a-z 0-9 - _). A retry with the same key and body returns the first answer instead of repeating the work.
currencybodystringISO 4217 code the buyer is charged in; an unserved one falls back to the store's main currency.
customerbodyobjectJa
customer.addressbodyobject
customer.companyNamebodystring
customer.countryCodebodystring
customer.customerTypebodyINDIVIDUAL / BUSINESS
customer.emailbodystring emailJa
customer.namebodystringJa
customer.passwordbodystringFor a new account.
customer.phonebodystring
customer.vatIdbodystring
itemsbodyobject[]Ja
items[].addOnIdsbodystring[]Add-ons, one of each. addOns wins when both are sent.
items[].addOnsbodyobject[]Add-ons with how many of each.
items[].addOns[].addOnIdbodystring
items[].addOns[].quantitybodyinteger
items[].configurationbodyobjectValues for the product's order fields.
items[].domainNamebodystringFor a domain line: the name to register or transfer.
items[].productIdbodystring
items[].productPriceIdbodystringThe price (billing cycle) to buy; the cheapest is used when absent.
items[].quantitybodyinteger
localebodystringLanguage code, e.g. en or de. Names and messages come back in it.

POST /orders/preview

Price an order server-side before checkout

ScopeAnmeldungSeit
checkout:writeNur Schlüssel2026-09
NameInTypErforderlichHinweise
currencybodystringISO 4217 code the buyer is charged in; an unserved one falls back to the store's main currency.
customerbodyobjectOnly what a price depends on.
customer.countryCodebodystring
customer.customerTypebodyINDIVIDUAL / BUSINESS
customer.vatIdbodystring
itemsbodyobject[]Ja
items[].addOnIdsbodystring[]Add-ons, one of each. addOns wins when both are sent.
items[].addOnsbodyobject[]Add-ons with how many of each.
items[].addOns[].addOnIdbodystring
items[].addOns[].quantitybodyinteger
items[].configurationbodyobjectValues for the product's order fields.
items[].domainNamebodystringFor a domain line: the name to register or transfer.
items[].productIdbodystring
items[].productPriceIdbodystringThe price (billing cycle) to buy; the cheapest is used when absent.
items[].quantitybodyinteger
localebodystringLanguage code, e.g. en or de. Names and messages come back in it.

platform

GET /platform/domains/check

Check whether a domain can be connected to a new system

ScopeAnmeldungSeit
catalog:readNur Schlüssel2026-10
NameInTypErforderlichHinweise
hostquerystringThe domain to check, as typed (besthost.com).

GET /platform/names/check

Check whether a system name can be claimed

ScopeAnmeldungSeit
catalog:readNur Schlüssel2026-10
NameInTypErforderlichHinweise
namequerystringThe system name to check, as typed.

GET /platform/plans

List the Teculiar plans with their prices, limits and features

ScopeAnmeldungSeit
catalog:readNur Schlüssel2026-10
NameInTypErforderlichHinweise
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

products

GET /products

List products

ScopeAnmeldungSeit
catalog:readNur Schlüssel2026-09

services

GET /services

List the customer's services

ScopeAnmeldungSeit
services:readKundenanmeldung2026-09

GET /services/{id}

Read one service, including its domain records

ScopeAnmeldungSeit
services:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

GET /services/{id}/actions

List the module actions a service offers

ScopeAnmeldungSeit
services:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

GET /services/{id}/actions/{action}

Run a read action of a service's module

ScopeAnmeldungSeit
services:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
actionpathstringJa

POST /services/{id}/actions/{action}

Run an action of a service's module

ScopeAnmeldungSeit
services:manageKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
actionpathstringJa

PATCH /services/{id}/auto-renew

Turn a service's auto-renew on or off

ScopeAnmeldungSeit
services:manageKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
autoRenewbodybooleanJa

POST /services/{id}/cancel

Request cancellation of a service

ScopeAnmeldungSeit
services:manageKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
cancelDomainsbodybooleanAlso cancel the domains on the service.
reasonbodystring
reasonTextbodystring
timingbodystringWhen the cancellation takes effect.

GET /services/{id}/cancellation-request

The cancellation request waiting on a service, if any

ScopeAnmeldungSeit
services:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

POST /services/{id}/change-plan

Change a service's plan

ScopeAnmeldungSeit
services:manageKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
productPriceIdbodystringThe price to move to, from plan-options.

POST /services/{id}/domain-actions/{action}

Run an action of a domain's registrar module

ScopeAnmeldungSeit
domains:manageKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
actionpathstringJa

GET /services/{id}/domain-lock

Read the transfer lock on a domain service

ScopeAnmeldungSeit
services:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

POST /services/{id}/domain-lock

Lock or unlock a domain for transfer

ScopeAnmeldungSeit
domains:manageKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
lockedbodybooleanJa

GET /services/{id}/plan-options

Plans this service can change to

ScopeAnmeldungSeit
services:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

POST /services/{id}/renew-domain

Renew the domain attached to a service

ScopeAnmeldungSeit
domains:manageKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
yearsbodyintegerJa

POST /services/{id}/restart

Restart a service

ScopeAnmeldungSeit
services:manageKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

POST /services/{id}/resume

Withdraw a cancellation request, or undo a scheduled cancellation

ScopeAnmeldungSeit
services:manageKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

GET /services/{id}/transfer-code

Read the transfer (EPP) code for an unlocked domain

ScopeAnmeldungSeit
domains:manageKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

storefront

GET /storefront/domain-prices

List TLD registration and transfer prices

ScopeAnmeldungSeit
catalog:readNur Schlüssel2026-09

GET /storefront/embed

Read the embed origin, feature toggles and brand theme for this tenant

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
brandquerystringUsed only where the store has saved no value of its own.
accentquerystringUsed only where the store has saved no value of its own.
onAccentquerystringUsed only where the store has saved no value of its own.
backgroundquerystringUsed only where the store has saved no value of its own.
surfacequerystringUsed only where the store has saved no value of its own.
textquerystringUsed only where the store has saved no value of its own.
mutedquerystringUsed only where the store has saved no value of its own.
linequerystringUsed only where the store has saved no value of its own.
dangerquerystringUsed only where the store has saved no value of its own.
successquerystringUsed only where the store has saved no value of its own.
radiusquerystringUsed only where the store has saved no value of its own.
fontquerystringUsed only where the store has saved no value of its own.
headingFontquerystringUsed only where the store has saved no value of its own.
densityquerystringUsed only where the store has saved no value of its own.
schemequerystringUsed only where the store has saved no value of its own.
logoUrlquerystringUsed only where the store has saved no value of its own.

GET /storefront/i18n

Languages this store serves, plus the packs installed on the platform

ScopeAnmeldungSeit
catalog:readNur Schlüssel2026-09

POST /storefront/inquiries

Submit a public contact enquiry

ScopeAnmeldungSeit
inquiries:writeNur Schlüssel2026-09
NameInTypErforderlichHinweise
Idempotency-KeyheaderstringA unique value per intended request (1 to 200 of A-Z a-z 0-9 - _). A retry with the same key and body returns the first answer instead of repeating the work.
_honeybodystringA hidden trap field; leave it empty. A filled one is dropped as spam.
emailbodystring emailJa
messagebodystringJa
namebodystringJa
phonebodystring
sourcebodycontact / inquiry
subjectbodystringJa

GET /storefront/modules

List the platform's module library

ScopeAnmeldungSeit
catalog:readNur Schlüssel2026-10
NameInTypErforderlichHinweise
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

GET /storefront/modules/{name}

Read one module with its guide

ScopeAnmeldungSeit
catalog:readNur Schlüssel2026-10
NameInTypErforderlichHinweise
namepathstringJa
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.

GET /storefront/page/{key}

Read a published Customizer page by key

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
keypathstringJa

GET /storefront/payment-gateways

List enabled payment methods; credentials are never exposed

ScopeAnmeldungSeit
payment-methods:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
localequerystringLanguage code, e.g. en or de. Names and messages come back in it.
currencyquerystringISO 4217 code the buyer is charged in; an unserved one falls back to the store's main currency.
baseTotalCentsquerystringThe basket total in the store's main currency, for method rules.
countryquerystringThe buyer's country code, for method rules.
familiesquerystringComma list of the basket's product families, for method rules.

GET /storefront/products

List active products, optionally by category

ScopeAnmeldungSeit
catalog:readNur Schlüssel2026-09
NameInTypErforderlichHinweise
categoryquerystringCategory slug.

GET /storefront/redirects

List the storefront's configured URL redirects

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09

GET /storefront/settings

Read public checkout settings including VAT percent

ScopeAnmeldungSeit
catalog:readNur Schlüssel2026-09

GET /storefront/theme

Read the active theme, menus and footer for rendering

ScopeAnmeldungSeit
content:readNur Schlüssel2026-09

tickets

GET /tickets

List the customer's tickets

ScopeAnmeldungSeit
tickets:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
statusquerystring
departmentquerystringDepartment id.

POST /tickets

Open a support ticket

ScopeAnmeldungSeit
tickets:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
Idempotency-KeyheaderstringA unique value per intended request (1 to 200 of A-Z a-z 0-9 - _). A retry with the same key and body returns the first answer instead of repeating the work.
bodybodystringJa
departmentIdbodystringJa
paidbodybooleanAn URGENT ticket needs a paid ticket credit: without paid, it is refused.
prioritybodyLOW / NORMAL / HIGH / URGENT
serviceIdbodystring
subjectbodystringJa

GET /tickets/{id}

Read one ticket with its replies

ScopeAnmeldungSeit
tickets:readKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

POST /tickets/{id}/attachments

Attach files to a ticket or reply

ScopeAnmeldungSeit
tickets:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

POST /tickets/{id}/close

Close a ticket

ScopeAnmeldungSeit
tickets:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa

POST /tickets/{id}/replies

Reply to a ticket

ScopeAnmeldungSeit
tickets:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
idpathstringJa
bodybodystringJa
internalbodybooleanStaff notes only; a customer who sets it is refused.

GET /tickets/departments

List the departments a ticket can be opened against

ScopeAnmeldungSeit
tickets:readKundenanmeldung2026-09

users

GET /users/me

Read the signed-in customer's profile

ScopeAnmeldungSeit
customers:readKundenanmeldung2026-09

PATCH /users/me

Update the signed-in customer's profile

ScopeAnmeldungSeit
customers:writeKundenanmeldung2026-09
NameInTypErforderlichHinweise
addressbodyobject
countryCodebodystring
currencybodystringThe currency the customer is billed in; must be one the store lists.
customerTypebodyINDIVIDUAL / BUSINESS
emailbodystring email
localebodystring
namebodystring
phonebodystring
vatIdbodystring

GET /users/me/export

Export everything this account holds

ScopeAnmeldungSeit
customers:readKundenanmeldung2026-09