Zum Inhalt springen

Dokumentation

Schnellstart: das erste Formular per API

Diese Anleitung führt vom API-Schlüssel bis zum ausgefüllten PDF. Alle Endpunkte mit sämtlichen Parametern finden Sie in der API-Referenz.

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.

Datei ohne Upload übergebenjson
// 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.

Felder erkennen – cURLbash
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.json

4. 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.

Aus Anweisungen – cURLbash
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)"
Aus Daten – Request-Bodyjson
{
  "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.

Run starten und abfragen – TypeScriptts
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.

Webhook-Bodyjson
{
  "eventId": "evt_…",
  "eventType": "edit_run.processed",
  "payload": { "object": "edit_run", "id": "edr_…", "status": "PROCESSED", "output": { … } }
}
Signatur prüfen – TypeScriptts
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, retryable und requestId. Geben Sie die requestId an, wenn Sie uns kontaktieren.
  • Wiederholen Sie nur Anfragen mit retryable: true, mit wachsendem Abstand. Senden Sie bei POST-Anfragen einen Idempotency-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" sowie failureReason und failureMessage.
Beispiel: Ratenbegrenzunghttp
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

Weiter geht’s

Alle Endpunkte, Parameter und Antwortformate finden Sie in der API-Referenz.