Zum Inhalt springen

QR-Codes API

Übersicht

Die Codes-API ist das Herzstück von qr3.app. Damit erstellst, aktualisierst und löschst du dynamische und statische QR-Codes.

Basis-URL: https://qr3.app/v1/codes

Verbindlicher REST-Vertrag

Der folgende Vertrag ist für alle Clients maßgeblich:

  • POST /v1/codes akzeptiert nur die Typen url, vcard, wifi, email, sms und location. Die Felder sind flach: url; mindestens vcard_first_name oder vcard_last_name; wifi_ssid; email_to; sms_phone; beziehungsweise location_lat und location_lng.
  • Nur url-Codes können dynamisch sein. Erstellen-Aufrufe dürfen optional expires_at mit einem ISO-8601-Zeitpunkt enthalten; ab_enabled, ab_target_url_b und ab_weight_a (A/B-Destinations) sind für dynamische url-Codes zulässig; redirect_after_expiry gehört nicht zum Erstellen-Vertrag.
  • GET /v1/codes unterstützt ausschließlich limit, cursor und status (live, paused, flagged, draft).
  • POST /v1/codes/batch akzeptiert url, vcard und wifi. Das Limit beträgt Free 10, Pro 500 und Business/Agency/Enterprise 1.000 Einträge pro Anfrage. URL-Scans laufen synchron für höchstens 50 URL-Einträge; bei mehr als 50 ist skip_url_scan: true erforderlich.

QR-Code erstellen

POST /v1/codes

Terminal-Fenster
curl -X POST https://qr3.app/v1/codes \
-H "Authorization: Bearer qr3_sk_..." \
-H "Content-Type: application/json" \
-d '{
"type": "url",
"url": "https://example.com",
"title": "Mein erster QR-Code",
"tags": ["marketing", "q1"],
"is_dynamic": true
}'

Response (HTTP 201):

{
"data": {
"id": "qr_a1b2c3d4",
"short_code": "r7f3Kx",
"redirect_url": "https://qr3.app/r7f3Kx",
"image_svg_url": "https://qr3.app/v1/codes/r7f3Kx/qr.svg",
"image_png_url": "https://qr3.app/v1/codes/r7f3Kx/qr.png",
"image_pdf_url": "https://qr3.app/v1/codes/r7f3Kx/qr.pdf",
"image_eps_url": "https://qr3.app/v1/codes/r7f3Kx/qr.eps",
"type": "url",
"target_url": "https://example.com",
"is_dynamic": true,
"status": "live",
"tags": ["marketing", "q1"],
"total_scans": 0,
"created_at": "2026-03-15T10:00:00.000Z"
},
"meta": { "request_id": "req_xyz123" }
}

QR-Code-Typen

TypBeschreibungPflichtfelder
urlWebsite-URL (dynamisch oder statisch)url
vcardVisitenkarte (vCard 3.0)vcard_first_name oder vcard_last_name
wifiWLAN-Konfigurationwifi_ssid
emailE-Mail (mailto:)email_to
smsSMSsms_phone
locationStandort (geo:)location_lat, location_lng

Batch-Erstellung

POST /v1/codes/batch

Terminal-Fenster
curl -X POST https://qr3.app/v1/codes/batch \
-H "Authorization: Bearer qr3_sk_..." \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "type": "url", "url": "https://produkt-1.example.com", "tags": ["batch"] },
{ "type": "url", "url": "https://produkt-2.example.com", "tags": ["batch"] },
{ "type": "wifi", "wifi_ssid": "GastWLAN", "wifi_password": "geheim123" }
],
"skip_url_scan": false
}'

Response (HTTP 201):

{
"data": {
"created": [
{ "id": "qr_...", "short_code": "abc123", "redirect_url": "https://qr3.app/abc123" },
{ "id": "qr_...", "short_code": "def456", "redirect_url": "https://qr3.app/def456" },
{ "id": "qr_...", "short_code": "ghi789", "status": "live" }
],
"total": 3,
"failed": 0
}
}

