QR-Codes für Entwickler: REST API, SDK und CLI

Wie du QR-Codes programmatisch generierst, in deine App integrierst und per Webhook auf Scans reagierst — ein technischer Deep-Dive.

von qr3.app Team

QR-Codes für Entwickler: REST API, SDK und CLI

Als Entwickler willst du QR-Codes nicht manuell über ein Dashboard erstellen — du willst sie automatisch generieren, in deine Build-Pipeline einbauen und per Webhook auf Scans reagieren. Das ist genau wofür qr3.app gebaut ist.

Der Stack

qr3.app ist vollständig Cloudflare-native:

  • API Worker: Hono auf Cloudflare Workers (V8-Runtime, keine Node.js-Abhängigkeiten)
  • Redirect Worker: Ultra-schlanker Worker, KV-gecachte Redirects in < 5ms p50
  • Datenbank: Cloudflare D1 (SQLite) mit Multi-Tenancy via workspace_id
  • Queue: Scan-Events über Cloudflare Queues entkoppelt verarbeitet

Authentifizierung

Alle API-Anfragen benötigen einen Bearer-Token und eine Workspace-ID:

POST https://qr3.app/v1/codes
Authorization: Bearer qr3_sk_...
Content-Type: application/json

API-Keys erstellst du unter app.qr3.app/dashboard/api-keys.

TypeScript SDK

npm install @qr3/sdk
# oder
pnpm add @qr3/sdk
import { QR3 } from "@qr3/sdk";

const client = new QR3({
  apiKey: process.env.QR3_API_KEY!,
  workspaceId: process.env.QR3_WORKSPACE_ID!,
});

// QR-Code erstellen
const { data: code } = await client.codes.create({
  type: "url",
  url: "https://mein-shop.de/produkt/42",
  title: "Produkt 42",
  tags: ["shop", "produkt"],
});

console.log(code.short_code);       // "r7f3Kx"
console.log(code.image_svg_url);    // SVG-Bild-URL
console.log(code.redirect_url);     // https://qr3.app/r7f3Kx

// URL später ändern (nur dynamische Codes)
await client.codes.update(code.id, {
  url: "https://mein-shop.de/produkt/42-neu",
});

// Analytics abfragen
const stats = await client.codes.stats(code.id, {
  from: "2026-01-01",
  to: "2026-03-31",
});
console.log(stats.data.total_scans); // z.B. 1247

REST API Direkt

Keine SDK-Abhängigkeit? Die REST API ist direkt nutzbar:

# QR-Code erstellen
curl -X POST https://qr3.app/v1/codes \
  -H "Authorization: Bearer $QR3_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "url",
    "url": "https://meine-seite.de",
    "title": "Meine Seite",
    "is_dynamic": true
  }'

# Alle QR-Codes auflisten
curl https://qr3.app/v1/codes?limit=20 \
  -H "Authorization: Bearer $QR3_API_KEY"

# Logo hochladen — wird ab sofort automatisch in SVG und PNG eingebettet
curl -X POST https://qr3.app/v1/codes/qr_abc123/logo \
  -H "Authorization: Bearer $QR3_API_KEY" \
  -F "[email protected]"

# QR-Code als SVG herunterladen (enthält jetzt das Logo)
curl "https://qr3.app/v1/codes/r7f3Kx/qr.svg?size=8" \
  -o qrcode.svg

CLI

Für Scripting und CI/CD gibt es die CLI @qr3/cli:

npm install -g @qr3/cli
qr3 login

# QR-Code erstellen und als SVG speichern
qr3 codes create --url "https://meine-seite.de" --title "CI Deploy" --output qr.svg

# Batch-Erstellung aus CSV
qr3 codes batch import urls.csv --format svg --output ./qr-codes/

Webhooks: Auf Scans reagieren

Webhooks sind der mächtigste Teil: Du kannst in Echtzeit auf jeden Scan reagieren.

// Webhook registrieren
const { data: webhook } = await client.webhooks.create({
  url: "https://deine-app.de/webhooks/qr3",
  events: ["qr.scanned", "qr.updated"],
});

Webhook-Payload

{
  "id": "evt_4f0c2a91d7e84b3c9a1f",
  "type": "qr.scanned",
  "created": "2026-03-15T14:30:00.000Z",
  "data": {
    "code_id": "qr_abc123",
    "short_code": "r7f3Kx",
    "scanned_at": "2026-03-15T14:30:00.000Z",
    "country": "DE",
    "device_type": "mobile",
    "os": "iOS",
    "browser": "Safari",
    "language": "de",
    "ab_variant": null,
    "redirected_to": "https://example.com/landing"
  }
}

Webhook verifizieren (HMAC)

import { createHmac, timingSafeEqual } from "crypto";

export async function verifyWebhook(
  payload: string,
  signature: string,
  secret: string,
): Promise<boolean> {
  const expected = createHmac("sha256", secret)
    .update(payload)
    .digest("hex");
  const expectedBytes = Buffer.from(`sha256=${expected}`);
  const receivedBytes = Buffer.from(signature);
  return (
    expectedBytes.length === receivedBytes.length &&
    timingSafeEqual(expectedBytes, receivedBytes)
  );
}

Rate Limiting

Jeder API-Key darf 60 Requests pro Minute senden, in allen Tarifen. Für das Anlegen von Codes gilt zusätzlich eine Tagesgrenze je Workspace, ebenfalls in allen Tarifen gleich: POST /v1/codes nimmt neue Codes an, bis an diesem Tag 200 angelegt sind, POST /v1/codes/batch bis zu 10.000.

Jede authentifizierte Response enthält X-RateLimit-Limit. Eine 429-Response nennt zusätzlich den Zeitpunkt für den nächsten Versuch:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1710510060
Retry-After: 60

Fehlerformat (RFC 7807)

Alle Fehler folgen RFC 7807 Problem Details:

{
  "type": "https://docs.qr3.app/errors/validation",
  "title": "Validation Error",
  "status": 422,
  "detail": "url is required for type 'url'",
  "errors": [
    { "field": "url", "message": "Required" }
  ]
}

Fazit

qr3.app ist von Anfang an als Developer-Tool gebaut: offene REST API, TypeScript-first SDK, CLI, Webhooks und vollständige OpenAPI-Dokumentation. Schau dir die API-Dokumentation an oder fange direkt im Dashboard an.