API-Dokumentation

Mit der Snapfritz-API lässt sich der PDF-Dienst aus eigener Software oder von einem autonomen Agenten nutzen: PDF hochladen, ausgelesene Felder prüfen und bestätigen, unterschreiben und das fertige PDF laden.

Überblick

Basis-URL
https://snapfritz.com/api/v1
Format
JSON (UTF-8); Upload als multipart/form-data oder application/pdf
Tarif
Platin – ohne Platin antworten Aufrufe mit 403 plan_required
Version
v1 – Erweiterungen kommen abwärtskompatibel dazu

Der Ablauf besteht immer aus denselben Schritten:

  1. Hochladen – die Felder werden sofort ausgelesen
  2. Felder prüfen und bestätigen (oder an einen Menschen übergeben)
  3. Signieren
  4. Fertiges PDF herunterladen

Authentifizierung

Jede Anfrage trägt einen API-Schlüssel im Header Authorization. Schlüssel legen Sie unter Entwickler an; sie werden nur einmal angezeigt und bei uns nur als Hash gespeichert. Ein Schlüssel gehört zu genau einem Konto und darf nur, was seine Scopes erlauben.

Authorization: Bearer sf_live_…
ScopeErlaubt
documents:readDokumente und Felder lesen, Kontingent abfragen, signiertes PDF herunterladen
documents:writePDFs hochladen, Felder bestätigen, Checkbox-Optionen wählen
documents:signDokumente mit der gespeicherten Unterschrift des Kontos signieren – standardmäßig aus

Behandeln Sie den Schlüssel wie ein Passwort: nie in Code-Repositories oder Client-Apps ablegen. Bei Verdacht unter Entwickler widerrufen und neu anlegen.

Schnellstart

Ein vollständiger Durchlauf. Ersetzen Sie $DOC und $FIELD durch die IDs aus der Antwort.

export SNAPFRITZ_API_KEY=sf_live_…

# 1. upload
curl -s -X POST https://snapfritz.com/api/v1/documents \
  -H "Authorization: Bearer $SNAPFRITZ_API_KEY" \
  -F file=@formular.pdf

# 2. confirm a field (omit "value" to accept the suggestion)
curl -s -X POST https://snapfritz.com/api/v1/documents/$DOC/fields/$FIELD/confirm \
  -H "Authorization: Bearer $SNAPFRITZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": "Erika Muster"}'

# 3. sign (needs the documents:sign scope)
curl -s -X POST https://snapfritz.com/api/v1/documents/$DOC/sign \
  -H "Authorization: Bearer $SNAPFRITZ_API_KEY"

# 4. download the filled PDF
curl -s https://snapfritz.com/api/v1/documents/$DOC/download \
  -H "Authorization: Bearer $SNAPFRITZ_API_KEY" -o ausgefuellt.pdf

Dasselbe als Python-Skript

Bestätigt sicher erkannte Felder automatisch und übergibt alles Unsichere an einen Menschen.

import os
import requests

BASE = "https://snapfritz.com/api/v1"
H = {"Authorization": f"Bearer {os.environ['SNAPFRITZ_API_KEY']}"}

with open("formular.pdf", "rb") as f:
    doc = requests.post(f"{BASE}/documents", headers=H, files={"file": f}, timeout=120)
doc.raise_for_status()
doc = doc.json()
if doc["status"] == "failed":
    raise SystemExit("PDF not readable")

for field in doc["fields"]:
    if field["confirmed"] or field["state"] in ("not_applicable", "out_of_scope"):
        continue
    if field["state"] != "ok":
        # unsure / missing / hand: decide yourself or hand over to a human
        raise SystemExit(f"Needs a decision: {field['label']} -> {doc['reviewUrl']}")
    requests.post(f"{BASE}/documents/{doc['id']}/fields/{field['id']}/confirm",
                  headers=H, json={}).raise_for_status()

requests.post(f"{BASE}/documents/{doc['id']}/sign", headers=H).raise_for_status()
pdf = requests.get(f"{BASE}/documents/{doc['id']}/download", headers=H)
open("ausgefuellt.pdf", "wb").write(pdf.content)

