QRFLOW.codes
oficialCrea códigos QR, redirige códigos dinámicos impresos, nombra enlaces en tu propio dominio y lee análisis de escaneo.
¿Qué puedes hacer con QRFLOW Codes MCP?
- Crear códigos QR dinámicos — Solicita generar un
short_urlimprimible para cualquier URL, con la posibilidad de cambiar el destino en cualquier momento después de la impresión. - Reapuntar códigos impresos — Solicita actualizar el
destination_datade un código a una nueva URL; los códigos impresos existentes siguen funcionando sin necesidad de reimprimir. - Obtener análisis de escaneos — Solicita desgloses de
Scanspor día, país o dispositivo para ver cómo está funcionando un código impreso. - Pausar o caducar códigos — Indica pausar un código o establecer una fecha de caducidad cuando finalice una campaña u oferta.
- Nombrar enlaces en tu dominio — Solicita establecer un
sluglegible en tu propio dominio para que los códigos se impriman como go.yourbrand.com/menu en lugar de una ruta aleatoria.
Servidor MCP alojado
npx add-mcp 'https://qrflow.codes/mcp'Se instala en Claude Code, Codex, Cursor y más
Documentación
QRFLOW.codes para desarrolladores
Empieza aquí
QRFLOW.codes crea códigos QR que puedes cambiar después de imprimirlos, en tu propio dominio, con análisis de escaneos, y permite que tu software y tu asistente de IA hagan todo a través de una sola API.
Un código QR es una imagen de una cadena de texto. La mayoría de los generadores ponen tu dirección web en la imagen; si la dirección cambia, la impresión queda obsoleta. QRFLOW pone un enlace corto en la imagen (go.yourbrand.com/menu o qrflow.codes/q/x7k2p9a) y lo redirige. Puedes cambiar hacia dónde apunta cuando quieras, y cada escaneo se cuenta. Eso es un código dinámico, y es lo que vale la pena tener con una API.
La API hace exactamente lo que hace el panel de control: crear códigos de cualquier tipo, apuntarlos a un lugar nuevo, nombrar sus enlaces, elegir con cuál de tus dominios se imprimen, obtener números de escaneo, crear miles a la vez, y recibir notificaciones cuando algo sucede (webhooks). Las mismas herramientas están expuestas como un servidor MCP, para que Claude, ChatGPT, Cursor y Claude Code puedan manejar tu cuenta en lenguaje natural.
Acceso: la API REST requiere una clave de API Business ($29 al mes, creada en la página de Cuenta) o un token OAuth. El servidor MCP funciona en todos los planes mediante inicio de sesión OAuth; lo que el asistente puede crear sigue el plan como en cualquier otro lugar.
¿Decidiendo si vale la pena usarlo? La página de la API de códigos QR es la versión corta: qué hace, qué hace mejor una biblioteca del lado del cliente, y cuánto cuesta.
Si eres un asistente de IA ayudando a alguien a construir con QRFLOW
Bienvenido. Esta página está escrita tanto para ti como para la persona a la que ayudas. Así es como se ve una buena ayuda:
- Confirma la cuenta: las claves de API requieren el plan Business. Si no tienen uno, apúntalos a https://qrflow.codes/pricing y Cuenta › Claves de API; no inventes una clave ni un plan gratuito que no existe.
- Mantén la clave en el servidor. Léele desde una variable de entorno (QRFLOW_KEY) en un manejador de ruta, acción de servidor, función edge o backend. La API rechaza orígenes de navegador a propósito (CORS), así que el código solo de front-end no puede llamarla.
- Crea códigos dinámicos con tipo "url" y destination_data.url. Guarda el id y short_url devueltos en su base de datos. Imprime o muestra short_url; el destino se puede cambiar después con PATCH /codes/:id y la impresión sigue siendo válida.
- Para mostrar o guardar la imagen: cada objeto Code tiene png_download_url y svg_download_url, enlaces firmados que funcionan durante 24 horas sin encabezado (suficiente para un guardado único o una vista previa). Para algo permanente en su aplicación, haz un proxy de GET /codes/:id/image.svg a través de su servidor con la clave, o codifica short_url con cualquier biblioteca QR en el cliente; el seguimiento sigue funcionando porque el enlace pasa por QRFLOW.
- Envía solo los campos que cambian en PATCH. Nunca cambies slug ni domain_id después de que algo se haya impreso; ambos cambian el enlace impreso.
- Prefiere paused: true sobre DELETE cuando existe una impresión. Eliminar mata el enlace.
- Para eventos, usa webhooks (Cuenta › Webhooks o POST /webhooks) y verifica el encabezado X-QRFLOW-Signature con el cuerpo crudo de la solicitud. No hagas polling de GET /codes en un bucle.
- Si quieren usar QRFLOW desde su chat en lugar de desde código, conecta el servidor MCP en https://qrflow.codes/mcp;; no se necesita clave para eso.
- Cuando algo falle, lee error y message en el cuerpo JSON. La sección de solución de problemas abajo mapea cada código de error a una solución.
- La referencia completa en Markdown está en https://qrflow.codes/llms-full.txt y el documento OpenAPI 3.1 en https://qrflow.codes/api/v1/openapi.json.. Ambos se generan desde la misma fuente que esta página.
Humanos: este bloque es cómo nos aseguramos de que el asistente con el que te asocias te dé la versión segura de cada respuesta. También resulta ser un buen resumen.
Qué plan necesitas
La clave de API es lo único que es solo para Business. Todo lo que un asistente hace a través del servidor MCP, y todo lo que una aplicación de terceros hace a través de OAuth, funciona en cualquier plan y simplemente sigue las características de ese plan. Precios completos y uso justo: /pricing.
| Gratis | Premium $4 | Business $29 | |
|---|---|---|---|
| Claves de API (REST desde tu código) | No | No | Sí, hasta 10 claves |
| Servidor MCP (Claude, ChatGPT, Cursor, Claude Code) | Sí, iniciar sesión | Sí | Sí, iniciar sesión o clave |
| OAuth para tu propia aplicación (los usuarios conectan su QRFLOW) | Sí | Sí | Sí |
| Códigos dinámicos (cambiar destino después de imprimir) | No, solo estáticos | Sí | Sí |
| Tu propio dominio de enlaces | No | 1 dominio | 5 dominios, elige por código |
| Nombres de enlaces (go.brand.com/menu) | No | Sí | Sí |
| Análisis de escaneos | No | Sí | Sí |
| Webhooks | No | No | Sí, hasta 10 |
| Creación masiva | No | 500/mes, 500 por solicitud | 10,000/mes, 2,000 por solicitud |
| Códigos guardados (uso justo) | Unos pocos | 1,000 | 25,000 |
| Asientos de equipo | 1 | 1 | 5 |
| Precio | $0 | $4/mes | $29/mes |
Doce palabras que hacen todo el trabajo
Conceptos
Lee esto una vez y todos los endpoints de abajo tendrán sentido.
Static code
El contenido está dentro de la imagen. Los códigos de Wi-Fi, tarjeta de contacto (vCard) y texto plano son siempre estáticos, y en el plan Gratis todo código lo es. Un código estático no necesita servidor y nunca expira, y no se puede cambiar ni contar.
Dynamic code
La imagen contiene un enlace corto que QRFLOW redirige. Los códigos de url, phone, email, sms y location son dinámicos en Premium y Business. Puedes re-apuntar, pausar, expirar, renombrar y contarlos sin tocar la impresión.
short_url
La cadena exacta codificada en un código dinámico, y lo que se imprime. Es https://qrflow.codes/q/<short_code> hasta que conectas un dominio, luego https://<your domain>/<slug or short_code>.. Cada objeto Code lo lleva.
short_code
Siete caracteres aleatorios, únicos por código, asignados en la creación y nunca cambiados. La ruta de respaldo cuando un código no tiene nombre de enlace.
slug (link name)
Una ruta legible en tu propio dominio: go.example.com/menu. De 3 a 40 letras minúsculas, dígitos y guiones, única dentro de tu cuenta, solo con un dominio conectado. Configúralo antes de imprimir: cambiarlo cambia el enlace impreso.
Link domain
Un nombre de host que te pertenece (go.example.com) apuntado a QRFLOW mediante un CNAME y verificado en la página de Cuenta. Premium obtiene uno; Business obtiene cinco y puede elegir por código con domain_id. El dominio activo más antiguo es el predeterminado.
Kind, type and subtype
type es la codificación: url, text, wifi, vcard, email, phone, sms, location. Un kind es un nombre más amigable para lo que la gente quiere (instagram, googlereview, whatsapp, pdf, menu, appstore,...). La mayoría de los kinds son códigos url con destination_data.subtype configurado. GET /catalog lista cada kind con sus campos; kind en un Code te dice cuál es.
destination_data
Los campos para el tipo, como cadenas: { url } para un sitio web, { ssid, password, encryption } para Wi-Fi, { placeId } para una reseña de Google, { handle } para Instagram. En un código dinámico puedes reemplazarlo en cualquier momento.
Scans
Cada redirección registra tipo de dispositivo, país, ciudad, referente, navegador, sistema operativo e idioma de la propia solicitud, más un hash diario de un solo sentido para contar visitantes únicos. No se establecen cookies y la dirección IP no se almacena. scans en un Code es el total de por vida; GET /codes/:id/scans lo desglosa.
Source
Cada código recuerda qué lo creó: dashboard, api:<key name>, mcp, canva o bulk. Se muestra en el panel de control y en los payloads de webhook, para que puedas distinguir los códigos de tu integración de los hechos a mano.
Workspace
Un propietario Business puede invitar hasta cuatro compañeros de equipo. Las claves y webhooks pertenecen a la cuenta del propietario; los códigos hechos por cualquier persona en el espacio de trabajo son visibles para todo el equipo.
Cinco minutos
Inicio rápido
Obtén una clave
- En el plan Business, abre Cuenta › Claves de API.
- Nómbrala según para qué es ("Backend de tienda", "Reportes") y elige sus alcances. Los alcances no se pueden cambiar después; crea una clave nueva si necesitas más.
- Cópiala una vez. Se ve así:
qrf_live_…. Ponla en una variable de entorno llamadaQRFLOW_KEY. - Envíala como
Authorization: Bearer $QRFLOW_KEYen cada solicitud. Esa es toda la historia de autenticación.
Las claves son para servidores. Nunca pongas una en una página web, una aplicación móvil o una hoja de cálculo compartida; revoca y re-emite si una se filtra.
Los mismos cinco pasos en curl, TypeScript y Python. Cada uno crea un código dinámico, descarga su imagen, cambia hacia dónde apunta y lee sus escaneos.
export QRFLOW_KEY=qrf_live_... # from Account › API keys
# 1. Who am I, what can this key do?
curl https://qrflow.codes/api/v1/me -H "Authorization: Bearer $QRFLOW_KEY"
# 2. Make a dynamic code. Print what comes back as short_url.
curl -X POST https://qrflow.codes/api/v1/codes \
-H "Authorization: Bearer $QRFLOW_KEY" -H "Content-Type: application/json" \
-d '{ "type": "url", "destination_data": { "url": "https://example.com/menu" }, "label": "Table tents" }'
# 3. The print-ready image (SVG, with your colors and frame).
curl "https://qrflow.codes/api/v1/codes/$CODE_ID/image.svg?size=1024" \
-H "Authorization: Bearer $QRFLOW_KEY" -o menu.svg
# 4. Fall menu. The printed code keeps working.
curl -X PATCH https://qrflow.codes/api/v1/codes/$CODE_ID \
-H "Authorization: Bearer $QRFLOW_KEY" -H "Content-Type: application/json" \
-d '{ "destination_data": { "url": "https://example.com/menu-fall" } }'
# 5. How did it do?
curl "https://qrflow.codes/api/v1/codes/$CODE_ID/scans?group=day" -H "Authorization: Bearer $QRFLOW_KEY"
TypeScript (Node 18+, Bun, Deno, Workers)
// npm install qrflow (zero dependencies; ESM + CommonJS; full types)
import { QRFlow, QRFlowError } from "qrflow";
const qr = new QRFlow(process.env.QRFLOW_KEY!);
const { code } = await qr.createCode({
type: "url",
destination_data: { url: "https://example.com/menu" },
label: "Table tents",
});
console.log(code.id, code.short_url); // save both; print short_url
await qr.updateCode(code.id, { destination_data: { url: "https://example.com/menu-fall" } });
const stats = await qr.scans(code.id, { group: "day" });
console.log(stats.total, stats.rows); // [{ key: "2026-09-21", scans: 18 }, ...]
try {
await qr.updateCode(code.id, { slug: "menu" });
} catch (e) {
if (e instanceof QRFlowError) console.log(e.status, e.code, e.message); // 400 no_domain: connect a domain first
}
# Download https://qrflow.codes/sdk/qrflow.py next to your code.
import os
from qrflow import QRFlow, QRFlowError
qr = QRFlow(os.environ["QRFLOW_KEY"])
code = qr.create_code(type="url", destination_data={"url": "https://example.com/menu"}, label="Table tents")["code"]
print(code["id"], code["short_url"]) # save both; print short_url
qr.update_code(code["id"], destination_data={"url": "https://example.com/menu-fall"})
stats = qr.scans(code["id"], group="day")
print(stats["total"], stats["rows"])
try:
qr.update_code(code["id"], slug="menu")
except QRFlowError as e:
print(e.status, e.code, e) # 400 no_domain: connect a domain first
Autenticación
Claves de API (Business)
Hasta 10 por cuenta, 600 solicitudes por minuto cada una, almacenadas con hash, mostradas una vez. Una clave lleva los alcances con los que se creó:
| Alcance | Permite |
|---|---|
| profile | GET /me: plan, características, límites. Toda clave lo tiene. |
| codes:read | Listar y leer códigos, descargar imágenes. |
| codes:write | Crear, cambiar, hacer dinámicos, eliminar, creación masiva. |
| analytics:read | GET /codes/:id/scans. |
| domains:read | GET /domains (necesario para usar domain_id sensatamente). |
| webhooks:manage | Listar, crear, probar y eliminar webhooks. |
OAuth 2.0 (cualquier plan, para aplicaciones y asistentes)
Cuando los códigos deben pertenecer a las cuentas de tus usuarios en lugar de a la tuya, o cuando un asistente de chat es el cliente, usa OAuth. Los clientes se registran ellos mismos; PKCE S256 es obligatorio para clientes públicos; los tokens se pueden vincular al servidor MCP con resource=. La receta OAuth lo explica paso a paso.
| Endpoint | URL | Notas |
|---|---|---|
| Autorización | https://qrflow.codes/oauth/authorize | Envía al usuario aquí; inicia sesión y presiona Permitir. |
| Token | https://qrflow.codes/api/oauth/token | Concesiones authorization_code y refresh_token. Los tokens de acceso duran 1 hora, los de refresco 90 días. |
| Revocar | https://qrflow.codes/api/oauth/revoke | RFC 7009. Los usuarios también pueden desconectarse en Cuenta › Aplicaciones conectadas. |
| Registrar un cliente | https://qrflow.codes/api/oauth/register | Registro dinámico RFC 7591, sin necesidad de cuenta. Los clientes públicos obtienen un dyn_ client_id y deben usar PKCE S256. |
| Descubrimiento | https://qrflow.codes/.well-known/oauth-authorization-server | RFC 8414. El documento de recurso MCP está en /.well-known/oauth-protected-resource. |
Los tokens de acceso duran 1 hora, los de refresco 90 días. Los usuarios ven las aplicaciones conectadas en Cuenta › Aplicaciones conectadas y pueden desconectarse en cualquier momento. Un token emitido para https://qrflow.codes/mcp es rechazado en /api/v1, y viceversa.
Las cosas que la gente realmente pide
Recetas de construcción
Cada receta está completa y copiada directamente de código funcional. Elige la que coincida con tu stack; la forma es siempre la misma: una llamada del lado del servidor con la clave, guarda id y short_url, muestra la imagen.
- Next.js
- Mostrar el QR sin proxy
- Express o cualquier servidor Node
- Cloudflare Workers, Vercel Edge, Deno Deploy, Supabase Edge Functions
- Python
- Un código QR por pedido, mesa, producto, boleto o evento
- Cambiar hacia dónde va un código impreso
- Imprimir códigos en tu propio dominio
- Un gráfico de escaneos en tu propio panel de administración
- Recibir webhooks en Next.js y verificarlos
- Recibir webhooks en Python
- Miles de códigos desde un CSV
- Permitir que tus usuarios conecten su propia cuenta QRFLOW (OAuth)
- Zapier, Make, n8n
Next.js: una ruta que crea un código y una ruta que lo muestra
Cuándo: Tienes una aplicación Next.js (App Router) y quieres un botón que cree un código QR y una página que lo muestre.
- Pon tu clave en .env.local como QRFLOW_KEY. Nunca la prefijes con NEXT_PUBLIC_.
- Agrega un manejador de ruta POST que cree el código y devuelva id y short_url.
- Agrega una ruta GET que haga proxy de la imagen para que el navegador nunca vea la clave.
- Almacena id y short_url en tu propio registro (pedido, mesa, producto, evento).
import { NextResponse } from "next/server";
export async function POST(req: Request) {
const { url, label } = await req.json();
const r = await fetch("https://qrflow.codes/api/v1/codes", {
method: "POST",
headers: { Authorization: \`Bearer ${process.env.QRFLOW_KEY}\`, "Content-Type": "application/json" },
body: JSON.stringify({ type: "url", destination_data: { url }, label }),
});
const data = await r.json();
if (!r.ok) return NextResponse.json(data, { status: r.status }); // { error, message }
return NextResponse.json({ id: data.code.id, short_url: data.code.short_url });
}
// Proxies the SVG so the key stays on the server. Check that the signed-in
// user owns this id before you serve it, or anyone with an id can fetch it.
export async function GET(_: Request, { params }: { params: Promise<{ id: string }> }) {
const { id } = await params;
const r = await fetch(\`https://qrflow.codes/api/v1/codes/${id}/image.svg?size=1024\`, {
headers: { Authorization: \`Bearer ${process.env.QRFLOW_KEY}\` },
});
return new Response(r.body, {
status: r.status,
headers: { "Content-Type": "image/svg+xml", "Cache-Control": "private, max-age=3600" },
});
}
<img src={\`/api/qr/${code.id}/image\`} alt={\`QR code for ${code.label}\`} width={256} height={256} />
<a href={code.short_url}>{code.short_url}</a>
- Las Server Actions funcionan igual: llama a fetch con la clave dentro de la acción.
- Para Pages Router, el mismo código va en pages/api/qr.ts con req/res.
Mostrar el QR sin proxy: renderiza short_url tú mismo
Cuándo: Quieres la imagen en el navegador de inmediato y no necesitas los marcos ni el logo de QRFLOW en ella. Cualquier librería de QR funciona, porque el código ES el enlace corto
import QRCode from "qrcode"; // npm i qrcode
// short_url came back from POST /codes. Encode it as-is.
const dataUrl = await QRCode.toDataURL(code.short_url, { width: 512, margin: 2 });
// <img src={dataUrl} /> scans go through QRFLOW, so analytics and re-pointing still work.
- Este es el camino más rápido para una vista previa. Para impresión, descarga /codes/:id/image.svg: lleva los colores guardados, marco, leyendas y logo, y es un vector.
- Si luego cambias slug o domain_id, short_url cambia; vuelve a renderizar.
Express o cualquier servidor Node
Cuándo: Un backend Node simple.
import express from "express";
const app = express();
app.use(express.json());
const H = { Authorization: \`Bearer ${process.env.QRFLOW_KEY}\`, "Content-Type": "application/json" };
app.post("/qr", async (req, res) => {
const r = await fetch("https://qrflow.codes/api/v1/codes", {
method: "POST", headers: H,
body: JSON.stringify({ type: "url", destination_data: { url: req.body.url }, label: req.body.label }),
});
res.status(r.status).json(await r.json());
});
app.get("/qr/:id.svg", async (req, res) => {
const r = await fetch(\`https://qrflow.codes/api/v1/codes/${req.params.id}/image.svg\`, { headers: H });
res.status(r.status).type("image/svg+xml").send(await r.text());
});
app.listen(3000);
Cloudflare Workers, Vercel Edge, Deno Deploy, Supabase Edge Functions
Cuándo: Un runtime solo con fetch y sin built-ins de Node.
export default {
async fetch(req: Request, env: { QRFLOW_KEY: string }) {
const { url, label } = await req.json();
const r = await fetch("https://qrflow.codes/api/v1/codes", {
method: "POST",
headers: { Authorization: \`Bearer ${env.QRFLOW_KEY}\`, "Content-Type": "application/json" },
body: JSON.stringify({ type: "url", destination_data: { url }, label }),
});
return new Response(r.body, { status: r.status, headers: { "Content-Type": "application/json" } });
},
};
Deno.serve(async (req) => {
const { url, label } = await req.json();
const r = await fetch("https://qrflow.codes/api/v1/codes", {
method: "POST",
headers: { Authorization: \`Bearer ${Deno.env.get("QRFLOW_KEY")}\`, "Content-Type": "application/json" },
body: JSON.stringify({ type: "url", destination_data: { url }, label }),
});
return new Response(await r.text(), { status: r.status, headers: { "Content-Type": "application/json" } });
});
// supabase secrets set QRFLOW_KEY=qrf_live_...
- El paquete npm
qrflow\usa solo fetch y WebCrypto, así que funciona en todos estos sin cambios.
Python: FastAPI, Flask, Django, un script
Cuándo: Tu backend es Python.
import os
from fastapi import FastAPI, HTTPException, Response
from qrflow import QRFlow, QRFlowError # https://qrflow.codes/sdk/qrflow.py
app = FastAPI()
qr = QRFlow(os.environ["QRFLOW_KEY"])
@app.post("/qr")
def make_qr(url: str, label: str | None = None):
try:
code = qr.create_code(type="url", destination_data={"url": url}, label=label)["code"]
except QRFlowError as e:
raise HTTPException(e.status, {"error": e.code, "message": str(e)})
return {"id": code["id"], "short_url": code["short_url"]}
@app.get("/qr/{code_id}.svg")
def qr_image(code_id: str):
import urllib.request
req = urllib.request.Request(qr.image_url(code_id), headers={"Authorization": f"Bearer {os.environ['QRFLOW_KEY']}"})
with urllib.request.urlopen(req) as r:
return Response(r.read(), media_type="image/svg+xml")
Un código QR por pedido, mesa, producto, ticket o evento
Cuándo: Cada fila en una de tus tablas necesita su propio código, creado automáticamente.
- Añade dos columnas a tu tabla: qrflow_code_id (uuid) y qr_short_url (texto).
- Cuando se crea una fila, haz POST /codes con la URL pública de la fila y una etiqueta que la nombre ("Pedido 10432", "Mesa 7"). Guarda id y short_url.
- Cuando la página de la fila se mueve (nuevo dominio, nueva ruta), haz PATCH destination_data. Los códigos impresos siguen funcionando.
- Cuando la fila se retira, haz PATCH { paused: true } si algo se imprimió; DELETE solo si nada se imprimió.
- ¿Necesitas miles a la vez (un menú por mesa para 300 restaurantes)? Usa POST /codes/bulk en lotes y mapea los códigos devueltos a tus filas por etiqueta o por orden.
const { code } = await qr.createCode({
type: "url",
destination_data: { url: \`https://example.com/orders/${order.id}\` },
label: \`Order ${order.number}\`,
});
await db.orders.update(order.id, { qrflow_code_id: code.id, qr_short_url: code.short_url });
- El uso justo es 25,000 códigos guardados en Business; la API se detiene al doble. Si necesitas un código por recibo para siempre, habla con nosotros primero: hello@qrflow.codes.
Cambiar a dónde va un código impreso
Cuándo: Una campaña terminó, una página se movió, un PDF fue reemplazado, una temporada cambió.
curl -X PATCH https://qrflow.codes/api/v1/codes/$CODE_ID \
-H "Authorization: Bearer $QRFLOW_KEY" -H "Content-Type: application/json" \
-d '{ "destination_data": { "url": "https://example.com/spring" } }'
Ponlo en pausa, o dale una fecha de fin
# Scans show a "paused" page instead of redirecting
curl -X PATCH https://qrflow.codes/api/v1/codes/$CODE_ID -H "Authorization: Bearer $QRFLOW_KEY" \
-H "Content-Type: application/json" -d '{ "paused": true }'
# Stops working after the date; null clears it
curl -X PATCH https://qrflow.codes/api/v1/codes/$CODE_ID -H "Authorization: Bearer $QRFLOW_KEY" \
-H "Content-Type: application/json" -d '{ "expires_at": "2026-12-31T23:59:59Z" }'
- Solo los códigos dinámicos pueden ser redirigidos. Un código url creado en una cuenta Free, o un código Wi-Fi/vCard/texto, responde 400 not_dynamic. Para un código url/teléfono/email/sms/ubicación en un plan de pago, POST /codes/:id/dynamic lo convierte, y debes volver a renderizar e imprimir porque la imagen cambia.
- Un escáner ve el nuevo destino en el siguiente escaneo. No hay caché que esperar.
Imprime códigos en tu propio dominio
Cuándo: Quieres go.example.com/menu en el código en lugar de qrflow.codes/q/x7k2p9a.
- En la página de Cuenta, bajo Tu propio dominio de enlace, añade go.example.com y crea el CNAME que te muestra en tu proveedor de DNS. La verificación suele completarse en minutos.
- Desde entonces, cada nuevo código dinámico short_url usa ese dominio. Los códigos existentes también cambian: su imagen codificaba qrflow.codes/q/..., que sigue redirigiendo, así que nada impreso se rompe.
- Da a los códigos nombres legibles con slug: PATCH { "slug": "menu" } hace go.example.com/menu. Hazlo antes de imprimir.
- En Business con varios dominios, GET /domains los lista con ids; pasa domain_id en POST o PATCH para elegir por código.
Nombra un enlace y elige un dominio
curl https://qrflow.codes/api/v1/domains -H "Authorization: Bearer $QRFLOW_KEY"
# { "default_base": "https://go.example.com", "domains": [ { "id": "…", "host": "go.example.com", "is_default": true, … }, { "id": "…", "host": "qr.example.fr", … } ] }
curl -X PATCH https://qrflow.codes/api/v1/codes/$CODE_ID -H "Authorization: Bearer $QRFLOW_KEY" -H "Content-Type: application/json" \
-d '{ "slug": "menu", "domain_id": "<id of qr.example.fr>" }'
# short_url is now https://qr.example.fr/menu
Un gráfico de escaneos en tu propio panel de administración
Cuándo: Quieres escaneos por día, por país o por dispositivo junto a tus propios números.
const stats = await qr.scans(code.id, { from: "2026-09-01", to: "2026-09-30", group: "day" });
// stats.total -> 412
// stats.rows -> [{ key: "2026-09-01", scans: 18 }, { key: "2026-09-02", scans: 25 }, ...]
// Feed rows straight into Recharts, Chart.js, or a <table>.
const byCountry = await qr.scans(code.id, { group: "country" }); // [{ key: "US", scans: 300 }, { key: "MX", scans: 41 }]
const byDevice = await qr.scans(code.id, { group: "device" }); // mobile, desktop, tablet
- Una solicitud cubre hasta 92 días; haz un bucle para rangos más largos. Las fechas son UTC.
- Para números en vivo sin sondeo, suscríbete a un webhook para el evento de escaneo: recibes cada escaneo con sus detalles en lotes cada pocos minutos.
Recibe webhooks en Next.js y verifícalos
Cuándo: Quieres saber cuándo un código es escaneado o cambiado, en tu propia base de datos, casi en tiempo real.
- Crea el webhook en Cuenta › Webhooks o con POST /webhooks. Copia el secreto (whsec_...) una vez en QRFLOW_WEBHOOK_SECRET.
- Lee el cuerpo crudo como texto antes de parsear; la firma cubre los bytes exactos.
- Verifica, luego cambia según el evento. Responde 2xx rápido; haz trabajo lento después de responder o en una cola.
- Pulsa Probar en el webhook para recibir un ping firmado y confirmar la conexión.
import { createHmac, timingSafeEqual } from "node:crypto";
function verify(raw: string, header: string, secret: string): boolean {
const t = /t=(\d+)/.exec(header)?.[1], v1 = /v1=([a-f0-9]+)/.exec(header)?.[1];
if (!t || !v1 || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const expected = createHmac("sha256", secret).update(\`${t}.${raw}\`).digest("hex");
return expected.length === v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
}
export async function POST(req: Request) {
const raw = await req.text();
if (!verify(raw, req.headers.get("x-qrflow-signature") ?? "", process.env.QRFLOW_WEBHOOK_SECRET!)) {
return new Response("bad signature", { status: 401 });
}
const evt = JSON.parse(raw) as { id: string; event: string; created_at: string; data: any };
// evt.id is stable across retries: store it and skip duplicates.
switch (evt.event) {
case "scan": // evt.data.scans[]: code_id, label, slug, scanned_at, device, country, city, referrer, browser, os, language
break;
case "code.created": // evt.data.code, evt.data.source
case "code.updated": // evt.data.code, evt.data.changed[]
case "code.deleted": // evt.data.code { id, label, short_code, slug }
break;
case "ping": // the Test button
break;
}
return new Response(null, { status: 204 });
}
- Localmente, expón tu servidor de desarrollo con un túnel (cloudflared tunnel --url http://localhost:3000, o ngrok) y usa esa URL https para el webhook mientras construyes.
- El paquete npm hace esto por ti:
import { parseWebhook } from "qrflow"\verifica y parsea en una llamada (WebCrypto, así que también funciona en Workers y Deno). El cliente Python incluye verify_webhook.
Recibe webhooks en Python
Cuándo: Flask, FastAPI o Django recibiendo los mismos eventos.
import os, json
from flask import Flask, request, abort
from qrflow import verify_webhook # https://qrflow.codes/sdk/qrflow.py
app = Flask(__name__)
@app.post("/qrflow")
def hook():
raw = request.get_data() # bytes, before any parsing
if not verify_webhook(raw, request.headers.get("X-QRFLOW-Signature", ""), os.environ["QRFLOW_WEBHOOK_SECRET"]):
abort(401)
evt = json.loads(raw)
if evt["event"] == "scan":
for s in evt["data"]["scans"]:
print(s["code_id"], s["scanned_at"], s["country"], s["device"])
return "", 204
Miles de códigos desde un CSV
Cuándo: Un código por SKU, por asiento, por etiqueta de activo, por correo.
import { parse } from "csv-parse/sync";
import { readFileSync } from "node:fs";
const rows = parse(readFileSync("skus.csv"), { columns: true }) as Array<{ sku: string; url: string }>;
const out: Array<{ sku: string; id: string; short_url: string }> = [];
for (let i = 0; i < rows.length; i += 2000) { // Business: 2,000 per request
const chunk = rows.slice(i, i + 2000);
const { codes, rejected, remaining_this_month } = await qr.bulkCreate(
chunk.map((r) => ({ destination: r.url, label: r.sku })),
);
codes.forEach((c, j) => out.push({ sku: chunk[j].sku, id: c.id, short_url: c.short_url }));
if (rejected.length) console.warn(rejected); // rows that were not web addresses
console.log(remaining_this_month, "left this month");
}
- Bulk crea solo códigos url dinámicos, todos con los mismos colores. Los códigos vuelven en el orden que los enviaste, menos las filas rechazadas; empareja por etiqueta si dudas.
- Los webhooks reciben un evento code.created por solicitud bulk con codes[] en lugar de uno por código.
Deja que tus usuarios conecten su propia cuenta QRFLOW (OAuth)
Cuándo: Estás construyendo un producto para otras personas y quieres que los códigos caigan en sus cuentas QRFLOW, no en la tuya.
- Registra un cliente una vez: POST https://qrflow.codes/api/oauth/register con client_name y redirect_uris. Obtienes un client_id (y un client_secret para clientes confidenciales).
- Envía al usuario a /oauth/authorize con response_type=code, client_id, redirect_uri, scope, state, y PKCE (code_challenge, code_challenge_method=S256).
- Intercambia el código en /api/oauth/token. Guarda el refresh token; los access tokens duran una hora.
- Llama a /api/v1 con Authorization: Bearer <access_token>. Todo funciona exactamente igual que con una clave, bajo el plan del usuario.
curl -X POST https://qrflow.codes/api/oauth/register -H "Content-Type: application/json" \
-d '{ "client_name": "Acme Menus", "redirect_uris": ["https://app.example.com/oauth/qrflow"], "token_endpoint_auth_method": "none" }'
# { "client_id": "dyn_…", "redirect_uris": [...], "grant_types": ["authorization_code","refresh_token"], … }
https://qrflow.codes/oauth/authorize?response_type=code&client_id=dyn_…&redirect_uri=https%3A%2F%2Fapp.example.com%2Foauth%2Fqrflow
&scope=profile%20codes%3Aread%20codes%3Awrite%20analytics%3Aread&state=…&code_challenge=…&code_challenge_method=S256
- Los scopes son los mismos seis que las claves API. Pide lo mínimo que necesites; la pantalla de consentimiento los lista.
- Si tu app es un asistente de chat o agente, añade resource=https://qrflow.codes/mcp a la solicitud de autorización y habla con el servidor MCP en su lugar; el token estará vinculado a él.
Zapier, Make, n8n: sin código en absoluto
Cuándo: Quieres que escaneos o nuevos códigos caigan en una hoja, un canal de Slack o un CRM.
- Crea un trigger de catch-hook (Zapier: Webhooks by Zapier › Catch Hook; Make: Custom webhook; n8n: nodo Webhook) y copia su URL https.
- En Cuenta › Webhooks, añade esa URL y elige los eventos. Pulsa Probar; el ping aparece en la herramienta y le da la forma del payload.
- Mapea data.scans[] (para scan) o data.code (para code.*) a tu hoja, mensaje o registro.
- Para crear códigos desde esas herramientas, usa su módulo HTTP contra POST /codes con el encabezado Authorization. Mantén la clave en el almacén de credenciales de la herramienta.
- Estas herramientas no pueden verificar la firma. La URL que te dan es imposible de adivinar, que es la protección que tienes; no la publiques en ningún sitio.
Vibe coding
Prompts para pegar
Los asistentes de codificación construyen lo correcto cuando se les dicen las reglas de antemano. Estos prompts llevan las reglas. Pega uno, completa el corchete, y el asistente leerá la referencia Markdown antes de escribir una línea.
Añade códigos QR a mi app
Claude, ChatGPT, Cursor, Codex, Windsurf, Copilot Chat: pega en el chat
Add QR codes to this project using the QRFLOW.codes API.
Read https://qrflow.codes/llms-full.txt before writing code; it is the complete reference.
Rules:
- The API key is in the environment variable QRFLOW_KEY. It must only be used server-side (route handler, server action, edge function). Never expose it to the browser.
- Create dynamic codes: POST https://qrflow.codes/api/v1/codes with { "type": "url", "destination_data": { "url": ... }, "label": ... }.
- Save the returned code.id and code.short_url on my record. short_url is what gets printed or displayed.
- Show the image by proxying GET /codes/:id/image.svg through my server, or by encoding short_url with a QR library on the client.
- Handle errors from the JSON body: { "error", "message" }. Map 402 to "upgrade needed", 429 to a retry with the Retry-After header.
What I want: [describe the feature, e.g. "every event in my events table gets a QR code that opens its public page; show it on the event admin page with a download button"].
Enseña a tu repositorio sobre QRFLOW
Ponlo en CLAUDE.md, AGENTS.md, .cursorrules o .github/copilot-instructions.md
## QR codes (QRFLOW.codes)
- Docs: https://qrflow.codes/llms-full.txt (Markdown), https://qrflow.codes/api/v1/openapi.json (OpenAPI 3.1).
- Base URL https://qrflow.codes/api/v1, header Authorization: Bearer $QRFLOW_KEY. Server-side only.
- Codes are created with POST /codes { type: "url", destination_data: { url }, label }. Store code.id and code.short_url.
- Change the destination with PATCH /codes/:id { destination_data: { url } }. Never change slug/domain_id after printing.
- Image: GET /codes/:id/image.svg (needs the key). Prefer paused: true over DELETE when a print exists.
- Webhooks arrive as POST with X-QRFLOW-Signature (t=,v1=HMAC-SHA256 of "t.rawBody"); verify with the raw body.
En Lovable, Bolt, v0, Replit y otros constructores de apps
Constructores front-end-first que te dan un backend (Supabase, funciones serverless)
Integrate QRFLOW.codes QR codes. The API refuses browser calls, so create a backend function (Supabase Edge Function / serverless function) that holds the secret QRFLOW_KEY and calls POST https://qrflow.codes/api/v1/codes with { "type": "url", "destination_data": { "url": "<the page URL>" }, "label": "<name>" }. Return code.id and code.short_url to the UI and save them in the database. Render the QR in the UI by encoding short_url with a QR library; add a "Download for print" button that fetches /codes/:id/image.svg through the same backend function. Reference: https://qrflow.codes/llms-full.txt
Un GPT personalizado que gestiona mis códigos
ChatGPT › Crear un GPT › Acciones
Import from URL: https://qrflow.codes/api/v1/openapi.json
Authentication: API Key › Bearer › paste a Business key with the scopes you want the GPT to have.
Then the GPT can list, create, re-point and report on your codes. For a no-setup version, add the MCP connector instead (Developer mode › Plugins › https://qrflow.codes/mcp).
Cosas que decir una vez que el conector está activo
Claude, ChatGPT, Claude Code con el servidor MCP QRFLOW conectado
"Make a QR code for https://example.com/fall-menu, call it Fall menu, frame caption 'Scan for menu'."
"Which of my codes got the most scans this month? Show a breakdown by country for the top one."
"Point the 'Lobby poster' code at https://example.com/events/october."
"Name the 'Business card' code's link 'hi' on my domain."
"Pause every code with 'Summer' in the label."
"Make 40 codes, one per table, going to https://example.com/order?table=1 through 40."
"Show me the PNG of the 'Front door' code."
Servidor MCP
Úsalo desde Claude, ChatGPT, Cursor y Claude Code
QRFLOW es un servidor MCP en https://qrflow.codes/mcp. Conéctalo una vez, inicia sesión, y luego di cosas como "haz un código QR para nuestra página de menú de otoño, llamado menu en mi dominio", "apunta el código del póster del lobby a la nueva página" o "¿cuántos escaneos tuvo el volante la semana pasada, por país?". El asistente obtiene las mismas herramientas que ofrece esta API, bajo las mismas reglas, y los códigos caen en tu panel con mcp como su fuente.
¿No eres desarrollador? La versión en lenguaje sencillo, con los clics exactos para cada asistente, está en Haz códigos QR con tu asistente de IA.
Conectar
Personalizar › Conectores › Añadir conector personalizado › pega la dirección, o pulsa Conectar en el listado del directorio de QRFLOW. Claude abre un inicio de sesión de QRFLOW; pulsa Permitir. Funciona en todos los planes.
https://qrflow.codes/mcp
Configuración › Seguridad y acceso › Modo desarrollador activado, luego Configuración › Plugins › + › pega la dirección; inicia sesión cuando se pida. Plus, Pro, Team, Enterprise y Edu.
https://qrflow.codes/mcp
Un comando, luego /mcp para iniciar sesión. Añade una clave Business como encabezado para omitir el inicio de sesión.
claude mcp add --transport http qrflow https://qrflow.codes/mcp
# or, with a key:
claude mcp add --transport http qrflow https://qrflow.codes/mcp --header "Authorization: Bearer $QRFLOW_KEY"
Cursor, Windsurf, VS Code, cualquier cliente MCP
Añade un servidor HTTP en la URL. El inicio de sesión OAuth ocurre en el navegador; o pasa un encabezado Authorization con una clave.
{
"mcpServers": {
"qrflow": { "type": "http", "url": "https://qrflow.codes/mcp" }
}
}
Tu propio agente (SDK de Anthropic o OpenAI)
Apunta el conector MCP o herramienta a la URL con una clave Business como bearer; no se necesita flujo de navegador.
// Anthropic Messages API, MCP connector
mcp_servers: [{ type: "url", url: "https://qrflow.codes/mcp", name: "qrflow", authorization_token: process.env.QRFLOW_KEY }]
Lo que el asistente puede hacer
| Herramienta | Qué hace | Alcance |
|---|---|---|
| list_code_kinds | Cada tipo de código con sus campos y el plan que necesita. El asistente lo llama antes de crear algo inusual. | profile |
| list_domains | Tus dominios de enlace, el predeterminado, y sus ids. | domains:read |
| get_qr_image | Un PNG que el asistente puede mostrar o guardar, más la URL SVG para impresión. | codes:read |
| get_account | Quién está conectado, plan, límites. | profile |
Cómo se mantiene seguro
- El asistente solo tiene un token para tu cuenta, emitido después de que tú pulsaste Permitir en una página de QRFLOW. Desconéctalo en cualquier momento en Cuenta › Apps conectadas.
- Los tokens están vinculados al servidor MCP; no pueden reproducirse contra la API REST.
- Cada escritura pasa por la misma validación que el panel: destinos en lista blanca, verificaciones de plan, límites de uso justo.
- Las herramientas destructivas se describen cuidadosamente:
delete_qr_codele dice al modelo que prefiera pausar cuando existe una impresión. - Los documentos de descubrimiento viven en
/.well-known/oauth-authorization-servery/.well-known/oauth-protected-resource; el registro es RFC 7591; solo PKCE S256.
SDKs y la especificación OpenAPI
- TypeScript / JavaScript:
npm install qrflow(npm). Cero dependencias, ESM y CommonJS, tipos completos; funciona en Node 18+, Bun, Deno y Workers. Reintenta 429s por ti e incluyeverifyWebhook/parseWebhook. - Python 3.9+, solo biblioteca estándar: qrflow.py.
- OpenAPI 3.1: /api/v1/openapi.json. Impórtalo en Postman o Insomnia, genera un cliente en cualquier lenguaje, o adjúntalo a una Acción de ChatGPT.
- Ambos clientes lanzan un error tipado (
QRFlowErrorconstatus,code,message,retryAfter) e incluyen un verificador de webhooks. El código fuente TypeScript de un solo archivo sigue en /sdk/qrflow.ts si prefieres incluirlo directamente.
| TypeScript | Python | Llamadas |
|---|---|---|
| me() | me() | GET /me |
| catalog() | catalog() | GET /catalog |
| listCodes({ limit, q }) | list_codes(limit, q) | GET /codes |
| getCode(id) | get_code(id) | GET /codes/:id |
| createCode(input) | create_code(**fields) | POST /codes |
| updateCode(id, patch) | update_code(id, **patch) | PATCH /codes/:id |
| deleteCode(id) | delete_code(id) | DELETE /codes/:id |
| makeDynamic(id) | make_dynamic(id) | POST /codes/:id/dynamic |
| scans(id, { from, to, group }) | scans(id, from_, to, group) | GET /codes/:id/scans |
| bulkCreate(rows, colors) | bulk_create(rows, **colors) | POST /codes/bulk |
| domains() | domains() | GET /domains |
| listWebhooks() / createWebhook() / testWebhook(id) / deleteWebhook(id) | list_webhooks() / create_webhook() / test_webhook(id) / delete_webhook(id) | /webhooks |
| imageUrl(id, size) | image_url(id, size) | La dirección de la imagen (búscala con la clave) |
| verifyWebhook(raw, header, secret) | verify_webhook(raw, header, secret) | Verificación de firma para entregas |
Referencia de endpoints
URL base https://qrflow.codes/api/v1. Los cuerpos y las respuestas son JSON. Las fechas están en ISO 8601 en UTC. Envía solo los campos que cambian en PATCH.
GETprofile
Plan, indicadores de funciones y los límites que se aplican a esta cuenta, además de los alcances de la clave.
curl https://qrflow.codes/api/v1/me \
-H "Authorization: Bearer $QRFLOW_KEY"
{ "id": "…", "email": "ops@example.com", "plan": "business", "paid": true,
"features": { "dynamic_codes": true, "custom_domain": true, "link_names": true, "gs1": true, "api_keys": true },
"limits": { "saved_codes": 25000, "bulk_per_month": 10000, "bulk_per_request": 2000, "link_domains": 5, "requests_per_minute": 600 },
"auth": "api_key", "scopes": ["codes:read", "codes:write"] }
GETpublic
Tipos nativos (url, wifi, vcard, email, phone, sms, text, location) y los más de 50 subtipos (Instagram, reseña de Google, Wi-Fi, tienda de aplicaciones…), cada uno con los campos que necesita y el plan que requiere. No se necesita clave.
curl https://qrflow.codes/api/v1/catalog
GETcodes:read
Más recientes primero.?limit= hasta 100,?q= busca etiquetas.
curl "https://qrflow.codes/api/v1/codes?limit=20&q=menu" \
-H "Authorization: Bearer $QRFLOW_KEY"
POSTcodes:write
Las mismas reglas que Guardar en el sitio: los códigos url, phone, email, sms y location son dinámicos en planes de pago; Wi-Fi, vCard y text llevan su contenido en el patrón. Pon un id de subtipo en destination_data.subtype para hacer, por ejemplo, un código de reseña de Google. El domain_id opcional elige con cuál de tus dominios de enlace se imprime el código (GET /domains los lista).
curl -X POST https://qrflow.codes/api/v1/codes \
-H "Authorization: Bearer $QRFLOW_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "url", "destination_data": { "url": "https://example.com/menu" }, "label": "Table tents", "frame_style": "caption-below", "frame_caption": "Scan for menu" }'
{ "code": { "id": "…", "label": "Table tents", "kind": "url", "dynamic": true, "short_code": "x7k2p9a",
"short_url": "https://go.example.com/x7k2p9a", "scans": 0, "image_url": "https://qrflow.codes/api/v1/codes/…/image.svg", … } }
GETcodes:read
El código con su recuento de escaneos, enlace corto y dirección de imagen.
curl https://qrflow.codes/api/v1/codes/$ID \
-H "Authorization: Bearer $QRFLOW_KEY"
PATCHcodes:write
Cualquiera de: destination_data (solo códigos dinámicos, la impresión sigue siendo válida), label, paused, expires_at (ISO o null), slug (un nombre de enlace en tu dominio), domain_id (con cuál de tus dominios de enlace se imprime este código; null = el predeterminado de la cuenta), fg_color, bg_color, frame_style, frame_caption, frame_caption2. Envía solo los campos que estás cambiando.
curl -X PATCH https://qrflow.codes/api/v1/codes/$ID \
-H "Authorization: Bearer $QRFLOW_KEY" \
-H "Content-Type: application/json" \
-d '{ "destination_data": { "url": "https://example.com/menu-fall" }, "slug": "menu" }'
DELETEcodes:write
Desaparece para siempre, incluido su historial de escaneos. El enlace impreso de un código dinámico deja de resolverse. Prefiere paused: true si la impresión todavía está por ahí.
curl -X DELETE https://qrflow.codes/api/v1/codes/$ID \
-H "Authorization: Bearer $QRFLOW_KEY"
204 No Content
POSTcodes:write
El patrón impreso cambia (ahora codifica el enlace corto), así que vuelve a renderizar la imagen después.
curl -X POST https://qrflow.codes/api/v1/codes/$ID/dynamic \
-H "Authorization: Bearer $QRFLOW_KEY"
GETcodes:read
SVG listo para imprimir con el marco y los colores.?size= establece el ancho de la cuadrícula de módulos en px; el archivo se escala sin pérdida de todos modos.
curl https://qrflow.codes/api/v1/codes/$ID/image.svg \
-H "Authorization: Bearer $QRFLOW_KEY" -o code.svg
GETanalytics:read
?from= y?to= (fechas ISO, hasta 92 días, por defecto los últimos 30) y?group= day, device, country, city, browser, os o referrer. Los mismos números que la página de analíticas.
curl "https://qrflow.codes/api/v1/codes/$ID/scans?from=2026-09-01&to=2026-09-21&group=day" \
-H "Authorization: Bearer $QRFLOW_KEY"
{ "code_id": "…", "from": "…", "to": "…", "group": "day", "total": 412,
"rows": [ { "key": "2026-09-01", "scans": 18 }, { "key": "2026-09-02", "scans": 25 }, … ] }
POSTcodes:write
Hasta 2,000 códigos URL en una sola llamada, todos dinámicos. Cuenta contra el mismo límite mensual de carga masiva que la página de Bulk (10,000 en Business). Las filas que no son direcciones web vuelven en rejected; el resto se crean.
curl -X POST https://qrflow.codes/api/v1/codes/bulk \
-H "Authorization: Bearer $QRFLOW_KEY" \
-H "Content-Type: application/json" \
-d '{ "rows": [ { "destination": "https://example.com/t/1", "label": "Table 1" }, { "destination": "https://example.com/t/2", "label": "Table 2" } ] }'
{ "codes": [ … ], "rejected": [], "remaining_this_month": 9998 }
GETdomains:read
Tus dominios conectados, su estado y con cuál se imprimen los códigos dinámicos por defecto (default_base). Pasa el id de un dominio como domain_id en un código para imprimir ese código con uno diferente.
curl https://qrflow.codes/api/v1/domains \
-H "Authorization: Bearer $QRFLOW_KEY"
GETwebhooks:manage
Tus webhooks con sus eventos, último estado y recuento de fallos.
curl https://qrflow.codes/api/v1/webhooks \
-H "Authorization: Bearer $QRFLOW_KEY"
POSTwebhooks:manage
url debe ser https en un host público; events es cualquiera de scan, code.created, code.updated, code.deleted. El secreto de firma se devuelve una sola vez.
curl -X POST https://qrflow.codes/api/v1/webhooks \
-H "Authorization: Bearer $QRFLOW_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/hooks/qrflow", "events": ["scan", "code.updated"] }'
{ "webhook": { "id": "…", "url": "…", "events": ["scan", "code.updated"], "active": true, "secret": "whsec_…" } }
POSTwebhooks:manage
Envía un ping firmado ahora e informa de la respuesta.
curl -X POST https://qrflow.codes/api/v1/webhooks/$WEBHOOK_ID \
-H "Authorization: Bearer $QRFLOW_KEY"
DELETEwebhooks:manage
Detiene todas las entregas, incluidos los reintentos en cola.
curl -X DELETE https://qrflow.codes/api/v1/webhooks/$WEBHOOK_ID \
-H "Authorization: Bearer $QRFLOW_KEY"
204 No Content
GETpublic
Identificadores de marco y qué necesita cada uno (caption, segunda línea), agrupados como el personalizador. No se necesita clave.
curl https://qrflow.codes/api/v1/frames
El objeto Code
Cada endpoint que toca un código devuelve la misma forma. Ignora los campos que no conozcas; se añaden nuevos con el tiempo.
| Campo | Tipo | Significado |
|---|---|---|
| id | uuid | Identificador estable. Úsalo en cualquier otra llamada. |
| label | string | null | El nombre en el panel. Hasta 120 caracteres. Buscable con?q=. |
| kind | string | El id del catálogo: url, wifi, instagram, googlereview,... kind_label es el nombre legible. |
| type | string | La codificación: url, text, wifi, vcard, email, phone, sms, location. |
| destination_data | object | Los campos que enviaste (más subtype para kinds). destination es un resumen de una línea de ello. |
| dynamic | boolean | True cuando los escaneos pasan por QRFLOW y el destino puede cambiar. dynamic_capable dice si este tipo podría ser dinámico en un plan de pago. |
| short_code | string | Siete caracteres, nunca cambia. |
| short_url | string | Qué imprimir para un código dinámico. Incluye tu dominio y slug cuando están configurados. |
| domain_id | uuid | null | Con qué dominio de enlace se imprime este código; null significa el predeterminado de la cuenta. |
| fg_color, bg_color | hex | Colores de módulo y fondo. |
| has_logo | boolean | Se añadió un logo en el panel; image.svg lo incluye. |
| frame_style, frame_caption, frame_caption2 | string | null | Identificador de marco y captions, como en el personalizador. |
| scans | integer | Recuento de escaneos de por vida. |
| created_at, updated_at | ISO 8601 | UTC. |
| manage_url | url | La página del código en el panel, para un enlace 'Abrir en QRFLOW'. |
| image_url | url | GET /codes/:id/image.svg. Necesita el encabezado Authorization; no es una URL de imagen pública. |
| svg_download_url, png_download_url | url | El mismo SVG (marco, colores, logo) y un PNG simple a través de enlaces firmados que funcionan durante 24 horas sin encabezado: para etiquetas <img>, scripts y asistentes que guardan un archivo. download_expires_at dice cuándo caducan; cualquier lectura del código devuelve otros nuevos. |
Otras formas: Scans (code_id, from, to, group, total, rows[key, scans]), Domain (id, host, status, active, is_default, verified_at, grace_until), Webhook (id, url, events, active, last_status, last_delivery_at, consecutive_failures, más secret una vez), Me (id, email, plan, paid, features, limits, auth, scopes) y Error (error, message). El documento OpenAPI tiene cada propiedad tipada.
Business
Webhooks
QRFLOW llama a tu URL https cuando algo sucede. Crea uno en Cuenta › Webhooks o con POST /webhooks; obtienes el secreto de firma una vez. Hasta 10 por cuenta.
| Evento | Cuándo | data |
|---|---|---|
| scan | Por lotes: cada pocos minutos, todos los nuevos escaneos desde la última entrega, hasta 500 por llamada. | count, from, to, scans[] con code_id, label, short_code, slug, scanned_at, device, country, city, referrer, browser, os, language |
| code.created | De inmediato desde la API y los asistentes; en minutos desde el panel. Una solicitud masiva envía un evento con bulk: true y codes[]. | code, source |
| code.updated | Mismo momento. Cubre destino, etiqueta, pausa, caducidad, nombre de enlace, dominio, colores, marco y conversión a dinámico. | code, changed[] (los nombres de los campos que cambiaron) |
| code.deleted | Mismo momento. | code: { id, label, short_code, slug } |
| ping | Cuando presionas Probar. | webhook_id, message |
Qué llega
Cada carga útil es { id, event, created_at, data }. id es estable entre reintentos de una entrega, así que puedes deduplicar con ello.
{
"id": "9b1c6d2e-…",
"event": "scan",
"created_at": "2026-09-21T18:05:00.000Z",
"data": {
"count": 2,
"from": "2026-09-21T18:00:00.000Z",
"to": "2026-09-21T18:04:12.331Z",
"scans": [
{ "code_id": "…", "label": "Table tents", "short_code": "x7k2p9a", "slug": "menu", "scanned_at": "2026-09-21T18:03:40.101Z",
"device": "mobile", "country": "US", "city": "Las Vegas", "referrer": null, "browser": "Safari", "os": "iOS", "language": "en-US" },
{ "code_id": "…", "label": "Table tents", "short_code": "x7k2p9a", "slug": "menu", "scanned_at": "2026-09-21T18:04:12.331Z",
"device": "mobile", "country": "MX", "city": "Tijuana", "referrer": null, "browser": "Chrome", "os": "Android", "language": "es-MX" }
]
}
}
{
"id": "2f0a…",
"event": "code.updated",
"created_at": "2026-09-21T18:06:00.000Z",
"data": {
"code": { "id": "…", "label": "Table tents", "kind": "url", "dynamic": true, "short_url": "https://go.example.com/menu", "scans": 412, "…": "…" },
"changed": ["destination_data"]
}
}
POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: QRFLOW-Webhooks/1.0 (+https://qrflow.codes/developers)
X-QRFLOW-Event: scan
X-QRFLOW-Signature: t=1758477900,v1=5f1c…e9
Verificar la firma
X-QRFLOW-Signature: t=<unix seconds>,v1=<hex>. Calcula HMAC-SHA256 sobre ${t}.${rawBody} con tu secreto y compara con v1 en tiempo constante; rechaza si t tiene más de cinco minutos. Usa los bytes sin procesar que recibiste, nunca un objeto re-serializado. Receptores funcionales para Next.js y Python están en las recetas, y ambos SDK incluyen el asistente.
Reglas de entrega
- Responde cualquier 2xx dentro de 8 segundos. Haz el trabajo después de responder.
- Cualquier otra cosa se reintenta después de 1, 5, 15, 60, 240 y 720 minutos.
- Veinte fallos consecutivos apagan el webhook y envían un correo al propietario de la cuenta. Vuelve a encenderlo una vez que el receptor esté arreglado; los reintentos en cola se reanudan.
- Las URLs deben ser https en un host público. localhost, rangos privados y el propio qrflow.codes son rechazados. Usa un túnel mientras desarrollas.
- Presiona Probar para recibir un
pingfirmado y ver el estado que respondió tu servidor.
Errores
Cada error es { "error": "<code>", "message": "<what to do>" } con el estado a continuación. El mensaje está escrito para una persona; muéstralo.
| Estado | error | Significado |
|---|
Síntoma, causa, solución
Solución de problemas
401 invalid_token en cada llamada
Por quéEl encabezado es incorrecto o la clave no está activa.
SoluciónEnvía exactamente Authorization: Bearer qrf_live_… (un espacio, no dos puntos). Comprueba que la clave no fue revocada en Cuenta › Claves API. Si la copiaste de un chat o un documento, vigila un punto final o una comilla inteligente.
401 en /api/v1 con un token que funciona en el servidor MCP
Por quéLos tokens emitidos para https://qrflow.codes/mcp están vinculados a él.
SoluciónUsa una clave API para REST, o ejecuta un segundo flujo OAuth sin resource= para obtener un token para la API REST.
402 upgrade_required al crear una clave o un webhook
Por quéAmbos son funciones de Business.
SoluciónMejora en /pricing, o usa el servidor MCP, que funciona en todos los planes mediante el inicio de sesión.
402 en POST /codes desde una aplicación OAuth
Por quéEl plan del usuario no incluye lo que la aplicación pidió (un código dinámico, un dominio).
SoluciónLee primero las funciones de GET /me y adáptate: crea el código de todos modos (será estático en Free) o dile al usuario lo que necesita el plan.
403 insufficient_scope
Por quéLos alcances se fijan cuando se crea una clave.
SoluciónCrea una nueva clave con los alcances que necesitas y revoca la antigua. Para OAuth, pide el alcance en la solicitud de autorización.
400 not_dynamic cuando hago PATCH a destination_data
Por quéEl código es estático: hecho en Free, o un tipo Wi-Fi/vCard/text.
SoluciónPara url/phone/email/sms/location en un plan de pago, haz POST /codes/:id/dynamic, luego vuelve a descargar y reimprimir (la imagen cambia). Wi-Fi, vCard y text nunca pueden ser dinámicos; haz un código url que abra una página en su lugar.
400 no_domain cuando configuro slug
Por quéLos nombres de enlace viven en tu dominio.
SoluciónConecta y verifica un dominio en la página de Cuenta primero. En qrflow.codes/q la ruta es siempre el short_code.
409 conflict en slug
Por quéOtro de tus códigos tiene ese nombre.
SoluciónHaz GET /codes?q= para encontrarlo, o elige otro nombre. Los nombres son por cuenta, no globales.
Error CORS en la consola del navegador
Por quéLa API solo acepta llamadas de servidor a servidor (y Canva). Esto es deliberado: una clave en una página web es una clave filtrada.
SoluciónMueve la llamada a un manejador de ruta, acción de servidor, función de borde o backend y llama a eso desde la página.
La imagen muestra una marca de agua de QRFLOW
Por quéLa cuenta está en el plan Free.
SoluciónLos planes de pago la eliminan. Free está pensado para el generador propio del sitio.
short_url todavía dice qrflow.codes/q/… después de añadir mi dominio
Por quéEl dominio aún no está verificado, o su CNAME es incorrecto.
SoluciónComprueba el estado en la página de Cuenta o con GET /domains (status debe ser verified). Los códigos existentes cambian automáticamente una vez que lo esté.
image_url da 401 en una etiqueta <img>
Por quéNecesita el encabezado Authorization, que una <img> no puede enviar.
SoluciónUsa svg_download_url o png_download_url del mismo objeto Code: enlaces firmados que funcionan durante 24 horas sin encabezado. Para algo permanente, haz proxy de image_url a través de tu servidor (ver la receta de Next.js) o codifica short_url tú mismo.
Necesito un PNG, no un SVG
Por quéEl SVG lleva el marco y el logo; el PNG es el código simple.
SoluciónGET /codes/:id/image.png (bearer o el png_download_url firmado) devuelve un PNG, de 256 a 2048 px. Para un PNG con el marco, convierte el SVG con sharp o resvg (sharp(svgBuffer).png().toBuffer()).
Mi asistente dijo que el endpoint de imagen lo rechazó y dibujó el código él mismo
Por quéObtuvo image_url, que necesita un bearer.
SoluciónCada código ahora lleva png_download_url y svg_download_url, y get_qr_image los devuelve; el asistente puede hacer curl a esos sin iniciar sesión. Un código dibujado localmente que codifica el mismo short_url todavía funciona y todavía cuenta escaneos, pero carece del marco y el logo.
El webhook nunca llega
Por quéReglas de URL o el receptor.
SoluciónLa URL debe ser https en un host público (sin localhost, sin IPs privadas, no qrflow.codes). Presiona Probar en el webhook: el resultado muestra el estado que respondió tu servidor. Los eventos de escaneo se agrupan y pueden tardar hasta unos cinco minutos; los eventos code.* del panel también se ponen en cola durante unos minutos, mientras que las escrituras de API y MCP se entregan de inmediato.
La firma nunca verifica
Por quéFirmaste un cuerpo re-serializado.
FixVerify contra los bytes brutos exactos que recibiste, antes del análisis JSON. En Express usa express.raw({ type: 'application/json' }) en esa ruta; en Next.js App Router usa await req.text(); en Flask request.get_data(). Luego calcula HMAC-SHA256 de ${t}.${raw}\.
El webhook se desactivó solo
Por quéVeinte fallos consecutivos.
SoluciónArregla el receptor y luego actívalo de nuevo (Cuenta › Webhooks, o elimínalo y recréalo). Recibiste un correo cuando ocurrió. Los reintentos en cola se reanudan.
Entregas de webhook duplicadas
Por quéUn 2xx lento (más de 8 segundos) cuenta como fallo y se reintenta.
SoluciónResponde primero, procesa después. Deduplica con el id del payload, que es estable entre reintentos.
429 rate_limited durante una importación
Por qué600 solicitudes por minuto por clave.
SoluciónUsa POST /codes/bulk (2,000 códigos en una solicitud) en lugar de un POST por código, o espera los segundos de Retry-After.
Eliminé un código y el póster impreso ahora muestra 'Code not found'
Por quéEl borrado es permanente y mata el enlace.
SoluciónNo hay deshacer. La próxima vez usa PATCH { paused: true }; un código en pausa muestra una página amigable y se puede reanudar.
Mi clave dejó de funcionar después de que bajé de plan
Por quéLas claves siguen funcionando 30 días después de dejar Business, luego responden 402.
SoluciónVuelve a suscribirte; nada se eliminó y las mismas claves vuelven a funcionar.
¿Algo que no esté en la lista? hello@qrflow.codes, con la solicitud exacta y el error JSON que recibiste. Las cuentas Business tienen prioridad.
Límites y uso justo
| Límite | |
|---|---|
| Solicitudes por minuto, por clave | 600. 429 con Retry-After más allá de eso. |
| Claves por cuenta | 10 |
| Webhooks por cuenta | 10 |
| Códigos guardados (uso justo) | 1,000 Premium, 25,000 Business; la API se detiene al doble de eso. |
| Bulk | 500/mes (500 por solicitud) Premium; 10,000/mes (2,000 por solicitud) Business. |
| Dominios de enlace | 1 Premium, 5 Business |
| Escaneos | Ilimitados. Un correo de verificación a los 100,000 escaneos por código al mes en Premium, 1,000,000 en Business; nada se limita. |
| Ventana de análisis de escaneos | 92 días por solicitud |
| Tamaño de página de listado | 100 (?limit=) |
| Etiqueta / subtítulos / slug | 120 / 60 / 40 caracteres |
| Tiempo de espera y reintentos del webhook | 8 segundos; reintentos después de 1, 5, 15, 60, 240 y 720 minutos; se apaga después de 20 fallos consecutivos. |
| Después de dejar Business | Las claves y webhooks siguen funcionando 30 días, luego 402. Nada se elimina. |
El uso justo es para lo que está diseñado el plan. Nada se limita en el número; la API se detiene al doble y una persona te contacta primero. Volúmenes más altos: hello@qrflow.codes.
Seguridad, para ti y para las personas que escanean
Cómo QRFLOW protege tu cuenta
- Las claves se muestran una vez y se almacenan como hashes SHA-256. Nadie en QRFLOW puede leer una clave de vuelta; si la pierdes, crea una nueva.
- Cada solicitud está limitada a la cuenta a la que pertenece la clave. Un id de código de otra cuenta es un 404, nunca una fuga.
- Los alcances son fijos por clave, así que una clave para un panel de informes no puede crear ni eliminar códigos.
- 600 solicitudes por minuto por clave; más allá de eso es un 429 limpio, no una ralentización para todos.
- Los destinos están en lista blanca: http, https, mailto, tel, sms, geo y una lista corta de esquemas de aplicaciones (whatsapp, tg, signal, spotify, tiendas de aplicaciones). javascript:, data: y file: se rechazan al crear, así que una integración comprometida no puede convertir tus códigos en un ataque.
- Las URL de webhook deben ser https en hosts públicos; QRFLOW nunca llama a redes privadas ni a sí mismo. Cada entrega está firmada y cada payload tiene un id estable.
- Los clientes OAuth se registran con PKCE S256 y los tokens están vinculados al servidor para el que se emitieron; un token para el servidor MCP no puede reproducirse en la API REST.
- Cualquiera puede revocar una clave o desconectar una aplicación en la página de Cuenta; el efecto es inmediato.
Qué hacer por tu parte
- Variables de entorno, nunca código fuente. Si una clave llega a un historial de git, revócala.
- Solo del lado del servidor. La API rechaza orígenes de navegador, pero tus propios endpoints que la envuelven también necesitan autenticación, o cualquiera puede crear códigos a tu cargo.
- Dale a cada integración su propia clave con los alcances que necesita, nombrada según la integración. Revocar una entonces afecta solo a una cosa.
- Verifica las firmas de webhook y rechaza marcas de tiempo de más de cinco minutos.
- Si los datos de tus usuarios van a etiquetas o destinos, recuerda que QRFLOW los almacena; mantén los datos personales fuera de las etiquetas cuando puedas.
Qué registran los escaneos y qué no
- Cada redirección almacena tipo de dispositivo, país, ciudad, referente, navegador, sistema operativo e idioma, derivados de la solicitud, y un hash unidireccional de código + día + IP + agente de usuario para que el propietario pueda contar visitantes únicos. El hash no puede convertirse de vuelta en una dirección.
- No se establecen cookies en la persona que escanea y la dirección IP en sí no se guarda con el escaneo. Los bots conocidos y los rastreadores de vista previa de enlaces se omiten.
- Eliminar un código elimina sus escaneos. Eliminar la cuenta elimina todo.
- Texto completo: https://qrflow.codes/privacy y https://qrflow.codes/terms.
Versionado y estabilidad
La API está versionada en la ruta: /api/v1. Dentro de v1 añadimos campos, endpoints, tipos y eventos; no eliminamos ni renombramos nada, y los campos desconocidos en las respuestas deben ignorarse en tu código.
Si un cambio alguna vez tiene que romper v1, se publica como /api/v2 y v1 sigue funcionando durante al menos doce meses. Los propietarios de claves reciben un correo sobre las deprecaciones con 90 días de antelación.
El servidor MCP sigue la misma regla para sus herramientas: los argumentos solo se añaden y cada herramienta mantiene su nombre.
El documento OpenAPI en /api/v1/openapi.json y el Markdown en /llms-full.txt se generan desde el código que sirve la API, así que describen lo que está activo hoy.
Preguntas que hacen los desarrolladores
¿Tengo que pagar para usar la API de QRFLOW?
Las claves de API vienen con el plan Business, $29 al mes, mes a mes. El servidor MCP (Claude, ChatGPT, Cursor, Claude Code) y OAuth para tu propia aplicación funcionan en todos los planes mediante inicio de sesión, y lo que puedan crear sigue el plan. GET /catalog, GET /frames y /preview.svg no necesitan clave en absoluto.
¿Puedo generar códigos QR gratis a través de la API?
No con una clave. Para una imagen estática simple, el generador gratuito en qrflow.codes o cualquier biblioteca QR de código abierto hace el trabajo. La API es para códigos dinámicos, tu propio dominio, análisis, bulk y webhooks, que es para lo que está una cuenta de pago.
¿Puedo llamar a la API desde el navegador?
No. Rechaza orígenes de navegador para que una clave nunca termine en una página web. Llámala desde un manejador de ruta, acción de servidor, función edge o backend, y llama a eso desde tu página.
¿Qué formatos de imagen obtengo?
SVG con tus colores, marco, subtítulos y logo (GET /codes/:id/image.svg, 256 a 4096 px nominales) y un PNG simple (GET /codes/:id/image.png, 256 a 2048 px). Ambos aceptan un bearer, o la URL firmada svg_download_url / png_download_url que cada objeto Code lleva, que funcionan durante 24 horas sin cabecera. La herramienta MCP get_qr_image devuelve el PNG en línea más ambos enlaces.
¿Puedo cambiar un código QR después de imprimirlo?
Sí, si es dinámico (url, phone, email, sms, location en un plan de pago). PATCH /codes/:id con un nuevo destination_data; la imagen no cambia, el siguiente escaneo va al nuevo lugar. Los códigos Wi-Fi, vCard y texto llevan su contenido en la imagen y no pueden cambiar.
¿Cuál es la diferencia entre short_url y el destino?
short_url es el enlace dentro de la imagen (go.example.com/menu). El destino es a dónde redirige ese enlace (https://example.com/menu-fall). Imprimes short_url una vez y cambias el destino tantas veces como quieras.
¿Pueden los códigos usar mi propio dominio?
Sí. Premium conecta 1 dominio, Business 5; añades un CNAME y lo verificas en la página de Cuenta. Business elige un dominio por código con domain_id. Los nombres de enlace (slug) crean rutas legibles en él.
¿Qué registra un escaneo sobre la persona que escanea?
Tipo de dispositivo, país, ciudad, referente, navegador, sistema operativo e idioma, de la solicitud, más un hash diario unidireccional para conteos de visitantes únicos. Sin cookies y la dirección IP no se almacena. Suficiente para una gráfica, no suficiente para identificar a nadie. Detalles: https://qrflow.codes/privacy#scans
¿Hay un paquete npm o PyPI?
npm: npm install qrflow\ (https://www.npmjs.com/package/qrflow), cero dependencias, ESM y CommonJS, tipos TypeScript completos, funciona en Node 18+, Bun, Deno y Workers; envuelve cada endpoint, reintenta 429s e incluye verifyWebhook/parseWebhook. Python: un cliente de un solo archivo en https://qrflow.codes/sdk/qrflow.py (solo biblioteca estándar) con verify_webhook; un paquete PyPI seguirá.
¿Funciona con Claude, ChatGPT, Cursor y Claude Code?
Sí. QRFLOW es un servidor MCP en https://qrflow.codes/mcp. Añádelo como conector, inicia sesión una vez y pregunta en palabras simples. Once herramientas cubren crear, editar, pausar, nombrar, bulk, análisis, imágenes y dominios.
¿Pueden mis propios usuarios conectar sus cuentas QRFLOW a mi aplicación?
Sí, con OAuth 2.0. Registra un cliente en /api/oauth/register (sin necesidad de cuenta), envía a los usuarios a /oauth/authorize con PKCE y llama a la API con su token. Los códigos caen en su cuenta bajo su plan.
¿Cómo pruebo webhooks en localhost?
Expón tu servidor de desarrollo con un túnel (cloudflared o ngrok) y usa su dirección https como URL de webhook, luego presiona Test en Cuenta › Webhooks para recibir un ping firmado. Las URL de webhook deben ser https públicas; localhost y direcciones privadas se rechazan.
¿Qué pasa con mi integración si cancelo Business?
Las claves y webhooks siguen funcionando durante 30 días, luego responden 402. Los códigos, escaneos y dominios permanecen en la cuenta. Volver a suscribirte reactiva todo con las mismas claves.
¿Cómo hago un código de reseña de Google, Instagram, Wi-Fi o PDF a través de la API?
GET /catalog lista cada tipo con sus campos. Luego POST /codes con el tipo y campos del tipo, añadiendo subtype para tipos: { type: 'url', destination_data: { subtype: 'googlereview', placeId: 'ChIJ…' } }, { type: 'wifi', destination_data: { ssid, password, encryption: 'WPA' } }, { type: 'url', destination_data: { subtype: 'instagram', handle: 'acme' } }.
¿Puede la API subir un logo a un código?
Aún no. Añade el logo en el panel; image.svg lo incluye y has_logo te dice que está ahí. Los colores, marcos y subtítulos se pueden configurar a través de la API.
¿Es estable la API?
v1 solo añade; nunca elimina ni renombra. Un cambio que rompa se publicaría como v2 con v1 mantenido durante al menos doce meses y 90 días de aviso por correo.
Para agentes, herramientas y asistentes que leen esto
Legible por máquina
Todo en esta página existe en una forma que el software puede obtener. Todo se genera desde el código que sirve la API, así que nunca está desactualizado.
| URL | Qué es | |
|---|---|---|
| llms-full.txt | https://qrflow.codes/llms-full.txt | Toda esta referencia como Markdown: conceptos, cada endpoint, cada herramienta MCP, recetas, solución de problemas, preguntas frecuentes. Generada desde la misma fuente que esta página. |
| developers.md | https://qrflow.codes/developers.md | El mismo documento, para herramientas que obtienen un .md. |
| llms.txt | https://qrflow.codes/llms.txt | El índice del sitio para asistentes, que apunta aquí. |
| La API en una página | https://qrflow.codes/qr-code-api | Qué hace la API, cuándo una biblioteca del lado del cliente es la mejor respuesta, y cuánto cuesta. La versión corta de esta referencia, para decidir en lugar de construir. |
| openapi.json | https://qrflow.codes/api/v1/openapi.json | OpenAPI 3.1. Impórtalo en Postman, Insomnia, un generador de código o una Acción de ChatGPT. |
| Servidor MCP | https://qrflow.codes/mcp | HTTP transmisible, OAuth con registro dinámico o una clave de negocio como portador. |
| server.json | https://qrflow.codes/.well-known/mcp/server.json | El manifiesto del registro MCP. |
| Descubrimiento OAuth | https://qrflow.codes/.well-known/oauth-authorization-server | Metadatos RFC 8414; el documento de recurso protegido está junto a él. |
| Paquete npm | https://www.npmjs.com/package/qrflow | npm install qrflow. Cliente tipado, cero dependencias, verificación de webhooks. Cliente Python de un solo archivo en https://qrflow.codes/sdk/qrflow.py. |
| GitHub | https://github.com/nativecodeapps/qrflow-sdk | Clientes, instantánea de OpenAPI y ejemplos ejecutables para webhooks de Next.js, Workers, Express, FastAPI y Flask. Issues y PRs bienvenidos. |
Preguntas, ideas, algún tipo de código que deberíamos añadir: hello@qrflow.codes. Términos: /terms. Privacidad: /privacy.