Postify

API-Dokumentation

Ein Endpunkt, ein Bearer-Token, Formulardaten hinein, JSON heraus. Ein Brief über die API kostet dieselben 2,00 EUR und wird vom Guthaben des Kontos abgezogen.

API-Schlüssel

Jedes Konto hat einen Schlüssel. Melden Sie sich an, um Ihren zu sehen, oder legen Sie ein Konto an.

Guthaben abfragen

Vor einem Serienlauf abfragen, damit ein Auftrag nicht wegen zwei Euro auf halber Strecke stehen bleibt.

GET https://meinpostify.de/api.php
curl https://meinpostify.de/api.php \
  -H "Authorization: Bearer psty_your_api_key"
200 OK
{
  "ok": true,
  "account": "you@example.com",
  "balance": "18.00",
  "currency": "EUR",
  "letter_price": "2.00",
  "letters_left": 9
}

Brief senden

POST https://meinpostify.de/api.php als multipart/form-data. Den Text in content senden oder eine fertige Datei als document anhängen. Eines von beiden muss dabei sein.

Feld Pflicht Hinweis
recipient ja Name des Empfängers. Höchstens 200 Zeichen.
address ja Vollständige Postanschrift, eine Zeile je Adresszeile. Mindestens 8 Zeichen.
content eines Der Brieftext, bis 20000 Zeichen.
document eines PDF- oder Textdatei, bis 5 MB.
subject nein Betreffzeile über dem Text. Höchstens 200 Zeichen.
template nein Eines von classic, modern, minimal, statement. Standard ist die im Briefkopf gespeicherte Vorlage.
company Briefkopf Name im Briefkopf. Fällt auf den Briefkopf unter /letterhead.php zurück.
sender_address Briefkopf Rücksendezeile und Fußzeile, eine Zeile je Adresszeile.
sender_contact Briefkopf Telefon, E-Mail, Web, Registernummer.
signature Briefkopf Name unter dem Grußformel-Block. Ohne Angabe entfällt der Gruß.
info_rows Briefkopf JSON-Liste aus {label, value} für den Block rechts. {reference} und {date} werden je Brief gefüllt.
var[...] wie deklariert Je Variable aus dem Briefkopf ein Feld. Siehe Variablen weiter unten.
style Briefkopf JSON: family (serif, sans, times, helvetica, mono), body, head, leading, tracking.
POST https://meinpostify.de/api.php
curl https://meinpostify.de/api.php \
  -H "Authorization: Bearer psty_your_api_key" \
  -F "recipient=Ilona Weber" \
  -F $'address=Hauptstraße 3\n50667 Köln' \
  -F "subject=Jahresabschluss 2025" \
  -F "template=modern" \
  -F "content=Sehr geehrte Frau Weber, anbei der Abschluss." \
  -F "document=@statement.pdf"
201 Created
{
  "ok": true,
  "letter_id": 41,
  "reference": "PSTY-000041",
  "recipient": "Ilona Weber",
  "subject": "Jahresabschluss 2025",
  "template": "modern",
  "charged": "2.00",
  "balance": "16.00",
  "status": "sent"
}

Vorlagen

Vier Layouts, schwarz auf weiß. Die Kennung geht in template.

Vorlage Kanzlei, Musterbrief

classic

Zentrierter Serifen-Briefkopf über doppelter Linie. Der Ton einer Kanzlei oder Steuerberatung.

Vorlage Kontor, Musterbrief

modern

Monogramm links, Kontaktspalte rechts, eine Haarlinie. Der alltägliche Geschäftsbrief.

Vorlage Studio, Musterbrief

minimal

Kleine Wortmarke, weite Ränder, keine Linien. Leise und teuer, für Beratungen und Studios.

Vorlage Kontrast, Musterbrief

statement

Schwarzes Kopfband mit Monogramm, passendes Fußband. Wirkt wie ein Bescheid, nicht wie Post.

Variablen

Alles, was Sie im Informationsblock Ihres Briefkopfs in spitze Klammern setzen, etwa <kundennummer>, wird je Brief abgefragt. Die deklarierten Variablen stehen in der Antwort auf GET, eine Anbindung kann sie also auslesen statt zu raten.