Beispielantwort des Uploads

Gekürzt auf ein Feld; Werte sind Beispiele.

{
  "id": "3f0c2d5e-8a4b-4f0e-9c1a-2b7d6e5f4a31",
  "filename": "formular.pdf",
  "title": "Anmeldung",
  "documentType": "registration",
  "status": "ready_for_review",
  "pageCount": 2,
  "requiresStrongerSignature": false,
  "requiresStrongerSignatureReason": null,
  "createdAt": "2026-09-25T13:04:11.000Z",
  "signedAt": null,
  "sentAt": null,
  "downloadable": false,
  "reviewUrl": "https://snapfritz.com/documents/3f0c2d5e-8a4b-4f0e-9c1a-2b7d6e5f4a31",
  "fields": [
    {
      "id": "b1f7c8a0-52de-4c1e-8f3a-9d0e7a6b5c42",
      "page": 1,
      "type": "text",
      "label": "Nachname",
      "semanticKey": "last_name",
      "state": "ok",
      "value": "Muster",
      "source": "profile",
      "confirmed": false,
      "checkboxGroup": null
    }
  ]
}

Endpunkte

Alle Pfade beziehen sich auf die Basis-URL. IDs sind UUIDs; fremde oder ungültige IDs antworten mit 404.

GET/usage

Kontingent im aktuellen Zeitraum

Scope: documents:read

Zeigt, wie viele Formulare schon fertiggestellt wurden und wie viele bleiben; unter „uploads“ dasselbe für hochgeladene PDFs.

Antworten

  • 200 Erfolgreich

GET/documents

Dokumente auflisten, neueste zuerst

Scope: documents:read

Parameter

NameinTypBeschreibung
limitqueryintegeroptional · 1–100, default 25
offsetqueryintegeroptional · default 0
statusqueryDocumentStatusoptional

Antworten

  • 200 Erfolgreich

POST/documents

PDF hochladen und Felder auslesen

Scope: documents:write

Die Extraktion läuft synchron und dauert meist 10–60 Sekunden; die Antwort enthält das fertige Dokument mit allen Feldern. Senden Sie entweder multipart/form-data mit dem Teil „file“ oder das rohe PDF als application/pdf (Dateiname über den Header X-Filename). Maximal 20 MB und 20 Seiten. Jeder Upload zählt zum Upload-Kontingent; nicht lesbare Scans enden mit dem Status „failed“ und zählen nicht.

Request-Body

multipart/form-data (Teil „file“) oder application/pdf

Antworten

  • 201 Angelegt
  • 402 Upload-Kontingent aufgebraucht (quota_exceeded)
  • 413 Datei zu groß
  • 415 Kein PDF
  • 422 Mehr Seiten, als ein Formular haben darf (too_many_pages)
  • 429 Zu viele Uploads (10 pro Minute und Schlüssel)

GET/documents/{id}

Ein Dokument mit seinen Feldern

Scope: documents:read

Parameter

NameinTypBeschreibung
idpathstring (uuid)Pflicht

Antworten

  • 200 Erfolgreich
  • 404 Nicht gefunden

POST/documents/{id}/fields/{fieldId}/confirm

Feld bestätigen, optional mit geändertem Wert

Scope: documents:write

Ohne „value“ wird der Vorschlag unverändert übernommen. „null“ oder ein leerer String leert das Feld.

Parameter

NameinTypBeschreibung
idpathstring (uuid)Pflicht
fieldIdpathstring (uuid)Pflicht

Request-Body

{ "value": "Erika Muster" } – optional

Antworten

  • 200 Erfolgreich
  • 404 Nicht gefunden

POST/documents/{id}/checkbox-groups/select

Eine Option einer Checkbox-Gruppe wählen

Scope: documents:write

Alle anderen Optionen der Gruppe werden auf „not_applicable“ gesetzt.

Parameter

NameinTypBeschreibung
idpathstring (uuid)Pflicht

Request-Body

{ "fieldId": "<uuid>" }

