Dokumentation
Schnellstart: das erste Formular per API
1. Schlüssel anlegen
Nach der Registrierung legen Sie im Dashboard unter „API-Schlüssel“ einen Schlüssel an. Er wird nur einmal angezeigt; speichern Sie ihn sicher, etwa als Umgebungsvariable.
ff_test_…– kostenlos, bis zu 50 Seiten pro Tag, Ergebnisse tragen ein Wasserzeichen „TEST“.ff_live_…– ohne Wasserzeichen, Abrechnung pro Seite (Preise).
Jede Anfrage trägt den Schlüssel im Header Authorization: Bearer …. Die Basis-URL ist https://api.ausfuellpilot.de.
2. PDF übergeben
POST /files/upload nimmt die Datei als multipart/form-data im Feld file an und liefert eine id (file_…), die Sie in späteren Anfragen verwenden.
Alternativ übergeben Sie die Datei direkt in der Anfrage – als öffentlich erreichbare URL oder Base64. Im Tarif API sind Dateien bis 50 MB und 100 Seiten möglich.
// Ohne Upload: Datei per öffentlicher URL oder als Base64 übergeben
{ "file": { "url": "https://example.org/formular.pdf" }, "config": { … } }
{ "file": { "data": "<Base64>", "name": "formular.pdf" }, "config": { … } }3. Felder erkennen (optional)
POST /detect_form liefert in output.schema ein JSON-Schema: je Feld Name, Art (Text, Datum, Ankreuzfeld, Unterschrift …), Seite und Position. Das funktioniert auch bei Scans und PDFs ohne Formularfelder.
Nötig ist der Schritt nicht – /edit erkennt die Felder selbst. Speichern Sie das Schema aber, wenn Sie dasselbe Formular oft ausfüllen: Mit config.schema entfällt die Erkennung, und die Feldnamen bleiben stabil.
curl -sS "$FORMFILL_API_URL/detect_form" \
-H "Authorization: Bearer $FORMFILL_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "file": { "id": "'"$FILE_ID"'" } }' > detect.json
# Feldnamen anzeigen; das Schema können Sie speichern und wiederverwenden
jq '.output.schema.properties | keys' detect.json4. Ausfüllen
POST /edit füllt synchron und antwortet, wenn das PDF fertig ist. Woher die Werte kommen, bestimmen Sie: aus Anweisungen in natürlicher Sprache (config.instructions), aus beliebigem JSON (config.data, die KI ordnet die Schlüssel den Feldern zu) oder aus bis zu zehn Dokumenten (config.documents, zum Beispiel ein Ausweis-Scan oder eine Rechnung). Die Quellen lassen sich kombinieren.
export FORMFILL_API_URL="https://api.ausfuellpilot.de"
export FORMFILL_API_KEY="ff_test_…"
# 1. PDF hochladen
FILE_ID=$(curl -sS "$FORMFILL_API_URL/files/upload" \
-H "Authorization: Bearer $FORMFILL_API_KEY" \
-F file=@formular.pdf | jq -r .id)
# 2. Ausfüllen (synchron; für lange Formulare: POST /edit_runs und GET /edit_runs/:id)
curl -sS "$FORMFILL_API_URL/edit" \
-H "Authorization: Bearer $FORMFILL_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"file": { "id": "'"$FILE_ID"'" },
"config": {
"instructions": "Fülle das Formular für Erika Mustermann aus, geboren am 12.08.1990 in Musterstadt, wohnhaft Musterstraße 1, 12345 Musterstadt. Unterschreibe mit ihrem Namen und dem heutigen Datum.",
"advancedOptions": { "flattenPdf": true }
}
}' > run.json
# 3. Ergebnis herunterladen (Link 15 Minuten gültig)
curl -sS -o ausgefuellt.pdf "$(jq -r .output.editedFile.presignedUrl run.json)"{
"file": { "id": "file_…" },
"config": {
"schema": { … },
"data": {
"vorname": "Erika",
"nachname": "Mustermann",
"geburtsdatum": "1990-08-12"
},
"instructions": "Datumsangaben im Format TT.MM.JJJJ eintragen.",
"advancedOptions": { "flattenPdf": false }
}
}Standardmäßig werden die Werte fest eingebettet (advancedOptions.flattenPdf: true); mit false bleiben die Formularfelder bearbeitbar. Digital signierte PDFs werden inkrementell gespeichert, damit vorhandene Signaturen gültig bleiben.
5. Lange Formulare: Runs und Webhooks
Für lange Formulare oder viele Dokumente starten Sie einen Run mit POST /edit_runs (Felderkennung: POST /form_detection_runs). Die Antwort kommt sofort; den Stand fragen Sie mit GET /edit_runs/:id ab, bis status nicht mehr PROCESSING ist.
const API = process.env.FORMFILL_API_URL!;
const headers = { Authorization: `Bearer ${process.env.FORMFILL_API_KEY}`, "Content-Type": "application/json" };
// Run starten – die Antwort kommt sofort mit status "PROCESSING"
const started = await fetch(`${API}/edit_runs`, {
method: "POST",
headers: { ...headers, "Idempotency-Key": crypto.randomUUID() },
body: JSON.stringify({ file: { id: fileId }, config: { data } }),
}).then((r) => r.json());
// Abfragen, bis der Run fertig ist – oder stattdessen auf den Webhook warten
let run = started;
while (run.status === "PROCESSING") {
await new Promise((r) => setTimeout(r, 2000));
run = await fetch(`${API}/edit_runs/${started.id}`, { headers }).then((r) => r.json());
}
if (run.status !== "PROCESSED") throw new Error(`${run.failureReason}: ${run.failureMessage}`);Statt abzufragen, können Sie im Dashboard einen Webhook einrichten. Ausfüllpilot sendet dann bei edit_run.processed oder edit_run.failed den Run an Ihre URL, signiert mit x-formfill-signature. Prüfen Sie die Signatur immer über den unveränderten Request-Body.
{
"eventId": "evt_…",
"eventType": "edit_run.processed",
"payload": { "object": "edit_run", "id": "edr_…", "status": "PROCESSED", "output": { … } }
}import crypto from "node:crypto";
/** Verify x-formfill-signature: t=<unix>,v1=<hex hmac_sha256(secret, "<t>.<body>")> */
export function verifyFormfillSignature(rawBody: string, header: string, secret: string, toleranceSec = 300): boolean {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=", 2) as [string, string]));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest();
const given = Buffer.from(parts.v1 ?? "", "hex");
return given.length === expected.length && crypto.timingSafeEqual(given, expected);
}6. Ergebnis auswerten
output.editedFile.presignedUrl- Download-Link zum ausgefüllten PDF, 15 Minuten gültig. Danach liefert GET /edit_runs/:id einen neuen Link, solange die Datei gespeichert ist.
output.filledValues- Die eingetragenen Werte je Feld – praktisch für Protokolle oder eine eigene Vorschau.
output.review- Felder, bei denen die KI unsicher war oder Text gekürzt werden musste. Lassen Sie diese vor dem Versand von einem Menschen prüfen.
usage- Seitenzahl und Verbrauch dieses Runs.
Dateien und Ergebnisse werden nach der eingestellten Aufbewahrungsfrist automatisch gelöscht (im Tarif API standardmäßig nach einem Tag). Mit DELETE /edit_runs/:id löschen Sie einen Run samt Ergebnis sofort.
7. Fehler, Wiederholungen, Limits
- Fehler kommen als JSON mit
code,message,retryableundrequestId. Geben Sie dierequestIdan, wenn Sie uns kontaktieren. - Wiederholen Sie nur Anfragen mit
retryable: true, mit wachsendem Abstand. Senden Sie bei POST-Anfragen einenIdempotency-Key: Eine Wiederholung mit demselben Schlüssel innerhalb von 24 Stunden liefert die ursprüngliche Antwort, ohne einen zweiten Run zu starten. - Im Tarif API sind bis zu 600 Anfragen pro Minute möglich. Darüber antwortet die API mit 429 und dem Header
Retry-After. Das Abfragen von Runs zählt nicht mit. - Ein fehlgeschlagener Run hat
status: "FAILED"sowiefailureReasonundfailureMessage.
HTTP/1.1 429 Too Many Requests
Retry-After: 12
{
"code": "RATE_LIMITED",
"message": "Too many requests. Try again in 12 s.",
"retryable": true,
"requestId": "…"
}Loslegen