GET https://meinpostify.de/api.php
{
  "ok": true,
  "template": "modern",
  "variables": [
    { "key": "kundennummer", "name": "Kundennummer", "field": "var[kundennummer]" },
    { "key": "ihr_schreiben_vom", "name": "Ihr Schreiben vom", "field": "var[ihr_schreiben_vom]" }
  ]
}
POST https://meinpostify.de/api.php
curl https://meinpostify.de/api.php \
  -H "Authorization: Bearer psty_your_api_key" \
  -F "recipient=Ilona Weber" \
  -F "address=Hauptstrasse 3, 50667 Koeln" \
  -F "content=Dear Ms Weber, ..." \
  -F "var[kundennummer]=4711" \
  -F "var[ihr_schreiben_vom]=14 August 2026"

Ein JSON-Objekt in variables geht genauso, ebenso ein flaches Feld var_kundennummer. Eine weggelassene Variable druckt nichts, und eine Zeile, die nur aus dieser Variable bestand, entfällt auf dem Blatt statt leer zu erscheinen. Werte sind auf 120 Zeichen begrenzt.

Aus dem Browser

Der Schlüssel hat die Rechte Ihres Kontos. In einer veröffentlichten Seite kann ihn jeder auslesen und auf Ihre Kosten Briefe versenden. Dort gehört er auf Ihren Server, und der Browser ruft einen eigenen Endpunkt auf, der weiterleitet.

Für einen Test auf dem eigenen Rechner geht es direkt: der Endpunkt antwortet mit Access-Control-Allow-Origin: *, beantwortet den Preflight mit 204 und erlaubt die Header Authorization und Content-Type. Das gilt auch für eine lokale Datei über file://, deren Herkunft null lautet.

Zwei Stolpersteine: credentials: "include" verträgt sich nicht mit dem Stern und wird vom Browser abgelehnt, Cookies braucht die API ohnehin nicht. Und den Content-Type bei FormData nicht selbst setzen, sonst fehlt die multipart-Grenze.

fetch mit FormData
const body = new FormData();
body.set('recipient', 'Ilona Weber');
body.set('address', 'Hauptstraße 3
50667 Köln');
body.set('subject', 'Jahresabschluss 2025');
body.set('content', 'Sehr geehrte Frau Weber, ...');
body.set('var[kundennummer]', '4711');

const response = await fetch('https://meinpostify.de/api.php', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
  body,
});
const letter = await response.json();
fetch mit JSON
const response = await fetch('https://meinpostify.de/api.php', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    recipient: 'Ilona Weber',
    address: 'Hauptstraße 3
50667 Köln',
    content: 'Sehr geehrte Frau Weber, ...',
    var: { kundennummer: '4711' },
  }),
});

Ein JSON-Körper wird gelesen wie Formulardaten. Eine Datei geht nur per multipart/form-data, ein Feld document im JSON wird ignoriert. Ungültiges JSON antwortet mit 400 invalid_json.

Für den echten Betrieb: ein Weiterleiter auf Ihrem Server, der den Schlüssel kennt und den Browser nie sehen lässt. Wichtig dabei, und die häufigste Stolperfalle: eine Seite mit fetch('send_letter.php') muss über einen Server laufen. Per Doppelklick geöffnet steht sie unter file://, dort führt niemand PHP aus, und der Aufruf endet im Verbindungsfehler. Zum Ausprobieren genügt im Ordner der Dateien: php -S localhost:8000, dann http://localhost:8000/index.html aufrufen.

proxy.php auf Ihrem Server
<?php
// Der Schluessel liegt hier, nicht im Browser.
$apiKey = getenv('POSTIFY_KEY');