QR-Code-Liste

GET /v1/codes

Terminal-Fenster
curl https://qr3.app/v1/codes?status=live&limit=20 \
-H "Authorization: Bearer qr3_sk_..."

Query-Parameter:

ParameterTypStandardBeschreibung
cursorstring—Cursor für Pagination
limitinteger20Ergebnisse pro Seite (max. 100)
statusstring—Filter: live, paused, flagged, draft

QR-Code abrufen

GET /v1/codes/:id

Terminal-Fenster
curl https://qr3.app/v1/codes/qr_a1b2c3d4 \
-H "Authorization: Bearer qr3_sk_..."

QR-Code aktualisieren

PATCH /v1/codes/:id

Aktualisierungsvertrag: url darf nur bei einem bestehenden Code mit type: "url" geändert werden und muss mit http:// oder https:// beginnen. Andere Code-Typen dürfen kein Feld url erhalten; ungültige Anfragen liefern 422.

Dynamische QR-Codes ermöglichen es, die Ziel-URL jederzeit zu ändern — ohne den QR-Code neu drucken zu müssen.

Terminal-Fenster
curl -X PATCH https://qr3.app/v1/codes/qr_a1b2c3d4 \
-H "Authorization: Bearer qr3_sk_..." \
-H "Content-Type: application/json" \
-d '{ "url": "https://neue-zielseite.example.com", "status": "live" }'

Für dynamische url-Codes kannst du is_landing_page: true setzen (beim Erstellen oder per PATCH). Ein Scan zeigt dann eine von qr3 gehostete Seite mit den öffentlichen Dateien und externen Links des Codes, statt weiterzuleiten. Externe Links werden als links-Array übergeben:

{
"is_landing_page": true,
"links": [
{ "label": "Datenblatt", "url": "https://example.com/datenblatt.pdf" },
{ "label": "Zertifikat", "url": "https://example.com/zertifikat.pdf" }
]
}
  • 0–20 Links pro Code; label ist 1–100 Zeichen; url muss http(s) sein (≤ 2048 Zeichen).
  • Jede URL wird mit Google Web Risk geprüft — eine unsichere URL liefert 422.
  • Ist Web Risk beim Speichern nicht erreichbar, wird der Link trotzdem akzeptiert, aber zur erneuten Prüfung vorgemerkt. Ein täglicher Job prüft gespeicherte Links erneut (und sauber eingestufte Links regelmäßig nach) und pausiert den Code automatisch, falls ein Link später als unsicher erkannt wird.
  • "links": [] löscht alle Links. Siehe den Landingpage-Leitfaden.

QR-Code löschen

DELETE /v1/codes/:id

Soft-Delete — der QR-Code wird archiviert, Scan-Daten bleiben erhalten.

Ein zweites DELETE auf denselben Code liefert 404, auch wenn beide Anfragen gleichzeitig ankommen. Der Webhook qr.deleted wird genau einmal gesendet.

Terminal-Fenster
curl -X DELETE https://qr3.app/v1/codes/qr_a1b2c3d4 \
-H "Authorization: Bearer qr3_sk_..."

QR-Bilder herunterladen

Alle Bildformate sind öffentlich zugänglich — keine Authentifizierung erforderlich.

FormatURLVerwendung
SVG (Vektor)/v1/codes/:code/qr.svgWeb, Scaling, Digital
PNG (Raster)/v1/codes/:code/qr.pngE-Mail, Präsentationen
PDF (Vektor)/v1/codes/:code/qr.pdfStandard: quadratisch (nur der Code); ?format=a4 für ein Druckblatt
EPS (Vektor)/v1/codes/:code/qr.epsProfi-Druck-Workflows (Adobe, Druckereien)

Optional: ?size=N — Modulgröße in Pixeln (2–20, Standard: 4) für SVG, PNG und EPS. Das PDF nutzt eine feste Modulgröße.

