Stabilität: Beta
Eine Rechnung durchgängig über REST rendern
Auf einen Blick
Abschnitt betitelt „Auf einen Blick“Bringen Sie eine Rechnung von JSON zu einem lokal verifizierten PDF über die
Representational-State-Transfer-Fläche (REST) von NextPDF Connect — einen
Austausch über die Leitung nach dem anderen. Dieses Recipe reicht einen
Render-Job an POST /api/v1/jobs ein, wiederholt die Einreichung unter
demselben Idempotency-Key, um den duplikatsicheren Pfad zu zeigen, fragt
GET /api/v1/jobs/{id} ab, lädt das PDF von
GET /api/v1/jobs/{id}/result herunter, prüft die Bytes mit
qpdf --check und löscht den fertigen Job.
Jede Antwort unten ist eine wortgetreue Erfassung aus einer echten
Connect-Bereitstellung der Core-Stufe (nextpdf/server unter RoadRunner,
gebunden an http://localhost:8080). Die einzige Ersetzung ist der
API-Schlüssel, dargestellt als Umgebungsvariable
$NEXTPDF_CONNECT_TOKEN; Job-Bezeichner, Anfrage-Bezeichner, Zeitstempel,
Header und Body-Bytes sind exakt das, was der Server zurückgegeben hat.
Header-Werte wie Date und X-Request-Id unterscheiden sich in Ihrer
Bereitstellung selbstverständlich.
Dieses Recipe verarbeitet ein einzelnes Dokument, sodass Sie jeden
Austausch vollständig lesen können. Für viele Dokumente, begrenzte
Nebenläufigkeit und Retry-After-gesteuerte Poll-Schleifen siehe
PDFs im Batch mit Fortschrittsverfolgung erzeugen,
das dieselbe Job-Fläche nutzt.
Installation
Abschnitt betitelt „Installation“Die Serverseite ist die Standard-Connect-Distribution:
composer require nextpdf/serverDie Clientseite dieses Recipes besteht aus curl plus qpdf, sodass Sie
es auf jeden HTTP-Client portieren können. Exportieren Sie zuerst die Werte
Ihrer Bereitstellung:
export NEXTPDF_CONNECT_URL="http://localhost:8080"export NEXTPDF_CONNECT_TOKEN="npk_live_{kid}_{secret}" # your real key# Key provisioning and server startup live in the quickstart:Konzeptioneller Überblick
Abschnitt betitelt „Konzeptioneller Überblick“Die Fläche der asynchronen Jobs trennt die Einreichung vom Abruf: Sie
reichen eine Render-Anfrage ein, erhalten einen Job-Datensatz und rufen das
Ergebnis ab, sobald der Job completed erreicht. Die Render-Anfrage selbst
ist ein geordnetes operations-Array — dieselben Operationstypen
(set_font, add_text, add_table, add_image, add_page), die den
Connect-Werkzeugaufrufen auf jedem Transport zugrunde liegen — sowie
Felder auf Dokumentebene (page_size, orientation, title, author).
Zwei Vertragsdetails prägen das Transkript, das Sie gleich lesen werden:
- Idempotente Einreichung. Eine mit
Idempotency-Keyversehene Einreichung liefert beim ersten Mal201 Createdund bei einer Wiederholung200 OKmit demselben Job-Datensatz, sodass ein Netzwerk-Retry niemals zweimal rendert. - Die Einreichung kann bereits terminal sein. Das aktuelle Release
verarbeitet den Job inline, bevor es den
POSTbeantwortet, sodass die Einreichungsantwort bereitsstatus: "completed"tragen kann — so wie unten. Der Vertrag, bis zum Endzustand abzufragen, ist die stabile API-Form: Schreiben Sie die Poll-Schleife und akzeptieren Sie einen Endzustand bei jedem Versuch, auch beim ersten.
Sie können bestätigen, was Ihre Bereitstellung unterstützt, bevor Sie
irgendetwas einreichen: GET /api/v1/capabilities gibt den
Operationskatalog zurück, den die Stufe Ihres API-Schlüssels erreichen
kann. Auf der hier erfassten Core-Stufen-Bereitstellung waren nur die
Core-Operationen aufgeführt; der maßgebliche Katalog ist immer die Antwort
des laufenden Servers selbst, nicht diese Seite.
API-Fläche
Abschnitt betitelt „API-Fläche“| Austausch | Methode und Pfad | Erfasster Status |
|---|---|---|
| Den Render-Job einreichen | POST /api/v1/jobs | 201 Created |
| Dieselbe Einreichung wiederholen | POST /api/v1/jobs (gleicher Idempotency-Key) | 200 OK |
| Den Job-Datensatz abfragen | GET /api/v1/jobs/{id} | 200 OK |
| Das PDF herunterladen | GET /api/v1/jobs/{id}/result | 200 OK, application/pdf |
| Den fertigen Job löschen | DELETE /api/v1/jobs/{id} | 204 No Content |
Die Authentifizierung erfolgt über ein Bearer-Token bei jeder
/api/v1/*-Anfrage: Authorization: Bearer npk_live_{kid}_{secret}.
Erfolgreiche JSON-Antworten teilen sich den Envelope
{ "data": ..., "meta": ... }; die Felder, mit denen Sie arbeiten, liegen
unter data.
Die Rechnungsanfrage
Abschnitt betitelt „Die Rechnungsanfrage“Schreiben Sie die Render-Anfrage in invoice.json. Es handelt sich um eine
schlichte, deterministische Operationsliste — eine fette Kopfzeile, eine
Ausstellungszeile und eine Tabelle mit Einzelposten:
{ "page_size": "A4", "orientation": "portrait", "title": "Invoice INV-2026-0042", "author": "Aurora Fixtures Ltd.", "operations": [ { "type": "set_font", "family": "helvetica", "style": "B", "size": 16 }, { "type": "add_text", "text": "Invoice INV-2026-0042" }, { "type": "set_font", "family": "helvetica", "style": "", "size": 10 }, { "type": "add_text", "text": "Issued 2026-07-08 by Aurora Fixtures Ltd. Payment is due within 30 days.", "width": 0, "line_height": 5 }, { "type": "add_table", "html": "<table><tr><th>Item</th><th>Qty</th><th>Unit price</th><th>Amount</th></tr><tr><td>Cable tray, 300 mm</td><td>12</td><td>18.40</td><td>220.80</td></tr><tr><td>Mounting kit</td><td>4</td><td>9.75</td><td>39.00</td></tr><tr><td>Site delivery</td><td>1</td><td>25.00</td><td>25.00</td></tr><tr><td>Total (EUR)</td><td></td><td></td><td>284.80</td></tr></table>" } ]}Die Rechnungsfelder hier sind Beispieldaten. Die maßgebliche Form der
Argumente für jede Operation ist diejenige, die Ihre Bereitstellung meldet
— über MCP gibt tools/list das vollständige Eingabeschema für jeden
Operationstyp zurück, den diese Anfrage verwendet.
Durchgängiges Transkript
Abschnitt betitelt „Durchgängiges Transkript“1. Den Render-Job einreichen
Abschnitt betitelt „1. Den Render-Job einreichen“curl -sS -i -X POST "$NEXTPDF_CONNECT_URL/api/v1/jobs" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: inv-2026-0042" \ --data-binary @invoice.jsonDer Server antwortet mit 201 Created:
HTTP/1.1 201 CreatedCache-Control: no-storeContent-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'Content-Type: application/json; charset=utf-8Referrer-Policy: no-referrerVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Powered-By: NextPDF ConnectX-Ratelimit-Remaining: 99X-Request-Id: 019f3fd6-c6ed-727c-958e-2fa4370ba97eDate: Wed, 08 Jul 2026 03:47:48 GMTTransfer-Encoding: chunked{ "data": { "job_id": "job_9bd0808960f10eb568484acc", "status": "completed", "created_at": "2026-07-08T03:47:48+00:00", "started_at": "2026-07-08T03:47:48+00:00", "completed_at": "2026-07-08T03:47:48+00:00", "result_url": "/api/v1/jobs/job_9bd0808960f10eb568484acc/result" }, "meta": { "request_id": "019f3fd6-c6ed-727c-958e-2fa4370ba97e", "timestamp": "2026-07-08T03:47:48+00:00", "duration_ms": 63.31, "api_version": "v1" }}Der Job ist in dieser Erfassung bereits terminal — status ist
"completed" und result_url ist vorhanden —, weil das aktuelle Release
inline rendert, bevor es antwortet. Verlassen Sie sich nicht darauf:
Behandeln Sie die Einreichungsantwort als erstes Abfrageergebnis und
verzweigen Sie über data.status wie bei jeder anderen Abfrage.
2. Die Einreichung wiederholen (idempotenter Pfad)
Abschnitt betitelt „2. Die Einreichung wiederholen (idempotenter Pfad)“Wiederholen Sie exakt denselben Befehl — gleicher Idempotency-Key,
gleicher Body:
curl -sS -i -X POST "$NEXTPDF_CONNECT_URL/api/v1/jobs" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: inv-2026-0042" \ --data-binary @invoice.jsonDer Server gibt 200 OK zurück — nicht 201 — mit demselben job_id, und
es findet kein zweiter Render statt (vergleichen Sie meta.duration_ms mit
der ersten Antwort):
HTTP/1.1 200 OKCache-Control: no-storeContent-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'Content-Type: application/json; charset=utf-8Referrer-Policy: no-referrerVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Powered-By: NextPDF ConnectX-Ratelimit-Remaining: 99X-Request-Id: 019f3fd6-c756-7226-bc85-b255d75bd449Date: Wed, 08 Jul 2026 03:47:48 GMTTransfer-Encoding: chunked{ "data": { "job_id": "job_9bd0808960f10eb568484acc", "status": "completed", "created_at": "2026-07-08T03:47:48+00:00", "started_at": "2026-07-08T03:47:48+00:00", "completed_at": "2026-07-08T03:47:48+00:00", "result_url": "/api/v1/jobs/job_9bd0808960f10eb568484acc/result" }, "meta": { "request_id": "019f3fd6-c756-7226-bc85-b255d75bd449", "timestamp": "2026-07-08T03:47:48+00:00", "duration_ms": 1.03, "api_version": "v1" }}3. Den Job-Datensatz abfragen
Abschnitt betitelt „3. Den Job-Datensatz abfragen“curl -sS -i "$NEXTPDF_CONNECT_URL/api/v1/jobs/job_9bd0808960f10eb568484acc" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN"HTTP/1.1 200 OKCache-Control: no-storeContent-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'Content-Type: application/json; charset=utf-8Referrer-Policy: no-referrerVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Powered-By: NextPDF ConnectX-Ratelimit-Remaining: 98X-Request-Id: 019f3fd7-0038-7131-b71e-4c69e637233bDate: Wed, 08 Jul 2026 03:48:02 GMTTransfer-Encoding: chunked{ "data": { "job_id": "job_9bd0808960f10eb568484acc", "status": "completed", "created_at": "2026-07-08T03:47:48+00:00", "started_at": "2026-07-08T03:47:48+00:00", "completed_at": "2026-07-08T03:47:48+00:00", "result_url": "/api/v1/jobs/job_9bd0808960f10eb568484acc/result" }, "meta": { "request_id": "019f3fd7-0038-7131-b71e-4c69e637233b", "timestamp": "2026-07-08T03:48:02+00:00", "duration_ms": 0.24, "api_version": "v1" }}Diese Abfrage zeigt einen terminalen Datensatz, daher gibt es keinen
Retry-After-Header und kein poll_url-Feld. Solange ein Job noch
pending oder running ist, setzt der Server bei jeder Abfrage
Retry-After (ein Intervall von 2 Sekunden) — halten Sie sich daran, statt
in einer engen Schleife abzufragen.
4. Das PDF herunterladen
Abschnitt betitelt „4. Das PDF herunterladen“curl -sS -D result-headers.txt \ "$NEXTPDF_CONNECT_URL/api/v1/jobs/job_9bd0808960f10eb568484acc/result" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN" \ -o invoice-inv-2026-0042.pdfHTTP/1.1 200 OKCache-Control: no-storeContent-Disposition: attachment; filename="job-job_9bd0808960f10eb568484acc.pdf"Content-Length: 3663Content-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'Content-Type: application/pdfReferrer-Policy: no-referrerVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Powered-By: NextPDF ConnectX-Ratelimit-Remaining: 98X-Request-Id: 019f3fd7-0056-711e-acad-9f7ac5f21ba7Date: Wed, 08 Jul 2026 03:48:02 GMTDer Body ist das binäre PDF — 3.663 Bytes in dieser Erfassung, passend
zum Content-Length-Header — und wird hier ausgelassen. Er wird in
invoice-inv-2026-0042.pdf geschrieben.
5. Die heruntergeladenen Bytes lokal verifizieren
Abschnitt betitelt „5. Die heruntergeladenen Bytes lokal verifizieren“Ein 200 mit Content-Type: application/pdf ist für sich genommen kein
Beweis, dass der Body ein wohlgeformtes PDF ist. Führen Sie eine
Strukturprüfung mit qpdf durch:
qpdf --check invoice-inv-2026-0042.pdfErfasste Ausgabe für die oben heruntergeladene Datei:
checking invoice-inv-2026-0042.pdfPDF Version: 2.0File is not encryptedFile is not linearizedNo syntax or stream encoding errors found; the file may still containerrors that qpdf cannot detectDie Formulierung von qpdf ist die ehrliche Grenze: Dies ist eine Syntax- und Stream-Prüfung, keine Konformitätsfeststellung gegenüber irgendeinem Standard.
6. Den fertigen Job löschen
Abschnitt betitelt „6. Den fertigen Job löschen“curl -sS -i -X DELETE "$NEXTPDF_CONNECT_URL/api/v1/jobs/job_9bd0808960f10eb568484acc" \ -H "Authorization: Bearer $NEXTPDF_CONNECT_TOKEN"HTTP/1.1 204 No ContentCache-Control: no-storeContent-Security-Policy: default-src 'none'; frame-ancestors 'none'; base-uri 'none'; form-action 'none'Content-Type: application/json; charset=utf-8Referrer-Policy: no-referrerVary: Accept-EncodingX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Powered-By: NextPDF ConnectX-Ratelimit-Remaining: 97X-Request-Id: 019f3fd7-0084-722f-bf54-3dabead8aeeaDate: Wed, 08 Jul 2026 03:48:02 GMTNach dem Löschen sind der Job-Datensatz und sein gespeichertes Ergebnis
verschwunden; ein anschließender GET auf den Job gibt 404 zurück.
Grenzfälle & Stolperfallen
Abschnitt betitelt „Grenzfälle & Stolperfallen“- Verzweigen Sie über
data.status, nicht allein über den HTTP-Status. Einreichung, Wiederholung und Abfrage geben alle2xxmit einem Job-Datensatz zurück; der Lebenszyklusstatus liegt indata.status(pending,running,completed,failed,cancelled). - Ein wiederholter Schlüssel mit einem anderen Body ist ein
409 Conflict. Die idempotente200-Wiederholung tritt nur ein, wenn der Body mit der ursprünglichen Einreichung übereinstimmt. Verwenden Sie einen Schlüssel niemals für unterschiedliche Inhalte erneut. /resultvor Abschluss ist ein409. Laden Sie erst herunter, nachdem eine Abfragecompletedzeigt. Der409ist eine normale, zu prüfende Antwort, kein Transportfehler — dieselbe Trennung zwischen Transport und Status, der jedes Connect-Recipe folgt (siehe Recipe-Konventionen).- Jobs sind eigentümergebunden. Ein unter einem API-Schlüssel
eingereichter Job ist für einen anderen Schlüssel unsichtbar: Ein
GETüber Eigentümergrenzen hinweg gibt404zurück, nicht403. Fragen Sie mit derselben Anmeldeinformation ab, mit der Sie eingereicht haben. progresskann fehlen. Der erfasste Datensatz trägt keinprogress-Feld, weil der Job bereits terminal war. Wenn der Server den Fortschritt für einen nicht-terminalen Job verfolgt, istdata.progresseine Ganzzahl von 0 bis 100; behandeln Sie ein fehlendes Feld als unbekannt, nicht als null.- Ein
failed-Job trägtdata.error. Halten Sie es fest; reichen Sie nicht blind erneut ein.
Performance
Abschnitt betitelt „Performance“Ein Render-Job kostet eine Einreichung, höchstens eine Handvoll Abfragen
und einen Download. Die erfassten meta.duration_ms-Werte erzählen die
Geschichte: 63,31 ms, um die Rechnung bei der Einreichung zu rendern,
1,03 ms für die idempotente Wiederholung, die keine Arbeit verrichtete, und
Statusabfragen im Submillisekundenbereich. Fragen Sie im
Retry-After-Takt des Servers ab statt in einer engen Schleife; die
Statusabfrage ist günstig, aber nicht kostenlos, und der Ratenbegrenzer
budgetiert sie (beobachten Sie, wie X-Ratelimit-Remaining in den
erfassten Headern herunterzählt). Begrenzen Sie bei Batches die in
Bearbeitung befindlichen Jobs, statt alles auf einmal einzureichen — das
Batch-Recipe
implementiert diese Schleife.
Sicherheitshinweise
Abschnitt betitelt „Sicherheitshinweise“- Halten Sie das Bearer-Token ausschließlich im
Authorization-Header. Niemals in einem Query-String, einer Log-Zeile oder einer committeten Datei. Das Transkript oben ersetzt es genau aus diesem Grund durch eine Umgebungsvariable. - Validieren Sie heruntergeladene Bytes, bevor Sie ihnen vertrauen.
Schritt 5 ist Teil des Ablaufs, kein optionaler Zusatz: Prüfen Sie, dass
die Antwort ein PDF ist (mindestens
%PDF-Header,qpdf --checkfür die Struktur), bevor Sie es archivieren oder weiterleiten. - Löschen Sie fertige Jobs, die Sie nicht mehr benötigen. Schritt 6 entfernt das gespeicherte Ergebnis vom Server; ein abgeschlossener Job bleibt andernfalls herunterladbar, bis die Job-Garbage-Collection des Servers ihn entfernt.
- Verwenden Sie einen Schlüssel mit minimalen Rechten. Dieser Ablauf benötigt einen Render-Schlüssel der Core-Stufe und nichts weiter.
Konformität
Abschnitt betitelt „Konformität“Dieses Recipe erhebt keinen normativen Standards-Anspruch. Es nutzt die
REST-Endpunkte der asynchronen Jobs von Connect und liest die Felder
des Job-Datensatzes, die der Server definiert. Der Schritt qpdf --check
bestätigt nur die strukturelle Integrität — „the file may still contain
errors that qpdf cannot detect“ ist qpdfs eigener Vorbehalt, oben
wortgetreu zitiert. Die Konformität gegenüber einem Standard (PDF/A-4,
PDF/UA) festzustellen, ist Aufgabe eines unabhängigen Prüfwerkzeugs und
eine andere Fläche — siehe
Eine Prüfung gegen einen benannten Standard ausführen
für diese Grenze.
Siehe auch
Abschnitt betitelt „Siehe auch“- PDFs im Batch mit Fortschrittsverfolgung erzeugen — dieselbe Job-Fläche, gesteuert als Batch mit begrenzter Nebenläufigkeit.
- Ihr erstes PDF erzeugen — der kleinste Connect-Render.
- Eine Agenten-Dokumentsitzung über MCP führen — dieselbe Engine, Werkzeug für Werkzeug, über den MCP-stdio-Transport.
- Connect-Recipe-Konventionen — der Vertrag aus Transport, Stufe und Konformität, dem jedes Connect-Recipe folgt.
- Exception-bewusste Fehlerbehandlung über Connect — wie man Transportfehler von Nicht-Erfolgs-Status trennt.