Antworten

  • 200 Erfolgreich
  • 404 Nicht gefunden
  • 422 Das Feld gehört zu keiner Checkbox-Gruppe

POST/documents/{id}/sign

PDF ausfüllen und Unterschrift aus dem Profil setzen

Scope: documents:sign

Braucht den Scope documents:sign (standardmäßig aus). Antwortet mit 409, solange Felder unbestätigt sind, und mit 422, wenn das Dokument eine stärkere Signatur verlangt, als wir liefern können, oder keine Unterschrift hinterlegt ist. Das Fertigstellen zählt ein Formular zum Kontingent (eine wieder geöffnete Kopie nicht): 402, wenn es aufgebraucht ist, 403 „plan_required“, wenn der Tarif das Unterschreiben im Namen der Organisation nicht enthält. Bei einem bereits signierten Dokument idempotent.

Parameter

NameinTypBeschreibung
idpathstring (uuid)Pflicht

Antworten

  • 200 Erfolgreich
  • 402 Allowance of finished forms used up, and additional forms off or at their cap (`quota_exceeded`)
  • 403 Tarif ohne Unterschreiben im Namen der Organisation (plan_required)
  • 409 Noch nicht signierbar (unbestätigte Felder)
  • 422 Lässt sich nicht automatisch signieren

GET/documents/{id}/download

Das ausgefüllte, signierte PDF

Scope: documents:read

Antwortet mit 409 „not_signed“, solange kein signiertes PDF existiert.

Parameter

NameinTypBeschreibung
idpathstring (uuid)Pflicht

Antworten

  • 200 Erfolgreich
  • 409 Noch nicht signiert

Maschinenlesbare Beschreibung für Client-Generatoren und Agenten-Frameworks: OpenAPI 3.1 (JSON)

Objekte

Das öffentliche Format eines Dokuments und seiner Felder. Interne Angaben wie Speicherorte sind bewusst nicht enthalten.

Dokument

NameTypBeschreibung
iduuidDokument-ID
filenamestringName der hochgeladenen Datei
titlestring | nullErkannter Titel des Formulars
documentTypestring | nullErkannte Dokumentart
statusstringBearbeitungsstand, siehe unten
pageCountinteger | nullSeitenzahl
requiresStrongerSignaturebooleantrue: braucht mehr als eine einfache elektronische Signatur – Signieren per API wird abgelehnt
requiresStrongerSignatureReasonstring | nullBegründung dazu
createdAt / signedAt / sentAtdate-time | nullISO 8601
downloadablebooleantrue, sobald ein signiertes PDF abrufbar ist
reviewUrluriPrüfansicht im Browser – hierhin übergeben Sie, wenn ein Mensch entscheiden soll
fieldsField[]Nur bei einzelnem Dokument und Upload enthalten

Feld

NameTypBeschreibung
iduuidFeld-ID
pageintegerSeite im PDF
typetext | date | checkbox | signature
labelstringBeschriftung im Formular
semanticKeystring | nullBedeutung, z. B. der Profilwert, aus dem befüllt wurde
statestringVerlässlichkeit, siehe unten
valuestring | nullVorgeschlagener oder bestätigter Wert
sourceprofile | learned_cache | user_input | noneWoher der Wert stammt
confirmedbooleanErst wenn alle relevanten Felder bestätigt sind, kann signiert werden
checkboxGroupstring | nullGleicher Wert = Optionen schließen sich gegenseitig aus

Feld-Status (state)

okSicher erkannt und befüllt.
unsureVorschlag vorhanden, aber unsicher – bitte prüfen.
missingKein Wert bekannt – muss geliefert werden.
handHandschriftlich oder nicht maschinell lesbar – besser einem Menschen übergeben (reviewUrl).
not_applicableTrifft hier nicht zu (z. B. abgewählte Checkbox-Option); muss nicht bestätigt werden.
out_of_scopeWird nicht von Snapfritz befüllt; muss nicht bestätigt werden.

Dokument-Status (status)