Nur PDF: ?format=a4|square — Seitenformat (Standard: square — nur Code + Quiet Zone, kein A4-Weißraum; a4 für ein druckfertiges A4-Blatt)

Terminal-Fenster
# SVG für Web
curl https://qr3.app/v1/codes/r7f3Kx/qr.svg
# PNG in hoher Auflösung
curl https://qr3.app/v1/codes/r7f3Kx/qr.png?size=10 -o qr-hires.png
# Kompaktes PDF (nur der Code, Standard)
curl https://qr3.app/v1/codes/r7f3Kx/qr.pdf -o qr.pdf
# Druckfertiges A4-Blatt
curl "https://qr3.app/v1/codes/r7f3Kx/qr.pdf?format=a4" -o qr-a4.pdf

Farben und Fehlerkorrektur

Alle vier Bildrouten nehmen drei optionale Parameter an. Sie gelten für diesen einen Abruf und haben Vorrang vor Farben, die am Code gespeichert sind (nächster Abschnitt).

ParameterWerteStandardSVG, PNGPDF, EPS
fgHex RRGGBB oder RGB000000wird gezeichnetwird als Druckfarbe gezeichnet
bgHex wie fg oder transparentffffffwird gezeichnetwird als Druckfarbe gezeichnet
eccL, M, Q, HMwirktwirkt
  • Schreibweise: Groß- und Kleinschreibung sind egal, das # ist optional. Mitgeschickt wird es als %23 kodiert.
  • Ungültige Werte zählen als nicht gesetzt. ?fg=lila liefert die am Code gespeicherte Farbe, ohne gespeicherte Farbe das normale Schwarz, mit 200 und nie einen Fehler. Nur ein ausdrückliches ?fg=000000 erzwingt Schwarz.
  • bg=transparent liefert ein SVG, PDF oder EPS ohne Hintergrund und ein PNG mit echtem Alphakanal. Der Untergrund, auf den der Code gelegt wird, muss hell sein und rundum 4 Module Ruhezone frei lassen. Ein dunkler Code auf dunklem Untergrund ist nicht lesbar.
  • PDF und EPS schreiben Schwarz und Grau als Graustufe (nur Schwarzplatte), jede andere Farbe als CMYK in ganzen Prozent, zum Beispiel 1F4E79 als C74 M36 Y0 K53. Sobald eine Farbe gewählt ist, liegt hinter dem Code und seiner Ruhezone eine deckende Fläche, weiß oder in der Farbe von bg, wie beim SVG. Ohne Farben bleiben beide Dateien unverändert. Umrechnung und Grenzen: Farben im Druck.
  • Lesbarkeit: Die Bildroute prüft den Kontrast nicht. Empfohlen sind mindestens 4:1 und dunkle Module auf hellem Grund. #1F4E79 auf Weiß hat 8,7:1, #ff6600 auf Weiß nur 2,9:1.
  • ecc ändert das Punktmuster, nicht den Inhalt. Ein gedruckter Code funktioniert weiter, alte und neue Druckdateien dürfen aber nicht gemischt werden. Q oder H machen den Code robuster, etwa auf Wellpappe. Ein Logo erzwingt immer H.
  • Mit Logo bleibt die Fläche hinter dem Logo weiß, auch bei farbigem oder transparentem Hintergrund.
  • Zwischenspeicherung: Nur das tatsächliche Standardbild (Schwarz auf Weiß, Fehlerkorrektur M, kein Logo) wird 24 Stunden als unveränderlich ausgeliefert, jede andere Darstellung 5 Minuten. Bei PDF und EPS zählt jeder angegebene Hintergrund als Abweichung: bg=ffffff zeichnet dort eine weiße Fläche, die die Standarddatei nicht hat.
  • Verfügbarkeit: Farben und Fehlerkorrektur stehen in jedem Tarif zur Verfügung, auch im kostenlosen.
