API de códigos QR
Descripción general
La API de códigos es el núcleo de qr3.app. Con ella puedes crear, actualizar y eliminar códigos QR dinámicos y estáticos.
URL base: https://qr3.app/v1/codes
Contrato REST vinculante
El siguiente contrato se aplica a todos los clientes:
POST /v1/codesacepta únicamente los tiposurl,vcard,wifi,email,smsylocation. Los campos son planos:url; al menosvcard_first_nameovcard_last_name;wifi_ssid;email_to;sms_phone; o amboslocation_latylocation_lng.- Solo los códigos
urlpueden ser dinámicos. Las solicitudes de creación pueden incluir opcionalmenteexpires_atcomo marca de tiempo ISO 8601;ab_enabled,ab_target_url_byab_weight_a(destinos A/B) se aceptan para códigosurldinámicos;redirect_after_expiryno forma parte del contrato de creación. GET /v1/codesadmite sololimit,cursorystatus(live,paused,flagged,draft).POST /v1/codes/batchaceptaurl,vcardywifi. El límite es 10 para Free, 500 para Pro y 1.000 registros para Business/Agency/Enterprise por solicitud. Los análisis de URL se ejecutan de forma síncrona para un máximo de 50 elementos URL; por encima de 50 se requiereskip_url_scan: true.
Crear código QR
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,q1Respuesta (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" }}Tipos de códigos QR
| Tipo | Descripción | Campos obligatorios |
|---|---|---|
url | URL del sitio web (dinámica o estática) | url |
vcard | Tarjeta de visita (vCard 3.0) | vcard_first_name o vcard_last_name |
wifi | Configuración Wi-Fi | wifi_ssid |
email | Correo electrónico (mailto:) | email_to |
sms | SMS | sms_phone |
location | Ubicación (geo:) | location_lat, location_lng |
Creación por lotes
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 }'Respuesta (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 }}Lista de códigos QR
GET /v1/codes
curl https://qr3.app/v1/codes?status=live&limit=20 \ -H "Authorization: Bearer qr3_sk_..."Parámetros de consulta:
| Parámetro | Tipo | Por defecto | Descripción |
|---|---|---|---|
cursor | string | — | Cursor para paginación |
limit | integer | 20 | Resultados por página (máx. 100) |
status | string | — | Filtro: live, paused, flagged, draft |
Obtener código QR
GET /v1/codes/:id
curl https://qr3.app/v1/codes/qr_a1b2c3d4 \ -H "Authorization: Bearer qr3_sk_..."Actualizar código QR
PATCH /v1/codes/:id
Update contract: url may be changed only for an existing type: "url" code, and its value must use http:// or https://. Other code types must not receive url; invalid requests return 422.
Los códigos QR dinámicos permiten cambiar la URL de destino en cualquier momento, sin tener que volver a imprimir el código QR.
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" }'Landing page y enlaces externos
Para códigos url dinámicos, puedes establecer is_landing_page: true (al crearlos o mediante PATCH). Al escanearlos, se mostrará una página alojada por qr3 con los archivos públicos y enlaces externos del código, en lugar de redirigir. Los enlaces externos se envían como un array links:
{ "is_landing_page": true, "links": [ { "label": "Datenblatt", "url": "https://example.com/datenblatt.pdf" }, { "label": "Zertifikat", "url": "https://example.com/zertifikat.pdf" } ]}- Entre 0 y 20 enlaces por código;
labeldebe tener de 1 a 100 caracteres;urldebe serhttp(s)(≤ 2048 caracteres). - Cada URL se comprueba con Google Web Risk; una URL no segura devuelve un error
422. - Si Web Risk no está disponible al guardar, el enlace se aceptará de todos modos, pero se marcará para una nueva comprobación. Una tarea diaria vuelve a comprobar los enlaces guardados (y reevalúa periódicamente los clasificados como seguros) y pausa el código automáticamente si un enlace se detecta más tarde como no seguro.
"links": []elimina todos los enlaces. Consulta la guía de landing pages.
Eliminar código QR
DELETE /v1/codes/:id
Eliminación suave (Soft-Delete): el código QR se archiva y los datos de escaneo se conservan.
Un segundo DELETE del mismo código devuelve 404, incluso si ambas solicitudes llegan al mismo tiempo. El webhook qr.deleted se envía exactamente una vez.
curl -X DELETE https://qr3.app/v1/codes/qr_a1b2c3d4 \ -H "Authorization: Bearer qr3_sk_..."Descargar imágenes de QR
Todos los formatos de imagen son de acceso público; no se requiere autenticación.
| Formato | URL | Uso |
|---|---|---|
| SVG (vectorial) | /v1/codes/:code/qr.svg | Web, escalado, digital |
| PNG (rasterizado) | /v1/codes/:code/qr.png | Correo electrónico, presentaciones |
| PDF (vectorial) | /v1/codes/:code/qr.pdf | Por defecto: cuadrado (solo el código); ?format=a4 para una hoja de impresión |
| EPS (vectorial) | /v1/codes/:code/qr.eps | Flujos de trabajo de impresión profesional (Adobe, imprentas) |
Opcional: ?size=N — Tamaño del módulo en píxeles (2–20, por defecto: 4) para SVG, PNG y EPS. El PDF utiliza un tamaño de módulo fijo.
Solo PDF: ?format=a4|square — Formato de página (por defecto: square — solo código + zona de silencio, sin espacio en blanco A4; a4 para una hoja A4 lista para imprimir)
# 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.pdfColores y corrección de errores
Las cuatro rutas de imagen aceptan tres parámetros opcionales. Se aplican a esa solicitud específica y tienen prioridad sobre los colores guardados en el código (siguiente sección).
| Parámetro | Valores | Predeterminado | SVG, PNG | PDF, EPS |
|---|---|---|---|---|
fg | Hex RRGGBB o RGB | 000000 | se dibuja | se dibuja como color de impresión |
bg | Hex como fg o transparent | ffffff | se dibuja | se dibuja como color de impresión |
ecc | L, M, Q, H | M | se aplica | se aplica |
- Formato: No distingue entre mayúsculas y minúsculas, el
#es opcional. Si se incluye, debe codificarse como%23. - Los valores no válidos cuentan como no establecidos.
?fg=liladevuelve el color guardado en el código, o el negro normal si no hay ninguno guardado, con200y nunca un error. Solo un?fg=000000explícito fuerza el negro. bg=transparentdevuelve un SVG, PDF o EPS sin fondo y un PNG con canal alfa real. La superficie sobre la que se coloque el código debe ser clara y dejar una zona de silencio de 4 módulos alrededor. Un código oscuro sobre un fondo oscuro no es legible.- PDF y EPS escriben el negro y el gris como escala de grises (solo la placa negra) y cualquier otro color como CMYK en porcentajes enteros, por ejemplo
1F4E79como C74 M36 Y0 K53. En cuanto se elige un color, un fondo opaco se sitúa detrás del código y su zona de silencio, en blanco o en el color debg, al igual que en el SVG. Sin colores, ambos archivos permanecen sin cambios. Conversión y límites: Colores en la impresión. - Contraste: La ruta de la imagen no lo comprueba. Se recomienda un mínimo de 4:1 y módulos oscuros sobre fondo claro.
#1F4E79sobre blanco tiene 8,7:1,#ff6600sobre blanco solo 2,9:1. ecccambia el patrón de puntos, no el contenido. Un código impreso seguirá funcionando, pero no se deben mezclar archivos de impresión antiguos y nuevos.QoHhacen que el código sea más robusto, por ejemplo, en cartón ondulado. Un logotipo siempre fuerzaH.- Con logotipo el área detrás del logotipo permanece blanca, incluso con un fondo de color o transparente.
- Caché: solo la imagen estándar real (negro sobre blanco, corrección de errores M, sin logotipo) se entrega como inmutable durante 24 horas; cualquier otra representación durante 5 minutos. Para PDF y EPS, cualquier fondo especificado cuenta como una desviación:
bg=ffffffdibuja allí un relleno blanco que el archivo estándar no tiene. - Plan: Los colores y la corrección de errores están disponibles en todos los planes, incluido el gratuito.
# Dark blue code on whitecurl "https://qr3.app/v1/codes/r7f3Kx/qr.svg?fg=1F4E79" -o qr-blue.svg
# Transparent PNG for a layout on a light surfacecurl "https://qr3.app/v1/codes/r7f3Kx/qr.png?size=10&bg=transparent" -o qr-transparent.png
# More robust for corrugated board: error correction Qcurl "https://qr3.app/v1/codes/r7f3Kx/qr.pdf?ecc=Q" -o qr-q.pdf// @qr3/sdk 1.2.0 or later — imageUrl() only builds the URL, it sends no request// Dark blue code on whiteqr3.codes.imageUrl('r7f3Kx', { format: 'svg', fg: '1F4E79' });// → https://qr3.app/v1/codes/r7f3Kx/qr.svg?fg=1F4E79
// Transparent PNG for a layout on a light surfaceqr3.codes.imageUrl('r7f3Kx', { format: 'png', size: 10, bg: 'transparent' });
// More robust for corrugated board: error correction Qqr3.codes.imageUrl('r7f3Kx', { format: 'pdf', ecc: 'Q' });Guardar colores en el código
PATCH /v1/codes/:id guarda los colores como appearance en el código. Las rutas de imagen los dibujan por defecto, sin parámetros.
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 1.2.0 or laterconst code = await qr3.codes.update('qr_a1b2c3d4', { appearance: { foreground_color: '#1F4E79' },});code.appearance; // { foreground_color: '#1F4E79', background_color: null }code.issues; // contrast check findings, absent when there are noneRespuesta (HTTP 200, abreviada):
{ "data": { "id": "qr_a1b2c3d4", "short_code": "r7f3Kx", "appearance": { "foreground_color": "#1F4E79", "background_color": null } }, "meta": { "request_id": "req_xyz", "issues": [] }}- Valores:
foreground_colorcomo#RRGGBB,background_colorcomo#RRGGBBotransparent. Otras claves devuelven400. - Combinación: Un campo omitido conserva su valor guardado.
nullrestablece un campo,"appearance": nullambos. El negro y el blanco no se guardan; la respuesta los muestra comonull. - Cada respuesta de código contiene
appearance, al igual que los webhooksqr.createdyqr.updated. - Orden de prioridad en las rutas de imagen: primero el parámetro, luego el color guardado, luego el valor por defecto. Por lo tanto,
?fg=000000devuelve el archivo de impresión en negro de un código de color. - Imágenes incrustadas: Una URL de imagen sigue los colores guardados, siempre que no los establezca por sí misma con
fgybg: una URL sin parámetros para ambos colores,?fg=000000para el fondo,?ecc=Qtambién para ambos. Después de un cambio de color, una página que incruste dicha URL aún puede mostrar la imagen antigua: hasta 24 horas si la URL entregaba la imagen predeterminada hasta entonces (negro sobre blanco, corrección de errores M, sin logo), de lo contrario hasta 5 minutos. La solución es un parámetro propio en la URL que cambie con cada cambio de color, como?v=2o elupdated_atdel código como en el Dashboard. Las rutas de imagen ignoran los parámetros desconocidos. - La corrección de errores nunca se guarda. Se selecciona para cada descarga con
?ecc=. - Solo mediante
PATCH:POST /v1/codes, el lote y la importación rechazanappearancecon422. - PDF y EPS dibujan los colores guardados como colores de impresión, al igual que los parámetros. Como el blanco nunca se guarda, un código con un color de primer plano guardado obtiene un relleno blanco allí, al igual que en el SVG.
Verificación de contraste
La API verifica el par resultante de la solicitud y el valor guardado:
| Nivel | Cuándo | Respuesta |
|---|---|---|
blocked | Contraste inferior a 1,5:1 | 422, no se guarda nada |
critical | inferior a 2:1 o diferencia de luminosidad inferior a 0,30; una advertencia junto con un logotipo; cualquier fondo transparente | 200 con meta.issues |
warning | inferior a 4:1 o diferencia de luminosidad inferior a 0,50; módulos claros sobre fondo oscuro | 200 con meta.issues |
| ok | cualquier otro caso | 200, meta.issues está vacío |
Cada entrada en meta.issues tiene code, severity, field, message y, opcionalmente, hints como contrast_ratio, con el mismo formato que los mensajes de conformidad de un pasaporte digital de producto. Un fondo transparente nunca se bloquea, ya que un código claro sobre un embalaje oscuro es un caso de uso real. No obstante, el fondo sigue necesitando un contraste claro y una zona de silencio despejada de 4 módulos a su alrededor.
Subir y eliminar un logotipo (POST y DELETE /v1/codes/:id/logo) realizan la misma comprobación en los colores guardados y también devuelven los resultados en meta.issues: con un logotipo una advertencia pasa a ser critical, sin él vuelve a ser una advertencia.
Si otra solicitud modifica el mismo código en el mismo instante, la API aplica los cambios a la última versión. Solo si esto falla tres veces consecutivas, responderá con 409; en ese caso, el cliente vuelve a cargar el código y repite la modificación.
Logo
POST /v1/codes/:id/logo
Solicitud multipart, campo file: PNG, JPEG o WebP, máximo 1 MB, detectado por los bytes mágicos. Normaliza la imagen a un PNG transparente de 512×512 y reemplaza un logo existente.
curl -X POST https://qr3.app/v1/codes/qr_a1b2c3d4/logo \ -H "Authorization: Bearer qr3_sk_..." \Antwort (HTTP 201): el código actualizado, con logo_file_id establecido.
DELETE /v1/codes/:id/logo
Elimina el logo y borra el objeto almacenado. Idempotente — una llamada sin un logo existente sigue devolviendo 200.
Si otra solicitud cambia el logo del mismo código en el mismo instante (una segunda subida o una eliminación), POST y DELETE /v1/codes/:id/logo responden con 409 (errors/conflict) y no cambian nada; la imagen subida se descarta. Vuelve a cargar el código e inténtalo de nuevo. Dos llamadas simultáneas de DELETE /v1/codes/:id/logo no son un conflicto; ambas devuelven 200.
Si está establecido, los cuatro formatos — qr.svg, qr.png, qr.pdf y qr.eps — incrustan los píxeles del logo y elevan la corrección de errores a H. El contrato completo — incluyendo qué cambio (añadir/eliminar vs. reemplazar) modifica el patrón de puntos — así como las instrucciones de impresión se encuentran en Logo en el código QR.
Comentarios
Los comentarios permiten ciclos de retroalimentación entre agencias y clientes.
GET /v1/codes/:id/comments
POST /v1/codes/:id/comments
PATCH /v1/codes/:id/comments/:commentId
DELETE /v1/codes/:id/comments/:commentId
Los comentarios del dashboard se atribuyen al usuario creador (author_id); los comentarios creados a través de API-keys puras permanecen sin atribuir (author_id: null). Solo el propio autor o un org_admin/ws_admin puede eliminar un comentario; las API-keys puras solo pueden eliminar comentarios de la API que no estén atribuidos.
# 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 }'