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/codesakzeptiert nur die Typenurl,vcard,wifi,email,smsundlocation. Die Felder sind flach:url; mindestensvcard_first_nameodervcard_last_name;wifi_ssid;email_to;sms_phone; beziehungsweiselocation_latundlocation_lng.- Nur
url-Codes können dynamisch sein. Erstellen-Aufrufe dürfen optionalexpires_atmit einem ISO-8601-Zeitpunkt enthalten;ab_enabled,ab_target_url_bundab_weight_a(A/B-Destinations) sind für dynamischeurl-Codes zulässig;redirect_after_expirygehört nicht zum Erstellen-Vertrag. GET /v1/codesunterstützt ausschließlichlimit,cursorundstatus(live,paused,flagged,draft).POST /v1/codes/batchakzeptierturl,vcardundwifi. 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 istskip_url_scan: trueerforderlich.
QR-Code erstellen
POST /v1/codes
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 }'const code = await qr3.codes.create({ type: 'url', url: 'https://example.com', title: 'Mein erster QR-Code', tags: ['marketing', 'q1'], is_dynamic: true,});qr3 create https://example.com --title "Mein QR-Code" --tags marketing,q1Response (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
| Typ | Beschreibung | Pflichtfelder |
|---|---|---|
url | Website-URL (dynamisch oder statisch) | url |
vcard | Visitenkarte (vCard 3.0) | vcard_first_name oder vcard_last_name |
wifi | WLAN-Konfiguration | wifi_ssid |
email | E-Mail (mailto:) | email_to |
sms | SMS | sms_phone |
location | Standort (geo:) | location_lat, location_lng |
Batch-Erstellung
POST /v1/codes/batch
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
curl https://qr3.app/v1/codes?status=live&limit=20 \ -H "Authorization: Bearer qr3_sk_..."Query-Parameter:
| Parameter | Typ | Standard | Beschreibung |
|---|---|---|---|
cursor | string | — | Cursor für Pagination |
limit | integer | 20 | Ergebnisse pro Seite (max. 100) |
status | string | — | Filter: live, paused, flagged, draft |
QR-Code abrufen
GET /v1/codes/:id
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.
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" }'Landingpage & externe Links
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;
labelist 1–100 Zeichen;urlmusshttp(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.
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.
| Format | URL | Verwendung |
|---|---|---|
| SVG (Vektor) | /v1/codes/:code/qr.svg | Web, Scaling, Digital |
| PNG (Raster) | /v1/codes/:code/qr.png | E-Mail, Präsentationen |
| PDF (Vektor) | /v1/codes/:code/qr.pdf | Standard: quadratisch (nur der Code); ?format=a4 für ein Druckblatt |
| EPS (Vektor) | /v1/codes/:code/qr.eps | Profi-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)
# SVG für Webcurl https://qr3.app/v1/codes/r7f3Kx/qr.svg
# PNG in hoher Auflösungcurl 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-Blattcurl "https://qr3.app/v1/codes/r7f3Kx/qr.pdf?format=a4" -o qr-a4.pdfFarben 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).
| Parameter | Werte | Standard | SVG, PNG | PDF, EPS |
|---|---|---|---|---|
fg | Hex RRGGBB oder RGB | 000000 | wird gezeichnet | wird als Druckfarbe gezeichnet |
bg | Hex wie fg oder transparent | ffffff | wird gezeichnet | wird als Druckfarbe gezeichnet |
ecc | L, M, Q, H | M | wirkt | wirkt |
- Schreibweise: Groß- und Kleinschreibung sind egal, das
#ist optional. Mitgeschickt wird es als%23kodiert. - Ungültige Werte zählen als nicht gesetzt.
?fg=lilaliefert die am Code gespeicherte Farbe, ohne gespeicherte Farbe das normale Schwarz, mit200und nie einen Fehler. Nur ein ausdrückliches?fg=000000erzwingt Schwarz. bg=transparentliefert 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
1F4E79als 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 vonbg, 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.
#1F4E79auf Weiß hat 8,7:1,#ff6600auf 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.QoderHmachen den Code robuster, etwa auf Wellpappe. Ein Logo erzwingt immerH.- 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=ffffffzeichnet 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.
# 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 Untergrundcurl "https://qr3.app/v1/codes/r7f3Kx/qr.png?size=10&bg=transparent" -o qr-transparent.png
# Robuster für Wellpappe: Fehlerkorrektur Qcurl "https://qr3.app/v1/codes/r7f3Kx/qr.pdf?ecc=Q" -o qr-q.pdf// @qr3/sdk ab 1.2.0 — imageUrl() baut nur die URL, es sendet keine Anfrage// Dunkelblauer Code auf Weißqr3.codes.imageUrl('r7f3Kx', { format: 'svg', fg: '1F4E79' });// → https://qr3.app/v1/codes/r7f3Kx/qr.svg?fg=1F4E79
// Transparentes PNG für ein Layout auf hellem Untergrundqr3.codes.imageUrl('r7f3Kx', { format: 'png', size: 10, bg: 'transparent' });
// Robuster für Wellpappe: Fehlerkorrektur Qqr3.codes.imageUrl('r7f3Kx', { format: 'pdf', ecc: 'Q' });Farben am Code speichern
PATCH /v1/codes/:id speichert Farben als appearance am Code. Die Bildrouten zeichnen sie dann standardmäßig, ohne Parameter.
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"}}'// @qr3/sdk ab 1.2.0const code = await qr3.codes.update('qr_a1b2c3d4', { appearance: { foreground_color: '#1F4E79' },});code.appearance; // { foreground_color: '#1F4E79', background_color: null }code.issues; // Befunde der Kontrastprüfung, fehlt ohne BefundAntwort (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_colorals#RRGGBB,background_colorals#RRGGBBodertransparent. Andere Schlüssel ergeben400. - Zusammenführen: Ein weggelassenes Feld behält seinen gespeicherten Wert.
nullsetzt ein Feld zurück,"appearance": nullbeide. Schwarz und Weiß werden nicht gespeichert; die Antwort zeigt sie alsnull. - Jede Code-Antwort enthält
appearance, ebenso die Webhooksqr.createdundqr.updated. - Reihenfolge in den Bildrouten: zuerst der Parameter, dann die gespeicherte Farbe, dann der Standard.
?fg=000000liefert deshalb die schwarze Druckdatei eines farbigen Codes. - Eingebettete Bilder: Eine Bild-URL folgt den gespeicherten Farben, soweit sie sie nicht selbst mit
fgundbgfestlegt: eine URL ohne Parameter bei beiden Farben,?fg=000000beim Hintergrund,?ecc=Qebenfalls 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=2oder dasupdated_atdes 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 lehnenappearancemit422ab. - 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:
| Stufe | Wann | Antwort |
|---|---|---|
blocked | Kontrast unter 1,5:1 | 422, nichts wird gespeichert |
critical | unter 2:1 oder Helligkeitsabstand unter 0,30; eine Warnung zusammen mit einem Logo; jeder transparente Hintergrund | 200 mit meta.issues |
warning | unter 4:1 oder Helligkeitsabstand unter 0,50; helle Module auf dunklem Grund | 200 mit meta.issues |
| ok | alles andere | 200, 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.
Logo
POST /v1/codes/:id/logo
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.
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.
DELETE /v1/codes/:id/logo
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.
# Kommentar hinzufügencurl -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 auflistencurl "https://qr3.app/v1/codes/qr_a1b2c3d4/comments?resolved=false" \ -H "Authorization: Bearer qr3_sk_..."
# Kommentar als erledigt markierencurl -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 }'