Terminal-Fenster
# Dunkelblauer Code auf Weiß
curl "https://qr3.app/v1/codes/r7f3Kx/qr.svg?fg=1F4E79" -o qr-blau.svg
# Transparentes PNG für ein Layout auf hellem Untergrund
curl "https://qr3.app/v1/codes/r7f3Kx/qr.png?size=10&bg=transparent" -o qr-transparent.png
# Robuster für Wellpappe: Fehlerkorrektur Q
curl "https://qr3.app/v1/codes/r7f3Kx/qr.pdf?ecc=Q" -o qr-q.pdf

Farben am Code speichern

PATCH /v1/codes/:id speichert Farben als appearance am Code. Die Bildrouten zeichnen sie dann standardmäßig, ohne Parameter.

Terminal-Fenster
curl -X PATCH https://qr3.app/v1/codes/qr_a1b2c3d4 \
-H "Authorization: Bearer qr3_sk_..." \
-H "Content-Type: application/json" \
-d '{"appearance": {"foreground_color": "#1F4E79"}}'

Antwort (HTTP 200, gekürzt):

{
"data": {
"id": "qr_a1b2c3d4",
"short_code": "r7f3Kx",
"appearance": { "foreground_color": "#1F4E79", "background_color": null }
},
"meta": { "request_id": "req_xyz", "issues": [] }
}
  • Werte: foreground_color als #RRGGBB, background_color als #RRGGBB oder transparent. Andere Schlüssel ergeben 400.
  • Zusammenführen: Ein weggelassenes Feld behält seinen gespeicherten Wert. null setzt ein Feld zurück, "appearance": null beide. Schwarz und Weiß werden nicht gespeichert; die Antwort zeigt sie als null.
  • Jede Code-Antwort enthält appearance, ebenso die Webhooks qr.created und qr.updated.
  • Reihenfolge in den Bildrouten: zuerst der Parameter, dann die gespeicherte Farbe, dann der Standard. ?fg=000000 liefert deshalb die schwarze Druckdatei eines farbigen Codes.
  • Eingebettete Bilder: Eine Bild-URL folgt den gespeicherten Farben, soweit sie sie nicht selbst mit fg und bg festlegt: eine URL ohne Parameter bei beiden Farben, ?fg=000000 beim Hintergrund, ?ecc=Q ebenfalls bei beiden. Nach einer Farbänderung kann eine Seite, die eine solche URL einbettet, noch das alte Bild zeigen: bis zu 24 Stunden, wenn die URL bis dahin das Standardbild lieferte (Schwarz auf Weiß, Fehlerkorrektur M, ohne Logo), sonst bis zu 5 Minuten. Abhilfe ist ein eigener Parameter an der URL, der sich mit jeder Farbänderung ändert, etwa ?v=2 oder das updated_at des Codes wie im Dashboard. Unbekannte Parameter ignorieren die Bildrouten.
  • Fehlerkorrektur wird nie gespeichert. Sie wird je Download mit ?ecc= gewählt.
  • Nur per PATCH: POST /v1/codes, der Batch und der Import lehnen appearance mit 422 ab.
  • PDF und EPS zeichnen gespeicherte Farben als Druckfarben, genau wie die Parameter. Weil Weiß nie gespeichert wird, bekommt ein Code mit gespeicherter Vordergrundfarbe dort eine weiße Fläche, wie im SVG.

Kontrastprüfung

Die API prüft das Paar, das sich aus der Anfrage und dem gespeicherten Wert ergibt:

StufeWannAntwort
blockedKontrast unter 1,5:1422, nichts wird gespeichert
criticalunter 2:1 oder Helligkeitsabstand unter 0,30; eine Warnung zusammen mit einem Logo; jeder transparente Hintergrund200 mit meta.issues
warningunter 4:1 oder Helligkeitsabstand unter 0,50; helle Module auf dunklem Grund200 mit meta.issues
okalles andere200, meta.issues ist leer

