Ein klarer Übergabepunkt zwischen Software und echter Post.
Die API nimmt Briefbestellungen entgegen — sie löst keine automatische Produktion und keinen automatischen Versand aus. Yonek prüft jede neue Bestellung im Business-Portal und steuert den weiteren Status bewusst.
API-Keys gehören nie in Browser-Code, Apps oder öffentlich ausgelieferte Automationen. Rufen Sie die API von Ihrem Backend auf.
Authentifizierung
Jeder Kunde erhält einen eigenen Live-Schlüssel. Der vollständige Schlüssel wird bei der Erstellung genau einmal angezeigt und serverseitig ausschließlich als SHA-256-Hash gespeichert.
Authorization: Bearer yk_live_… - Schlüssel ausschließlich in einem Secret Store oder einer geschützten Umgebungsvariable speichern.
- Für Test, Produktion und getrennte Anwendungen jeweils eigene Schlüssel verwenden.
- Bei Verdacht auf Offenlegung den Schlüssel sofort über Yonek widerrufen lassen.
Die erste Bestellung
Ein API-Key, ein stabiler Idempotency-Key und ein JSON-Body genügen.
curl --request POST 'https://yonek.de/api/brief/v1/orders' \
--header 'Authorization: Bearer yk_live_…' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: crm-4711-2026-09-26' \
--data '{
"external_id": "crm-4711",
"recipient": {
"name": "Max Mustermann",
"company": "Muster GmbH",
"address_line1": "Musterstraße 1",
"postal_code": "12345",
"city": "Berlin",
"country": "DE"
},
"letter": {
"salutation": "Sehr geehrter Herr Mustermann,",
"text": "vielen Dank für das angenehme Gespräch. Wie angekündigt erhalten Sie heute unsere Unterlagen."
},
"sender_reference": "CRM-4711",
"context": "Interne Zusatzinformation zur manuellen Prüfung.",
"metadata": {
"campaign": "herbst-2026",
"account_owner": "roman"
}
}' JavaScript-Beispiel anzeigen
const response = await fetch('https://yonek.de/api/brief/v1/orders', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.YONEK_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': 'crm-4711-2026-09-26'
},
body: JSON.stringify({
external_id: 'crm-4711',
recipient: {
name: 'Max Mustermann',
company: 'Muster GmbH',
address_line1: 'Musterstraße 1',
postal_code: '12345',
city: 'Berlin',
country: 'DE'
},
letter: {
text: 'Vielen Dank für das angenehme Gespräch. Wie angekündigt erhalten Sie heute unsere Unterlagen.'
}
})
});
const result = await response.json();
if (!response.ok) throw new Error(result.error); Erfolgreiche Antwort 201 Created
{
"order_id": "bo_mg12abc_1a2b3c4d",
"status": "received",
"status_url": "/api/brief/v1/orders/bo_mg12abc_1a2b3c4d",
"duplicate": false
} POST /orders
Legt eine neue Bestellung im Status received an. Der Endpunkt erwartet UTF-8-kodiertes JSON.
Header
| Name | Pflicht | Beschreibung |
|---|---|---|
Authorization | Ja | Bearer <API_KEY> |
Content-Type | Ja | application/json |
Idempotency-Key | Ja | 8–128 Zeichen; erlaubt: Buchstaben, Ziffern, Punkt, Unterstrich, Doppelpunkt und Bindestrich. |
Request-Body
| Feld | Typ | Pflicht | Regel |
|---|---|---|---|
external_id | string | Ja | Ihre Bestell- oder CRM-ID, max. 100 Zeichen. |
recipient | object | Ja | Empfänger und Postanschrift. |
recipient.name | string | bedingt | Name, max. 160 Zeichen. Name oder Firma muss gesetzt sein. |
recipient.company | string | bedingt | Firma, max. 160 Zeichen. Name oder Firma muss gesetzt sein. |
recipient.address_line1 | string | Ja | Straße und Hausnummer, max. 180 Zeichen. |
recipient.address_line2 | string | Nein | Adresszusatz, max. 180 Zeichen. |
recipient.postal_code | string | Ja | Postleitzahl, max. 20 Zeichen. |
recipient.city | string | Ja | Ort, max. 120 Zeichen. |
recipient.country | string | Nein | Land oder Ländercode, Standard DE, max. 80 Zeichen. |
letter.text | string | Ja | Finaler Brieftext, 10–10.000 Zeichen. |
letter.salutation | string | Nein | Separate Anrede, max. 160 Zeichen. |
sender_reference | string | Nein | Interne Referenz, max. 160 Zeichen. |
context | string | Nein | Zusatzinformation zur manuellen Prüfung, max. 3.000 Zeichen. |
metadata | object | Nein | Bis zu 20 String-Werte; Schlüssel max. 50, Werte max. 500 Zeichen. |
context ist kein Prompt.Das Feld wird als unvertrauenswürdige Kundeneingabe gespeichert und angezeigt, aber niemals automatisch von einem LLM verarbeitet.
GET /orders/{order_id}
Ruft den aktuellen Status einer Bestellung ab. Ein API-Key sieht ausschließlich Bestellungen, die mit demselben Schlüssel angelegt wurden.
curl 'https://yonek.de/api/brief/v1/orders/bo_mg12abc_1a2b3c4d' \
-H 'Authorization: Bearer yk_live_…' {
"order_id": "bo_mg12abc_1a2b3c4d",
"external_id": "crm-4711",
"status": "production",
"status_text": "In Produktion",
"created_at": "2026-09-26T09:30:00.000Z",
"updated_at": "2026-09-26T11:15:00.000Z",
"sent_at": null,
"estimated_delivery_at": null,
"delivered_at": null,
"delivery_estimate_passed": false,
"request_id": "req_muidwm02_1709e62e0f"
} Statusmodell
receivedEingegangenDie Bestellung wurde gespeichert und wartet auf die Prüfung.
in_reviewIn PrüfungYonek prüft Inhalt, Adresse und Umsetzbarkeit.
acceptedAngenommenDie Bestellung wurde zur Produktion freigegeben.
productionIn ProduktionDer Brief wird vorbereitet und geschrieben.
sentVersendetDer Brief wurde an den Versand übergeben.
deliveredAngekommenVon Yonek bewusst als angekommen bestätigt; keine automatische Statusänderung.
rejectedAbgelehntDie Bestellung kann nicht umgesetzt werden.
cancelledStorniertDie Bestellung wurde storniert.
estimated_delivery_at ist eine operative Prognose von zwei Werktagen. Der Status bleibt sent, bis Yonek die Ankunft bewusst bestätigt. delivery_estimate_passed zeigt lediglich, ob die Prognose überschritten wurde.
Signierte Webhooks
Optional sendet Yonek die Ereignisse order.created und order.status_changed an Ihren HTTPS-Endpunkt. Zustellungen laufen über eine persistente Outbox und werden bei Fehlern mit exponentiellem Backoff bis zu zehnmal wiederholt.
{
"id": "wh_muidx1_1a2b3c4d",
"event": "order.status_changed",
"created_at": "2026-09-26T12:30:00.000Z",
"data": {
"order_id": "bo_mg12abc_1a2b3c4d",
"external_id": "crm-4711",
"status": "sent",
"created_at": "2026-09-26T09:30:00.000Z",
"updated_at": "2026-09-26T12:30:00.000Z",
"sent_at": "2026-09-26T12:30:00.000Z",
"estimated_delivery_at": "2026-09-30T12:30:00.000Z",
"delivered_at": null
}
} Signatur prüfen
Der Header Yonek-Signature hat das Format t=UNIXZEIT,v1=HMAC. Berechnen Sie HMAC-SHA-256 über t + "." + rawBody mit Ihrem einmalig angezeigten Webhook-Secret und vergleichen Sie timing-sicher. Lehnen Sie Zeitstempel ab, die deutlich älter als fünf Minuten sind.
Nur öffentliche HTTPS-Ziele sind erlaubt. Lokale und private IP-Bereiche, eingebettete Zugangsdaten sowie Weiterleitungen werden blockiert.
Idempotenz & Retries
Verwenden Sie pro logischer Bestellung einen stabilen Idempotency-Key. Senden Sie denselben Request bei Timeout oder unklarer Netzwerkantwort mit demselben Key erneut.
duplicate: false
Bestellung und Benachrichtigung werden einmal angelegt.
duplicate: true
Die bestehende Bestell-ID wird zurückgegeben.
Die Idempotenz gilt innerhalb eines API-Keys. Derselbe Key kann bei zwei verschiedenen Kundenschlüsseln jeweils eine Bestellung erzeugen.
Limits
Ihre tatsächlich konfigurierten Grenzen und den vereinbarten Preis erhalten Sie authentifiziert über:
/infoVersion · Endpunkte · Limits · Statuswerte · PreisRate-Limits werden dauerhaft gespeichert und überstehen Neustarts. Antworten enthalten X-RateLimit-Limit, X-RateLimit-Remaining und X-RateLimit-Reset. Bei 429 kommt zusätzlich Retry-After. Tageskontingente werden um 00:00 UTC zurückgesetzt.
Fehlercodes
Fehler werden konsistent als JSON mit den Feldern error, message und request_id ausgegeben. Validierungsfehler enthalten zusätzlich details. Unbekannte Nutzdatenfelder werden bewusst abgelehnt.
| HTTP | error | Bedeutung |
|---|---|---|
| 400 | invalid_json | Der Request-Body ist kein gültiges JSON. |
| 400 | invalid_idempotency_key | Der Idempotency-Key fehlt oder erfüllt das Format nicht. |
| 408 | request_timeout | Der Request-Body wurde nicht innerhalb von zehn Sekunden übertragen. |
| 401 | invalid_api_key | Bearer-Key fehlt, ist ungültig oder wurde widerrufen. |
| 401 | api_key_revoked | Der Key wurde während der Verarbeitung widerrufen. |
| 404 | order_not_found | Bestellung existiert nicht oder gehört zu einem anderen API-Key. |
| 405 | method_not_allowed | Die HTTP-Methode ist für den Endpunkt nicht erlaubt. |
| 413 | payload_too_large | Der Request überschreitet 32 KB. |
| 415 | content_type_must_be_application_json | Beim Erstellen fehlt Content-Type: application/json. |
| 422 | validation_failed | Ein oder mehrere Nutzdatenfelder sind ungültig. |
| 429 | rate_limit_exceeded | Minutenlimit für IP oder API-Key erreicht. |
| 429 | daily_limit_exceeded | Tageskontingent des API-Keys erreicht. |
{
"error": "validation_failed",
"details": [
"recipient.address_line1 fehlt",
"letter.text muss mindestens 10 Zeichen enthalten"
]
} Sicherheit & Datenverarbeitung
Schlüssel
Kundenspezifisch, sofort widerrufbar und im System nur als Hash gespeichert.
Mandantentrennung
Statusabrufe sind an den API-Key gebunden; fremde Bestellungen erscheinen als nicht gefunden.
Eingaben
Strikte Größen- und Feldlimits, bereinigte Steuerzeichen und keine automatische LLM-Verarbeitung.
Transaktionen & Audit
Bestellungen, Idempotenz und Kontingente werden atomar in SQLite/WAL gespeichert; administrative Änderungen landen im Audit-Log.
Empfohlene Integrationspraxis
- 1Vor dem Senden validieren
Adresse, Empfänger und finalen Brieftext bereits in Ihrem System prüfen.
- 2Secrets serverseitig halten
API-Key nie loggen, an Clients senden oder in Quellcode einchecken.
- 3Retries begrenzen
Bei
429denRetry-After-Header beachten, sonst exponentielles Backoff verwenden. - 4IDs speichern
order_id,external_idund Idempotency-Key gemeinsam in Ihrer Datenbank ablegen. - 5Status moderat pollen
Für normale Abläufe reichen wenige Statusabfragen pro Tag.
Bereit für den ersten echten Brief?
Wir richten Kundenschlüssel, Kontingent und Preis passend zu Ihrem Workflow ein und begleiten den ersten Testlauf.