receivedEingegangen, wird verarbeitet.
understandingDokumentart wird erkannt.
extractingFelder werden ausgelesen.
ready_for_reviewFertig ausgelesen – Felder bestätigen.
in_reviewWird gerade im Browser geprüft.
signedSigniert; das PDF ist abrufbar.
sentSigniert und versendet.
revokedWiderrufen.
failedNicht lesbar; zählt nicht zum Kontingent.

Fehler

Fehler haben immer diese Form; der code ist stabil und für Programme gedacht, die message für Menschen:

{ "error": { "code": "not_found", "message": "Document not found." } }
HTTPcodeBedeutung
400invalid_json, invalid_parameter, missing_fileFehlerhafte Anfrage
401unauthorizedSchlüssel fehlt, ist unbekannt oder widerrufen
402quota_exceededKontingent des Zeitraums aufgebraucht – quota.kind sagt, ob fertige Formulare (forms) oder Uploads (uploads)
403plan_required, insufficient_scopeTarif ohne API-Zugang bzw. ohne Unterschreiben im Namen der Organisation, oder dem Schlüssel fehlt der Scope
403forbiddenDas Dokument ist mit Ihnen nur zum Ansehen bzw. Ausfüllen geteilt
403key_owner_leftWer den Schlüssel angelegt hat, gehört nicht mehr zur Organisation – neuen Schlüssel anlegen
404not_foundUnbekannte, ungültige oder fremde ID – auch ein nicht mit Ihnen geteiltes Dokument der Organisation
409conflict, not_signedFalscher Zustand, z. B. unbestätigte Felder oder noch nichts zum Herunterladen
413 / 415payload_too_large, not_a_pdf, unsupported_media_typeUngeeigneter Upload
422invalid, too_many_pagesLässt sich nicht automatisch erledigen (stärkere Signatur nötig, keine Unterschrift hinterlegt …) bzw. PDF mit mehr als 20 Seiten (Felder pages und limit)
429rate_limitedZu viele Anfragen – Header Retry-After beachten
500internal_errorFehler auf unserer Seite – später erneut versuchen
HTTP/1.1 402 Payment Required
Content-Type: application/json

{
  "error": {
    "code": "quota_exceeded",
    "message": "The plan's upload allowance for this period is used up.",
    "quota": { "kind": "uploads", "plan": "platin", "used": 200, "limit": 200, "resetsAt": "2026-10-25T00:00:00.000Z" }
  }
}

Limits & Kontingent

  • 60 Anfragen pro Minute und Schlüssel, davon höchstens 10 Uploads. Darüber: 429 mit Retry-After (Sekunden); jede Antwort trägt X-RateLimit-Header.
  • PDFs bis 20 MB und 20 Seiten. Der Tarif zählt fertige Formulare (jedes erfolgreiche Signieren) und, doppelt so viele, hochgeladene lesbare PDFs.
  • Ist eins davon aufgebraucht, antworten Upload bzw. Signieren mit 402 quota_exceeded. Es wird nichts nachberechnet. GET /usage zeigt vorab, wie viel bleibt und wann es sich erneuert.
  • Die Extraktion läuft synchron im Upload-Request und dauert je nach Umfang bis zu etwa einer Minute – Timeout im Client entsprechend hoch setzen (≥ 120 s).
  • Alle Aktionen mit einem Schlüssel erscheinen im Protokoll des Kontos als „API-Schlüssel <Präfix>“.

Für Agenten

  • Prüfen Sie state und confirmed jedes Felds. Nur ok automatisch bestätigen; bei unsure, missing und hand entscheiden Sie selbst oder übergeben an einen Menschen über reviewUrl.
  • Rufen Sie vor dem Upload GET /usage ab, um 402 zu vermeiden.
  • Bei 429 den Header Retry-After abwarten, bei 5xx mit Backoff wiederholen. POST …/sign ist idempotent; Uploads sind es nicht – nach einem Timeout zuerst GET /documents prüfen, statt blind neu hochzuladen.
  • Vergeben Sie documents:sign nur an Schlüssel, die wirklich unterschreiben sollen, und trennen Sie Lese- von Schreibschlüsseln.
  • Dokumente mit requiresStrongerSignature = true lassen sich nicht per API signieren – sie gehören in die Prüfansicht.