Jeder Eintrag in meta.issues hat code, severity, field, message und optional hints wie contrast_ratio — dieselbe Form wie die Compliance-Meldungen eines Digitalen Produktpasses. Ein transparenter Hintergrund wird nie gesperrt, weil ein heller Code auf dunkler Verpackung ein echter Anwendungsfall ist. Der Untergrund braucht trotzdem deutlichen Kontrast und rundum eine freie Ruhezone von 4 Modulen.

Das Hochladen und das Entfernen eines Logos (POST und DELETE /v1/codes/:id/logo) prüfen die gespeicherten Farben ebenso und liefern die Befunde ebenfalls in meta.issues: mit Logo wird aus einer Warnung critical, ohne Logo wieder eine Warnung.

Ändert eine andere Anfrage denselben Code im selben Moment, wendet die API die Änderungen auf den neuesten Stand an. Erst wenn das dreimal in Folge scheitert, antwortet sie mit 409; der Client lädt den Code dann neu und wiederholt die Änderung.


Multipart-Request, Feld file: PNG, JPEG oder WebP, höchstens 1 MB, erkannt an den Magic Bytes. Normalisiert das Bild auf ein transparentes 512×512-PNG und ersetzt ein bestehendes Logo.

Terminal-Fenster
curl -X POST https://qr3.app/v1/codes/qr_a1b2c3d4/logo \
-H "Authorization: Bearer qr3_sk_..." \

Antwort (HTTP 201): der aktualisierte Code, mit gesetztem logo_file_id.

Entfernt das Logo und löscht das gespeicherte Objekt. Idempotent — ein Aufruf ohne bestehendes Logo liefert weiterhin 200.

Ändert eine andere Anfrage gleichzeitig das Logo desselben Codes (ein zweiter Upload oder ein Entfernen), antworten POST und DELETE /v1/codes/:id/logo mit 409 (errors/conflict) und ändern nichts; ein hochgeladenes Bild wird verworfen. Lade den Code neu und versuche es erneut. Zwei gleichzeitige Aufrufe von DELETE /v1/codes/:id/logo sind kein Konflikt, beide liefern 200.

Ist es gesetzt, betten alle vier Formate — qr.svg, qr.png, qr.pdf und qr.eps — die Logo-Pixel ein und heben die Fehlerkorrektur auf H an. Der vollständige Vertrag — inklusive welche Änderung (Hinzufügen/Entfernen vs. Ersetzen) das Punktmuster ändert — sowie Druckhinweise stehen unter Logo im QR-Code.


Kommentare

Kommentare ermöglichen Feedback-Schleifen zwischen Agenturen und Kunden.

GET /v1/codes/:id/comments

POST /v1/codes/:id/comments

PATCH /v1/codes/:id/comments/:commentId

DELETE /v1/codes/:id/comments/:commentId

Dashboard-Kommentare werden dem erstellenden Nutzer zugeordnet (author_id); Kommentare über reine API-Keys bleiben unattribuiert (author_id: null). Löschen darf einen Kommentar nur der Autor selbst oder ein org_admin/ws_admin — reine API-Keys können nur unattribuierte API-Kommentare löschen.

Terminal-Fenster
# Kommentar hinzufügen
curl -X POST https://qr3.app/v1/codes/qr_a1b2c3d4/comments \
-H "Authorization: Bearer qr3_sk_..." \
-H "Content-Type: application/json" \
-d '{ "body": "QR-Code bitte auf grünen Hintergrund abstimmen.", "author_name": "Max Müller" }'
# Offene Kommentare auflisten
curl "https://qr3.app/v1/codes/qr_a1b2c3d4/comments?resolved=false" \
-H "Authorization: Bearer qr3_sk_..."
# Kommentar als erledigt markieren
curl -X PATCH https://qr3.app/v1/codes/qr_a1b2c3d4/comments/cmt_xyz \
-H "Authorization: Bearer qr3_sk_..." \
-H "Content-Type: application/json" \
-d '{ "resolved": true }'