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 willst | Lies |
|---|
| Ein JS-SDK oder Widgets zum Einbauen | sdk-widgets.md |
| Ereignis-Benachrichtigungen an deinen Server | webhooks.md |
| Jede Route, auch die internen | api-endpoints.md |
| Wie der Vertrag in der CI durchgesetzt wird | api-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 belegt | Wo es liegt |
|---|
API-Schlüssel (Teculiar-Key) | welche Website aufruft | pk_ im Quelltext der Seite, sk_ auf deinem Server |
Kunden-Token (Authorization: Bearer) | zu welcher Person die Daten gehören | von /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).
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
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.
| Methode | Pfad | Scope | Was sie tut |
|---|
GET | /tickets | tickets:read | Listet die Tickets des angemeldeten Kunden. Optional ?status= und ?department=. |
POST | /tickets | tickets:write | Eröffnet ein Ticket. Pflicht: subject, body, departmentId. Optional: priority, serviceId. |
GET | /tickets/departments | tickets:read | Listet die Abteilungen, bei denen ein Ticket eröffnet werden kann (id/slug/name/colour). |
GET | /tickets/{id} | tickets:read | Liest 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}/replies | tickets:write | Antwortet auf ein Ticket. Das Feld internal wird bei Kunden-Tokens ignoriert. |
POST | /tickets/{id}/attachments | tickets:write | Hä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}/close | tickets:write | Schließ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:
| Klasse | Route | Scope | Hinweise |
|---|
| lesen | GET /services/:id/actions/:action | services:read | |
| umkehrbar | POST /services/:id/actions/:action (bei einer Registrierungsstelle: POST /services/:id/domain-actions/:action) | services:manage (domains:manage) | 30 pro Minute |
| geheim | wie umkehrbar | wie umkehrbar | nie 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.
| Modul | Veröffentlicht |
|---|
| Hetzner Cloud | power-on, reboot, shutdown, power-off (umkehrbar) |
| cPanel | sso (Feld app: files, email, apps), webmail: Single-Use-Anmeldelinks (geheim) |
| Resell.biz, Dynadot | lock-state (lesen), lock (umkehrbar), unlock (geheim: gibt den Transfer-Code zurück) |
| Virtualmin | nichts |
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ür | Schlüssel |
|---|
| Eine Domain-Position | billingCycle, domainAction, extraAttributes, nameServers, transferAuthCode |
| Eine Hosting-Position und ihre Domain | bundledDomain, domainName, domainUse |
| Ein Teculiar-Tarif | adminEmail, 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:
| Status | Bedeutung |
|---|
PENDING | Angelegt und wartet darauf, dass die Zahlung eingeht, oder darauf, dass das Hosting-Modul die Einrichtung bestätigt. |
PROVISIONING | Die 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.) |
ACTIVE | Läuft normal. |
SUSPENDED | Wegen Nichtzahlung gesperrt. Wird automatisch wiederhergestellt, wenn die überfällige Rechnung beglichen ist. |
CANCELLED | Zur Beendigung zum Zeitpunkt cancelAt vorgemerkt oder bereits gekündigt. Verlängerungsrechnungen enden. |
TERMINATED | Dauerhaft abgebaut. Das Konto wurde beim Anbieter entfernt. |
FAILED | Die 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.
| Status | Bedeutet |
|---|
400 | Die Anfrage ist fehlerhaft, oder eine Rückkehr-URL hat die Prüfung nicht bestanden. |
401 | Schlüssel fehlt oder ist ungültig, oder ein Kunden-Token ist nötig und fehlt. |
403 | Dem Schlüssel fehlt der Scope, oder die Origin ist nicht erlaubt. |
404 | Nicht veröffentlicht oder tatsächlich nicht vorhanden, bewusst nicht unterscheidbar. |
409 | Eine Zahlung für diese Rechnung läuft bereits, oder die Rechnung wurde inzwischen bezahlt. |
429 | Rate-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
| Scope | Anmeldung | Seit |
|---|
customers:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
password | body | string | Ja | |
GET /auth/2fa/setup
Begin two-factor setup
| Scope | Anmeldung | Seit |
|---|
customers:write | Kundenanmeldung | 2026-09 |
GET /auth/2fa/status
Whether two-factor is enabled
| Scope | Anmeldung | Seit |
|---|
customers:read | Kundenanmeldung | 2026-09 |
POST /auth/2fa/verify
Confirm two-factor setup
| Scope | Anmeldung | Seit |
|---|
customers:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
code | body | string | Ja | |
POST /auth/login
Sign a customer in and return bearer tokens
| Scope | Anmeldung | Seit |
|---|
auth:write | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
email | body | string email | Ja | |
password | body | string | Ja | |
scope | body | admin / client | | |
totpCode | body | string | | The six digits from the authenticator, when two-factor is on. |
POST /auth/logout
Revoke a refresh token
| Scope | Anmeldung | Seit |
|---|
auth:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
refreshToken | body | string | Ja | |
POST /auth/password-reset/confirm
Complete a password reset
| Scope | Anmeldung | Seit |
|---|
auth:write | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
password | body | string | Ja | |
token | body | string | Ja | |
POST /auth/password-reset/request
Request a password-reset email
| Scope | Anmeldung | Seit |
|---|
auth:write | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
email | body | string email | Ja | |
scope | body | admin / client | | |
POST /auth/refresh
Exchange a refresh token for a new access token
| Scope | Anmeldung | Seit |
|---|
auth:write | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
refreshToken | body | string | Ja | |
POST /auth/register
Register a customer account
| Scope | Anmeldung | Seit |
|---|
auth:write | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
address | body | object | | |
companyName | body | string | | |
countryCode | body | string | | |
customerType | body | INDIVIDUAL / BUSINESS | | |
email | body | string email | Ja | |
name | body | string | Ja | |
password | body | string | Ja | |
phone | body | string | | |
vatId | body | string | | |
billing
POST /billing/add-funds
Top up the customer's account balance
| Scope | Anmeldung | Seit |
|---|
payments:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
Idempotency-Key | header | string | | A 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. |
amountCents | body | integer | Ja | |
iban | body | string | | |
method | body | CREDIT_CARD / PAYPAL / SEPA / CRYPTO / BANK_TRANSFER / IDEAL | Ja | |
GET /billing/invoices
List the customer's invoices
| Scope | Anmeldung | Seit |
|---|
invoices:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
status | query | string | | Only invoices in this status. |
GET /billing/invoices/{id}
Read one invoice
| Scope | Anmeldung | Seit |
|---|
invoices:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
GET /billing/invoices/{id}/html
Render an invoice as HTML
| Scope | Anmeldung | Seit |
|---|
invoices:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
POST /billing/invoices/{id}/pay
Pay an invoice
| Scope | Anmeldung | Seit |
|---|
invoices:pay | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
Idempotency-Key | header | string | | A 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. |
cancelUrl | body | string | | Where the buyer ends up after a cancelled payment. Same rule as returnUrl. |
iban | body | string | | |
method | body | CREDIT_CARD / PAYPAL / SEPA / CRYPTO / BANK_TRANSFER / IDEAL | Ja | |
paymentMethodId | body | string | | |
returnUrl | body | string | | Where 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. |
useAccountBalance | body | boolean | | |
GET /billing/invoices/{id}/payment-methods
List the payment methods that can pay one invoice
| Scope | Anmeldung | Seit |
|---|
invoices:pay | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
GET /billing/invoices/{id}/pdf
Download an invoice PDF
| Scope | Anmeldung | Seit |
|---|
invoices:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
GET /billing/payment-methods
List the customer's saved payment methods
| Scope | Anmeldung | Seit |
|---|
payment-methods:read | Kundenanmeldung | 2026-09 |
cms
GET /cms/announcements
List announcements for the signed-in customer
| Scope | Anmeldung | Seit |
|---|
customers:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
POST /cms/announcements/{id}/hide
Hide an announcement for this customer
| Scope | Anmeldung | Seit |
|---|
customers:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
POST /cms/announcements/{id}/read
Mark an announcement as read
| Scope | Anmeldung | Seit |
|---|
customers:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
GET /cms/blog-categories
List blog categories
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
GET /cms/blog-tags
List blog tags
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
GET /cms/pages/{locale}/{slug}
Read a published CMS page
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
locale | path | string | Ja | |
slug | path | string | Ja | |
GET /cms/post-tags
List blog post tags
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
limit | query | string | | How many tags, most used first. |
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
GET /cms/posts
List published blog posts
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
tag | query | string | | Tag slug. |
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
GET /cms/posts/{locale}/{slug}
Read a published blog post
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
locale | path | string | Ja | |
slug | path | string | Ja | |
domains
GET /domains/search
Check domain availability and price
| Scope | Anmeldung | Seit |
|---|
domains:search | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
domain | query | string | Ja | The name to look up, with its extension. |
years | query | string | | Registration years to price. |
suggestions | query | true / false | | false skips the other-extension suggestions (default true). |
knowledgebase
GET /knowledgebase
Read published knowledgebase articles
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
GET /knowledgebase/{slug}
Read published knowledgebase articles
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
slug | path | string | Ja | |
GET /knowledgebase/suggest
Read published knowledgebase articles
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
q | query | string | | What the visitor has typed so far. |
orders
GET /orders/{id}
Read one order
| Scope | Anmeldung | Seit |
|---|
orders:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
POST /orders/{id}/pay
Pay for an order
| Scope | Anmeldung | Seit |
|---|
payments:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
Idempotency-Key | header | string | | A 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. |
cancelUrl | body | string | | Where the buyer ends up after a cancelled payment. Same rule as returnUrl. |
iban | body | string | | |
method | body | CREDIT_CARD / PAYPAL / SEPA / CRYPTO / BANK_TRANSFER / IDEAL | Ja | |
paymentMethodId | body | string | Ja | |
returnUrl | body | string | | Where 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
| Scope | Anmeldung | Seit |
|---|
checkout:write | Nur Schlüssel | 2026-10 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
token | query | string | Ja | The statusToken that checkout answered with. |
POST /orders/checkout
Place an order (guest or signed-in)
| Scope | Anmeldung | Seit |
|---|
checkout:write | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
Idempotency-Key | header | string | | A 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. |
currency | body | string | | ISO 4217 code the buyer is charged in; an unserved one falls back to the store's main currency. |
customer | body | object | Ja | |
customer.address | body | object | | |
customer.companyName | body | string | | |
customer.countryCode | body | string | | |
customer.customerType | body | INDIVIDUAL / BUSINESS | | |
customer.email | body | string email | Ja | |
customer.name | body | string | Ja | |
customer.password | body | string | | For a new account. |
customer.phone | body | string | | |
customer.vatId | body | string | | |
items | body | object[] | Ja | |
items[].addOnIds | body | string[] | | Add-ons, one of each. addOns wins when both are sent. |
items[].addOns | body | object[] | | Add-ons with how many of each. |
items[].addOns[].addOnId | body | string | | |
items[].addOns[].quantity | body | integer | | |
items[].configuration | body | object | | Values for the product's order fields. |
items[].domainName | body | string | | For a domain line: the name to register or transfer. |
items[].productId | body | string | | |
items[].productPriceId | body | string | | The price (billing cycle) to buy; the cheapest is used when absent. |
items[].quantity | body | integer | | |
locale | body | string | | Language code, e.g. en or de. Names and messages come back in it. |
POST /orders/preview
Price an order server-side before checkout
| Scope | Anmeldung | Seit |
|---|
checkout:write | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
currency | body | string | | ISO 4217 code the buyer is charged in; an unserved one falls back to the store's main currency. |
customer | body | object | | Only what a price depends on. |
customer.countryCode | body | string | | |
customer.customerType | body | INDIVIDUAL / BUSINESS | | |
customer.vatId | body | string | | |
items | body | object[] | Ja | |
items[].addOnIds | body | string[] | | Add-ons, one of each. addOns wins when both are sent. |
items[].addOns | body | object[] | | Add-ons with how many of each. |
items[].addOns[].addOnId | body | string | | |
items[].addOns[].quantity | body | integer | | |
items[].configuration | body | object | | Values for the product's order fields. |
items[].domainName | body | string | | For a domain line: the name to register or transfer. |
items[].productId | body | string | | |
items[].productPriceId | body | string | | The price (billing cycle) to buy; the cheapest is used when absent. |
items[].quantity | body | integer | | |
locale | body | string | | Language code, e.g. en or de. Names and messages come back in it. |
GET /platform/domains/check
Check whether a domain can be connected to a new system
| Scope | Anmeldung | Seit |
|---|
catalog:read | Nur Schlüssel | 2026-10 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
host | query | string | | The domain to check, as typed (besthost.com). |
Check whether a system name can be claimed
| Scope | Anmeldung | Seit |
|---|
catalog:read | Nur Schlüssel | 2026-10 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
name | query | string | | The system name to check, as typed. |
List the Teculiar plans with their prices, limits and features
| Scope | Anmeldung | Seit |
|---|
catalog:read | Nur Schlüssel | 2026-10 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
products
GET /products
List products
| Scope | Anmeldung | Seit |
|---|
catalog:read | Nur Schlüssel | 2026-09 |
services
GET /services
List the customer's services
| Scope | Anmeldung | Seit |
|---|
services:read | Kundenanmeldung | 2026-09 |
GET /services/{id}
Read one service, including its domain records
| Scope | Anmeldung | Seit |
|---|
services:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
GET /services/{id}/actions
List the module actions a service offers
| Scope | Anmeldung | Seit |
|---|
services:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
GET /services/{id}/actions/{action}
Run a read action of a service's module
| Scope | Anmeldung | Seit |
|---|
services:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
action | path | string | Ja | |
POST /services/{id}/actions/{action}
Run an action of a service's module
| Scope | Anmeldung | Seit |
|---|
services:manage | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
action | path | string | Ja | |
PATCH /services/{id}/auto-renew
Turn a service's auto-renew on or off
| Scope | Anmeldung | Seit |
|---|
services:manage | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
autoRenew | body | boolean | Ja | |
POST /services/{id}/cancel
Request cancellation of a service
| Scope | Anmeldung | Seit |
|---|
services:manage | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
cancelDomains | body | boolean | | Also cancel the domains on the service. |
reason | body | string | | |
reasonText | body | string | | |
timing | body | string | | When the cancellation takes effect. |
GET /services/{id}/cancellation-request
The cancellation request waiting on a service, if any
| Scope | Anmeldung | Seit |
|---|
services:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
POST /services/{id}/change-plan
Change a service's plan
| Scope | Anmeldung | Seit |
|---|
services:manage | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
productPriceId | body | string | | The price to move to, from plan-options. |
POST /services/{id}/domain-actions/{action}
Run an action of a domain's registrar module
| Scope | Anmeldung | Seit |
|---|
domains:manage | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
action | path | string | Ja | |
GET /services/{id}/domain-lock
Read the transfer lock on a domain service
| Scope | Anmeldung | Seit |
|---|
services:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
POST /services/{id}/domain-lock
Lock or unlock a domain for transfer
| Scope | Anmeldung | Seit |
|---|
domains:manage | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
locked | body | boolean | Ja | |
GET /services/{id}/plan-options
Plans this service can change to
| Scope | Anmeldung | Seit |
|---|
services:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
POST /services/{id}/renew-domain
Renew the domain attached to a service
| Scope | Anmeldung | Seit |
|---|
domains:manage | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
years | body | integer | Ja | |
POST /services/{id}/restart
Restart a service
| Scope | Anmeldung | Seit |
|---|
services:manage | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
POST /services/{id}/resume
Withdraw a cancellation request, or undo a scheduled cancellation
| Scope | Anmeldung | Seit |
|---|
services:manage | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
GET /services/{id}/transfer-code
Read the transfer (EPP) code for an unlocked domain
| Scope | Anmeldung | Seit |
|---|
domains:manage | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
storefront
GET /storefront/domain-prices
List TLD registration and transfer prices
| Scope | Anmeldung | Seit |
|---|
catalog:read | Nur Schlüssel | 2026-09 |
GET /storefront/embed
Read the embed origin, feature toggles and brand theme for this tenant
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
brand | query | string | | Used only where the store has saved no value of its own. |
accent | query | string | | Used only where the store has saved no value of its own. |
onAccent | query | string | | Used only where the store has saved no value of its own. |
background | query | string | | Used only where the store has saved no value of its own. |
surface | query | string | | Used only where the store has saved no value of its own. |
text | query | string | | Used only where the store has saved no value of its own. |
muted | query | string | | Used only where the store has saved no value of its own. |
line | query | string | | Used only where the store has saved no value of its own. |
danger | query | string | | Used only where the store has saved no value of its own. |
success | query | string | | Used only where the store has saved no value of its own. |
radius | query | string | | Used only where the store has saved no value of its own. |
font | query | string | | Used only where the store has saved no value of its own. |
headingFont | query | string | | Used only where the store has saved no value of its own. |
density | query | string | | Used only where the store has saved no value of its own. |
scheme | query | string | | Used only where the store has saved no value of its own. |
logoUrl | query | string | | Used 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
| Scope | Anmeldung | Seit |
|---|
catalog:read | Nur Schlüssel | 2026-09 |
POST /storefront/inquiries
Submit a public contact enquiry
| Scope | Anmeldung | Seit |
|---|
inquiries:write | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
Idempotency-Key | header | string | | A 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. |
_honey | body | string | | A hidden trap field; leave it empty. A filled one is dropped as spam. |
email | body | string email | Ja | |
message | body | string | Ja | |
name | body | string | Ja | |
phone | body | string | | |
source | body | contact / inquiry | | |
subject | body | string | Ja | |
GET /storefront/modules
List the platform's module library
| Scope | Anmeldung | Seit |
|---|
catalog:read | Nur Schlüssel | 2026-10 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
GET /storefront/modules/{name}
Read one module with its guide
| Scope | Anmeldung | Seit |
|---|
catalog:read | Nur Schlüssel | 2026-10 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
name | path | string | Ja | |
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
GET /storefront/page/{key}
Read a published Customizer page by key
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
key | path | string | Ja | |
GET /storefront/payment-gateways
List enabled payment methods; credentials are never exposed
| Scope | Anmeldung | Seit |
|---|
payment-methods:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
locale | query | string | | Language code, e.g. en or de. Names and messages come back in it. |
currency | query | string | | ISO 4217 code the buyer is charged in; an unserved one falls back to the store's main currency. |
baseTotalCents | query | string | | The basket total in the store's main currency, for method rules. |
country | query | string | | The buyer's country code, for method rules. |
families | query | string | | Comma list of the basket's product families, for method rules. |
GET /storefront/products
List active products, optionally by category
| Scope | Anmeldung | Seit |
|---|
catalog:read | Nur Schlüssel | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
category | query | string | | Category slug. |
GET /storefront/redirects
List the storefront's configured URL redirects
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
GET /storefront/settings
Read public checkout settings including VAT percent
| Scope | Anmeldung | Seit |
|---|
catalog:read | Nur Schlüssel | 2026-09 |
GET /storefront/theme
Read the active theme, menus and footer for rendering
| Scope | Anmeldung | Seit |
|---|
content:read | Nur Schlüssel | 2026-09 |
tickets
GET /tickets
List the customer's tickets
| Scope | Anmeldung | Seit |
|---|
tickets:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
status | query | string | | |
department | query | string | | Department id. |
POST /tickets
Open a support ticket
| Scope | Anmeldung | Seit |
|---|
tickets:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
Idempotency-Key | header | string | | A 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. |
body | body | string | Ja | |
departmentId | body | string | Ja | |
paid | body | boolean | | An URGENT ticket needs a paid ticket credit: without paid, it is refused. |
priority | body | LOW / NORMAL / HIGH / URGENT | | |
serviceId | body | string | | |
subject | body | string | Ja | |
GET /tickets/{id}
Read one ticket with its replies
| Scope | Anmeldung | Seit |
|---|
tickets:read | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
POST /tickets/{id}/attachments
Attach files to a ticket or reply
| Scope | Anmeldung | Seit |
|---|
tickets:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
POST /tickets/{id}/close
Close a ticket
| Scope | Anmeldung | Seit |
|---|
tickets:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
POST /tickets/{id}/replies
Reply to a ticket
| Scope | Anmeldung | Seit |
|---|
tickets:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
id | path | string | Ja | |
body | body | string | Ja | |
internal | body | boolean | | Staff notes only; a customer who sets it is refused. |
GET /tickets/departments
List the departments a ticket can be opened against
| Scope | Anmeldung | Seit |
|---|
tickets:read | Kundenanmeldung | 2026-09 |
users
GET /users/me
Read the signed-in customer's profile
| Scope | Anmeldung | Seit |
|---|
customers:read | Kundenanmeldung | 2026-09 |
PATCH /users/me
Update the signed-in customer's profile
| Scope | Anmeldung | Seit |
|---|
customers:write | Kundenanmeldung | 2026-09 |
| Name | In | Typ | Erforderlich | Hinweise |
|---|
address | body | object | | |
countryCode | body | string | | |
currency | body | string | | The currency the customer is billed in; must be one the store lists. |
customerType | body | INDIVIDUAL / BUSINESS | | |
email | body | string email | | |
locale | body | string | | |
name | body | string | | |
phone | body | string | | |
vatId | body | string | | |
GET /users/me/export
Export everything this account holds
| Scope | Anmeldung | Seit |
|---|
customers:read | Kundenanmeldung | 2026-09 |
Erzeugt aus Version e39fd835c am 2026-10-22.