$curl = curl_init('https://meinpostify.de/api.php');
curl_setopt_array($curl, [
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => [
        'recipient' => $_POST['recipient'] ?? '',
        'address'   => $_POST['address'] ?? '',
        'subject'   => $_POST['subject'] ?? '',
        'content'   => $_POST['content'] ?? '',
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$answer = curl_exec($curl);
http_response_code(curl_getinfo($curl, CURLINFO_HTTP_CODE));
header('Content-Type: application/json');
echo $answer;

Das Druckblatt

Jeder Auftrag, aus der Übersicht wie über die API, erzeugt ein zweiseitiges PDF, das per Mail in die Druckerei geht. Seite 1 ist ein Geschäftsbrief nach DIN 5008 Form A: Briefkopf in der gewählten Vorlage, Rücksendezeile und Anschriftfeld passend für einen Fensterumschlag C5 oder C6, Informationsblock auf Höhe des Empfängers, Betreff, Text, Grußformel und eine Fußzeile mit Ihren vollständigen Absenderangaben. Die Maße stehen fest, unabhängig von Vorlage und Schrift: Anschriftfeld bei 45 mm, 85 mm breit, Informationsblock daneben, Betreff bei 98,46 mm, Faltmarken bei 105 mm und 210 mm. Seite 2 ist der Auftragsbeleg: Auftragsnummer, Vorlage, Typografie, beide Parteien, Betreff, Informationsblock, Variablen, Anlage, Eingangsweg, Konto und der berechnete Betrag.

Hängen Sie ein document an, reist es als zweiter Anhang mit, und Seite 1 vermerkt, dass die Anlage das zu druckende Blatt ist. Ihre hochgeladene Datei wird vom Server gelöscht, sobald die Mail heraus ist.

Statuscodes

Code Antwort Bedeutung
200 Guthabenobjekt Guthaben gelesen.
201 Briefobjekt Berechnet, Druckauftrag ist heraus.
202 Briefobjekt, status failed Berechnet und angenommen, aber die Meldung an die Druckerei kam nicht durch. Sie wird alle zehn Minuten wiederholt, bis sie durchgeht. Nicht erneut senden, der Brief wuerde sonst zweimal gedruckt.
401 missing_bearer_token, invalid_api_key Kein Schlüssel oder ein Schlüssel, den es nicht mehr gibt.
403 email_unverified Die E-Mail-Adresse des Kontos ist nicht bestätigt. Link im Postfach, oder /verify.php?neu=1 im angemeldeten Browser.
402 insufficient_balance Guthaben unter 2,00 EUR. In der Übersicht aufladen.
400 invalid_json Der Koerper war als JSON angekuendigt, ist aber keins.
405 method_not_allowed Nur GET, POST und OPTIONS.
422 recipient_required, address_required, address_not_germany, address_zip_required, content_or_document_required, invalid_template, document_too_large, document_type Ein Feld hat die Prüfung nicht bestanden. Die Meldung steht im Feld message.

Wir liefern nur innerhalb Deutschlands ein. Eine Anschrift ohne fünfstellige Postleitzahl oder mit einem ausländischen Land wird mit 422 und dem Code address_not_germany beziehungsweise address_zip_required abgewiesen, bevor etwas berechnet wird.

Ein Aufruf der Schnittstelle ist zugleich das ausdrückliche Verlangen, sofort mit dem Druck zu beginnen; das Widerrufsrecht erlischt mit der Einlieferung (§ 356 Abs. 4 BGB). Im Browser bestätigt der Kunde das mit einem Haken, in der Schnittstelle steht es in den AGB.

Das Guthaben wird in einer einzigen atomaren Anweisung belastet. Zwei gleichzeitige Aufrufe gegen ein Guthaben von zwei Euro können daher nicht beide durchgehen, der zweite bekommt 402.

Beispiele

PHP
$curl = curl_init('https://meinpostify.de/api.php');
curl_setopt_array($curl, [
    CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $apiKey],
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => [
        'company'   => 'Alscher Steuerberatung',
        'recipient' => 'Ilona Weber',
        'address'   => "Hauptstraße 3\n50667 Köln",
        'template'  => 'modern',
        'content'   => 'Sehr geehrte Frau Weber, anbei der Abschluss.',
    ],
    CURLOPT_RETURNTRANSFER => true,
]);
$letter = json_decode(curl_exec($curl), true);
Node
const body = new FormData();
body.set('recipient', 'Ilona Weber');
body.set('address', 'Hauptstraße 3, 50667 Köln');
body.set('template', 'statement');
body.set('document', new Blob([pdfBytes], { type: 'application/pdf' }), 'statement.pdf');

const response = await fetch('https://meinpostify.de/api.php', {
  method: 'POST',
  headers: { Authorization: `Bearer ${apiKey}` },
  body,
});
const letter = await response.json();