QRFLOW.codes

oficial

Crie códigos QR, redirecione códigos dinâmicos impressos, nomeie links no seu próprio domínio e leia análises de escaneamento.

O que você pode fazer com QRFLOW Codes MCP?

  • Criar QR codes dinâmicos — Peça para gerar um short_url imprimível para qualquer URL, com o destino podendo ser alterado a qualquer momento após a impressão.
  • Redirecionar códigos impressos — Solicite a atualização do destination_data de um código para uma nova URL; códigos já impressos continuam funcionando sem necessidade de reimpressão.
  • Obter análises de escaneamento — Peça relatórios de Scans por dia, país ou dispositivo para ver o desempenho de um código impresso.
  • Pausar ou expirar códigos — Instrua a pausar um código ou definir uma data de expiração quando uma campanha ou oferta terminar.
  • Nomear links no seu domínio — Solicite a definição de um slug legível no seu próprio domínio para que os códigos sejam impressos como go.yourbrand.com/menu em vez de um caminho aleatório.

Servidor MCP hospedado

npx add-mcp 'https://qrflow.codes/mcp'

Instala no Claude Code, Codex, Cursor e outros

Documentação

QRFLOW.codes para desenvolvedores

Comece aqui

QRFLOW.codes cria códigos QR que você pode alterar depois de imprimir, no seu próprio domínio, com análise de escaneamentos, e permite que seu software e seu assistente de IA façam tudo isso por meio de uma única API.

Um código QR é uma imagem de uma string. A maioria dos geradores coloca seu endereço web na imagem; se o endereço mudar, a impressão fica morta. QRFLOW coloca um link curto na imagem (go.yourbrand.com/menu ou qrflow.codes/q/x7k2p9a) e o redireciona. Você muda para onde ele aponta quando quiser, e cada escaneamento é contabilizado. Isso é um código dinâmico, e é isso que vale uma API.

A API faz exatamente o que o painel faz: cria códigos de qualquer tipo, aponta-os para um novo destino, nomeia seus links, escolhe em qual dos seus domínios eles serão impressos, obtém números de escaneamento, cria milhares de uma vez e é notificada quando algo acontece (webhooks). As mesmas ferramentas são expostas como um servidor MCP, para que Claude, ChatGPT, Cursor e Claude Code possam gerenciar sua conta em linguagem natural.

Acesso: a API REST aceita uma chave de API Business ($29 por mês, criada na página Conta) ou um token OAuth. O servidor MCP funciona em todos os planos por meio de login OAuth; o que o assistente pode criar segue o plano como em qualquer outro lugar.

Decidindo se vale a pena usar? A página da API de códigos QR é a versão resumida: o que ela faz, o que uma biblioteca client-side faz melhor e quanto custa.

Se você é um assistente de IA ajudando alguém a construir com QRFLOW

Bem-vindo. Esta página foi escrita para você tanto quanto para a pessoa que você está ajudando. Aqui está o que uma boa ajuda parece:

  1. Confirme a conta: chaves de API exigem o plano Business. Se eles não tiverem uma, aponte para https://qrflow.codes/pricing e Conta › Chaves de API; não invente uma chave ou um nível gratuito que não existe.
  2. Mantenha a chave no servidor. Leia-a de uma variável de ambiente (QRFLOW_KEY) em um route handler, server action, edge function ou backend. A API recusa origens de navegador de propósito (CORS), então código somente front-end não pode chamá-la.
  3. Crie códigos dinâmicos com type "url" e destination_data.url. Salve o id e short_url retornados no seu banco de dados. Imprima ou exiba short_url; o destino pode ser alterado depois com PATCH /codes/:id e a impressão continua válida.
  4. Para exibir ou salvar a imagem: todo objeto Code tem png_download_url e svg_download_url, links assinados que funcionam por 24 horas sem cabeçalho (ótimo para um salvamento único ou uma prévia). Para algo permanente no seu aplicativo, faça proxy de GET /codes/:id/image.svg pelo seu servidor com a chave, ou codifique short_url com qualquer biblioteca QR no cliente; o rastreamento continua funcionando porque o link passa pelo QRFLOW.
  5. Envie apenas os campos que mudam no PATCH. Nunca altere slug ou domain_id depois que algo foi impresso; ambos mudam o link impresso.
  6. Prefira paused: true em vez de DELETE quando uma impressão existir. Excluir mata o link.
  7. Para eventos, use webhooks (Conta › Webhooks ou POST /webhooks) e verifique o cabeçalho X-QRFLOW-Signature com o corpo bruto da requisição. Não faça polling de GET /codes em loop.
  8. Se eles quiserem usar QRFLOW pelo chat em vez de código, conecte o servidor MCP em https://qrflow.codes/mcp; — nenhuma chave é necessária para isso.
  9. Quando algo falhar, leia error e message no corpo JSON. A seção de solução de problemas abaixo mapeia cada código de erro para uma correção.
  10. A referência completa em Markdown está em https://qrflow.codes/llms-full.txt e o documento OpenAPI 3.1 em https://qrflow.codes/api/v1/openapi.json. Ambos são gerados a partir da mesma fonte desta página.

Humanos: este bloco é como garantimos que o assistente com o qual você está trabalhando dê a versão segura de cada resposta. Ele também serve como um bom resumo.

Qual plano você precisa

A chave de API é a única coisa exclusiva do Business. Tudo o que um assistente faz pelo servidor MCP, e tudo o que um aplicativo de terceiros faz via OAuth, funciona em qualquer plano e simplesmente segue os recursos desse plano. Preços completos e uso justo: /pricing.

FreePremium $4Business $29
Chaves de API (REST do seu código)NãoNãoSim, até 10 chaves
Servidor MCP (Claude, ChatGPT, Cursor, Claude Code)Sim, faça loginSimSim, login ou chave
OAuth para seu próprio aplicativo (usuários conectam seu QRFLOW)SimSimSim
Códigos dinâmicos (mudar destino após imprimir)Não, somente estáticoSimSim
Seu próprio domínio de linkNão1 domínio5 domínios, escolha por código
Nomes de link (go.brand.com/menu)NãoSimSim
Análise de escaneamentosNãoSimSim
WebhooksNãoNãoSim, até 10
Criação em massaNão500/mês, 500 por requisição10.000/mês, 2.000 por requisição
Códigos salvos (uso justo)Alguns1.00025.000
Assentos de equipe115
Preço$0$4/mês$29/mês

Doze palavras que fazem todo o trabalho

Conceitos

Leia isto uma vez e todos os endpoints abaixo farão sentido.

Static code

O conteúdo está dentro da imagem. Códigos de Wi-Fi, cartão de contato (vCard) e texto simples são sempre estáticos, e no plano Free todo código é. Um código estático não precisa de servidor e nunca expira, e não pode ser alterado ou contabilizado.

Dynamic code

A imagem contém um link curto que o QRFLOW redireciona. Códigos url, phone, email, sms e location são dinâmicos nos planos Premium e Business. Você pode re-apontar, pausar, expirar, renomear e contabilizar sem tocar na impressão.

short_url

A string exata codificada em um código dinâmico, e a coisa a imprimir. É https://qrflow.codes/q/<short_code> até você conectar um domínio, depois https://<your domain>/<slug or short_code>. Todo objeto Code o carrega.

short_code

Sete caracteres aleatórios, únicos por código, atribuídos na criação e nunca alterados. O caminho de fallback quando um código não tem nome de link.

slug (link name)

Um caminho legível no seu próprio domínio: go.example.com/menu. 3 a 40 letras minúsculas, dígitos e hífens, único dentro da sua conta, somente com um domínio conectado. Defina-o antes de imprimir: alterá-lo muda o link impresso.

Link domain

Um hostname que você possui (go.example.com) apontado para o QRFLOW por um CNAME e verificado na página Conta. Premium recebe um; Business recebe cinco e pode escolher por código com domain_id. O domínio ativo mais antigo é o padrão.

Kind, type and subtype

type é a codificação: url, text, wifi, vcard, email, phone, sms, location. Um kind é um nome mais amigável para o que as pessoas querem (instagram, googlereview, whatsapp, pdf, menu, appstore,...). A maioria dos kinds são códigos url com destination_data.subtype definido. GET /catalog lista cada kind com seus campos; kind em um Code informa qual é.

destination_data

Os campos para o type, como strings: { url } para um site, { ssid, password, encryption } para Wi-Fi, { placeId } para uma avaliação do Google, { handle } para Instagram. Em um código dinâmico, você pode substituí-lo a qualquer momento.

Scans

Cada redirecionamento registra tipo de dispositivo, país, cidade, referenciador, navegador, SO e idioma da própria requisição, além de um hash diário unidirecional para contar visitantes únicos. Nenhum cookie é definido e o endereço IP não é armazenado. scans em um Code é o total vitalício; GET /codes/:id/scans detalha isso.

Source

Todo código lembra o que o criou: dashboard, api:<key name>, mcp, canva ou bulk. Isso aparece no painel e nos payloads de webhook, para que você possa distinguir os códigos da sua integração dos feitos manualmente.

Workspace

Um proprietário Business pode convidar até quatro colegas de equipe. Chaves e webhooks pertencem à conta do proprietário; códigos feitos por qualquer pessoa no workspace são visíveis para toda a equipe.

Cinco minutos

Início rápido

Obtenha uma chave

  1. No plano Business, abra Conta › Chaves de API.
  2. Dê um nome relacionado ao propósito ("Backend da loja", "Relatórios") e escolha seus escopos. Escopos não podem ser alterados depois; crie uma nova chave se precisar de mais.
  3. Copie-a uma vez. Ela se parece com qrf_live_…. Coloque-a em uma variável de ambiente chamada QRFLOW_KEY.
  4. Envie-a como Authorization: Bearer $QRFLOW_KEY em toda requisição. Essa é toda a história da autenticação.

Chaves são para servidores. Nunca coloque uma em uma página web, aplicativo móvel ou planilha compartilhada; revogue e re-emita se uma vazar.

Os mesmos cinco passos em curl, TypeScript e Python. Cada um cria um código dinâmico, baixa sua imagem, muda para onde aponta e lê seus escaneamentos.

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

Autenticação

Chaves de API (Business)

Até 10 por conta, 600 requisições por minuto cada, armazenadas com hash, exibidas uma vez. Uma chave carrega os escopos com os quais foi criada:

EscopoPermite
profileGET /me: plano, recursos, limites. Toda chave tem isso.
codes:readListar e ler códigos, baixar imagens.
codes:writeCriar, alterar, tornar dinâmico, excluir, criação em massa.
analytics:readGET /codes/:id/scans.
domains:readGET /domains (necessário para usar domain_id de forma sensata).
webhooks:manageListar, criar, testar e excluir webhooks.

OAuth 2.0 (qualquer plano, para aplicativos e assistentes)

Quando os códigos devem pertencer às contas dos seus usuários em vez da sua, ou quando um assistente de chat é o cliente, use OAuth. Clientes se registram; PKCE S256 é obrigatório para clientes públicos; tokens podem ser vinculados ao servidor MCP com resource=. A receita OAuth explica passo a passo.

EndpointURLNotas
Autorizaçãohttps://qrflow.codes/oauth/authorizeEnvie o usuário para cá; ele faz login e pressiona Permitir.
Tokenhttps://qrflow.codes/api/oauth/tokenConcessões authorization_code e refresh_token. Tokens de acesso duram 1 hora, tokens de atualização 90 dias.
Revogarhttps://qrflow.codes/api/oauth/revokeRFC 7009. Usuários também podem desconectar em Conta › Aplicativos conectados.
Registrar um clientehttps://qrflow.codes/api/oauth/registerRegistro dinâmico RFC 7591, sem necessidade de conta. Clientes públicos recebem um dyn_ client_id e devem usar PKCE S256.
Descobertahttps://qrflow.codes/.well-known/oauth-authorization-serverRFC 8414. O documento de recurso MCP está em /.well-known/oauth-protected-resource.

Tokens de acesso duram 1 hora, tokens de atualização 90 dias. Usuários veem aplicativos conectados em Conta › Aplicativos conectados e podem desconectar a qualquer momento. Um token emitido para https://qrflow.codes/mcp é recusado em /api/v1, e vice-versa.

As coisas que as pessoas realmente pedem

Receitas de construção

Cada receita é completa e copiada diretamente de código funcional. Escolha a que corresponde à sua stack; o formato é sempre o mesmo: uma chamada no lado do servidor com a chave, salve id e short_url, exiba a imagem.

Next.js: uma rota que cria um código e uma rota que o exibe

Quando: Você tem um aplicativo Next.js (App Router) e quer um botão que cria um código QR e uma página que o exibe.

  1. Coloque sua chave em .env.local como QRFLOW_KEY. Nunca a prefixe com NEXT_PUBLIC_.
  2. Adicione um route handler POST que cria o código e retorna id e short_url.
  3. Adicione uma rota GET que faça proxy da imagem para que o navegador nunca veja a chave.
  4. Armazene id e short_url no seu próprio registro (pedido, mesa, produto, 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>
  • Server Actions funcionam da mesma forma: chame fetch com a chave dentro da action.
  • Para Pages Router, o mesmo código vai em pages/api/qr.ts com req/res.

Exibir o QR sem proxy: renderize short_url você mesmo

Quando: Você quer a imagem no navegador imediatamente e não precisa dos frames ou logotipo do QRFLOW nela. Qualquer biblioteca de QR funciona, porque o código É o link curto

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 é o caminho mais rápido para uma prévia. Para impressão, baixe /codes/:id/image.svg: ele carrega as cores salvas, moldura, legendas e logotipo, e é um vetor.
  • Se você depois mudar o slug ou domain_id, o short_url muda; renderize novamente.

Express ou qualquer servidor Node

Quando: Um backend Node simples.

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

Quando: Um runtime somente com fetch, sem built-ins do 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_...
  • O pacote npm qrflow\ usa apenas fetch e WebCrypto, então roda em todos esses sem alterações.

Python: FastAPI, Flask, Django, um script

Quando: Seu backend é 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")

Um código QR por pedido, mesa, produto, ingresso ou evento

Quando: Cada linha em uma das suas tabelas precisa do seu próprio código, feito automaticamente.

  1. Adicione duas colunas à sua tabela: qrflow_code_id (uuid) e qr_short_url (texto).
  2. Quando uma linha for criada, faça POST /codes com a URL pública da linha e um rótulo que a identifique ("Pedido 10432", "Mesa 7"). Salve o id e o short_url.
  3. Quando a página da linha mudar (novo domínio, novo caminho), faça PATCH em destination_data. Os códigos impressos continuam funcionando.
  4. Quando a linha for desativada, faça PATCH { paused: true } se algo foi impresso; DELETE apenas se nada foi impresso.
  5. Precisa de milhares de uma vez (um menu por mesa para 300 restaurantes)? Use POST /codes/bulk em lotes e mapeie os códigos retornados de volta às suas linhas pelo rótulo ou pela ordem.
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 });
  • O uso justo é de 25.000 códigos salvos no Business; a API para no dobro disso. Se você precisar de um código por recibo para sempre, fale conosco primeiro: hello@qrflow.codes.

Mudar para onde um código impresso aponta

Quando: Uma campanha terminou, uma página mudou, um PDF foi substituído, uma temporada mudou.

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" } }'

Pause-o ou dê a ele uma data de término

# 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" }'
  • Apenas códigos dinâmicos podem ser redirecionados. Um código de URL criado em uma conta Free, ou um código de Wi-Fi/vCard/texto, responde 400 not_dynamic. Para um código de url/telefone/email/sms/localização em um plano pago, POST /codes/:id/dynamic o converte, e você precisa renderizar e imprimir novamente porque a imagem muda.
  • Um leitor vê o novo destino na próxima leitura. Não há cache para esperar.

Imprimir códigos no seu próprio domínio

Quando: Você quer go.example.com/menu no código em vez de qrflow.codes/q/x7k2p9a.

  1. Na página da Conta, em Seu próprio domínio de link, adicione go.example.com e crie o CNAME que ela mostra no seu provedor de DNS. A verificação geralmente é concluída em minutos.
  2. A partir daí, todo novo short_url de código dinâmico usa esse domínio. Os códigos existentes também mudam: a imagem deles codificava qrflow.codes/q/..., que continua redirecionando, então nada impresso quebra.
  3. Dê nomes legíveis aos códigos com slug: PATCH { "slug": "menu" } cria go.example.com/menu. Faça isso antes de imprimir.
  4. No Business com vários domínios, GET /domains os lista com ids; passe domain_id no POST ou PATCH para escolher por código.

Dê um nome a um link e escolha um domínio

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

Um gráfico de leituras no seu próprio painel

Quando: Você quer leituras por dia, por país ou por dispositivo ao lado dos seus próprios 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
  • Uma requisição cobre até 92 dias; faça um loop para períodos maiores. As datas são em UTC.
  • Para números ao vivo sem polling, assine um webhook para o evento de leitura: você recebe cada leitura com seus detalhes em lotes a cada poucos minutos.

Receber webhooks no Next.js e verificá-los

Quando: Você quer saber quando um código é lido ou alterado, no seu próprio banco de dados, quase em tempo real.

  1. Crie o webhook em Conta › Webhooks ou com POST /webhooks. Copie o segredo (whsec_...) uma vez para QRFLOW_WEBHOOK_SECRET.
  2. Leia o corpo bruto como texto antes de fazer o parse; a assinatura cobre os bytes exatos.
  3. Verifique e então use o evento. Responda 2xx rápido; faça o trabalho lento depois de responder ou em uma fila.
  4. Pressione Testar no webhook para receber um ping assinado e confirmar a conexão.
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, exponha seu servidor de desenvolvimento com um túnel (cloudflared tunnel --url http://localhost:3000, ou ngrok) e use essa URL https para o webhook enquanto desenvolve.
  • O pacote npm faz isso por você: import { parseWebhook } from "qrflow"\ verifica e faz o parse em uma chamada (WebCrypto, então também roda em Workers e Deno). O cliente Python inclui verify_webhook.

Receber webhooks em Python

Quando: Flask, FastAPI ou Django recebendo os mesmos 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

Milhares de códigos a partir de um CSV

Quando: Um código por SKU, por assento, por etiqueta de ativo, por mala direta.

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");
}
  • O bulk cria apenas códigos de URL dinâmicos, todos com as mesmas cores. Os códigos voltam na ordem em que você os enviou, menos as linhas rejeitadas; use o rótulo para corresponder em caso de dúvida.
  • Webhooks recebem um evento code.created por requisição em bulk com codes[] em vez de um por código.

Deixe seus usuários conectarem a própria conta QRFLOW (OAuth)

Quando: Você está construindo um produto para outras pessoas e quer que os códigos caiam nas contas QRFLOW delas, não na sua.

  1. Registre um cliente uma vez: POST https://qrflow.codes/api/oauth/register com client_name e redirect_uris. Você recebe um client_id (e um client_secret para clientes confidenciais).
  2. Envie o usuário para /oauth/authorize com response_type=code, client_id, redirect_uri, scope, state e PKCE (code_challenge, code_challenge_method=S256).
  3. Troque o código em /api/oauth/token. Armazene o refresh token; os access tokens duram uma hora.
  4. Chame /api/v1 com Authorization: Bearer <access_token>. Tudo funciona exatamente como com uma chave, sob o plano do usuário.
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
  • Os escopos são os mesmos seis das chaves de API. Peça o mínimo que precisar; a tela de consentimento os lista.
  • Se seu aplicativo é um assistente de chat ou agente, adicione resource=https://qrflow.codes/mcp à requisição de autorização e fale com o servidor MCP; o token será vinculado a ele.

Zapier, Make, n8n: sem código algum

Quando: Você quer que leituras ou novos códigos caiam em uma planilha, um canal do Slack ou um CRM.

  1. Crie um gatilho de catch-hook (Zapier: Webhooks by Zapier › Catch Hook; Make: Custom webhook; n8n: nó Webhook) e copie a URL https.
  2. Em Conta › Webhooks, adicione essa URL e escolha os eventos. Pressione Testar; o ping aparece na ferramenta e dá a ela o formato do payload.
  3. Mapeie data.scans[] (para leitura) ou data.code (para code.*) para sua planilha, mensagem ou registro.
  4. Para criar códigos a partir dessas ferramentas, use o módulo HTTP delas contra POST /codes com o cabeçalho Authorization. Mantenha a chave no armazenamento de credenciais da ferramenta.
  • Essas ferramentas não conseguem verificar a assinatura. A URL que elas fornecem é impossível de adivinhar, que é a proteção que você tem; não a publique em lugar nenhum.

Vibe coding

Prompts para colar

Assistentes de codificação constroem a coisa certa quando recebem as regras antecipadamente. Estes prompts carregam as regras. Cole um, preencha o colchete, e o assistente lerá a referência Markdown antes de escrever uma linha.

Adicionar códigos QR ao meu aplicativo

Claude, ChatGPT, Cursor, Codex, Windsurf, Copilot Chat: cole no 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"].

Ensine seu repositório sobre QRFLOW

Coloque em CLAUDE.md, AGENTS.md, .cursorrules ou .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.

Em Lovable, Bolt, v0, Replit e outros construtores de aplicativos

Construtores focados em front-end que dão a você um backend (Supabase, funções 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

Um GPT personalizado que gerencia meus códigos

ChatGPT › Criar um GPT › Ações

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).

Coisas para dizer depois que o conector estiver ativo

Claude, ChatGPT, Claude Code com o 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

Use-o a partir do Claude, ChatGPT, Cursor e Claude Code

QRFLOW é um servidor MCP em https://qrflow.codes/mcp. Conecte-o uma vez, faça login e então diga coisas como "crie um código QR para nossa página de menu de outono, chamado menu no meu domínio", "aponte o código do pôster do lobby para a nova página" ou "quantas leituras o panfleto teve na semana passada, por país?". O assistente obtém as mesmas ferramentas que esta API oferece, sob as mesmas regras, e os códigos caem no seu painel com mcp como fonte.

Não é desenvolvedor? A versão em linguagem simples, com os cliques exatos para cada assistente, está em Crie códigos QR com seu assistente de IA.

Conectar

Personalizar › Conectores › Adicionar conector personalizado › cole o endereço, ou pressione Conectar na listagem do diretório do QRFLOW. O Claude abre um login do QRFLOW; pressione Permitir. Funciona em todos os planos.

https://qrflow.codes/mcp

Configurações › Segurança e login › Modo desenvolvedor ativado, depois Configurações › Plugins › + › cole o endereço; faça login quando solicitado. Plus, Pro, Team, Enterprise e Edu.

https://qrflow.codes/mcp

Um comando, depois /mcp para fazer login. Adicione uma chave Business como cabeçalho para pular o login.

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, qualquer cliente MCP

Adicione um servidor HTTP na URL. O login OAuth acontece no navegador; ou passe um cabeçalho Authorization com uma chave.

{
  "mcpServers": {
    "qrflow": { "type": "http", "url": "https://qrflow.codes/mcp" }
  }
}

Seu próprio agente (SDK Anthropic ou OpenAI)

Aponte o conector ou ferramenta MCP para a URL com uma chave Business como bearer; nenhum fluxo de navegador é necessário.

// Anthropic Messages API, MCP connector
mcp_servers: [{ type: "url", url: "https://qrflow.codes/mcp", name: "qrflow", authorization_token: process.env.QRFLOW_KEY }]

O que o assistente pode fazer

FerramentaO que ela fazEscopo
list_code_kindsCada tipo de código com seus campos e o plano necessário. O assistente chama isso antes de criar algo incomum.profile
list_domainsSeus domínios de link, o padrão e seus ids.domains:read
get_qr_imageUm PNG que o assistente pode mostrar ou salvar, além da URL SVG para impressão.codes:read
get_accountQuem está conectado, plano, limites.profile

Como isso permanece seguro

  • O assistente só segura um token para a sua conta, emitido depois que você pressionou Permitir em uma página do QRFLOW. Desconecte a qualquer momento em Conta › Aplicativos conectados.
  • Os tokens são vinculados ao servidor MCP; eles não podem ser reproduzidos contra a API REST.
  • Cada escrita passa pela mesma validação do painel: destinos na lista de permissões, verificações de plano, limites de uso justo.
  • Ferramentas destrutivas se descrevem com cuidado: delete_qr_code diz ao modelo para preferir pausar quando existe uma impressão.
  • Documentos de descoberta estão em /.well-known/oauth-authorization-server e /.well-known/oauth-protected-resource; o registro é RFC 7591; apenas PKCE S256.

SDKs e a especificação OpenAPI

  • TypeScript / JavaScript: npm install qrflow (npm). Zero dependências, ESM e CommonJS, tipos completos; roda em Node 18+, Bun, Deno e Workers. Repete 429s para você e inclui verifyWebhook / parseWebhook.
  • Python 3.9+, apenas biblioteca padrão: qrflow.py.
  • OpenAPI 3.1: /api/v1/openapi.json. Importe no Postman ou Insomnia, gere um cliente em qualquer linguagem ou anexe a uma Ação do ChatGPT.
  • Ambos os clientes lançam um erro tipado (QRFlowError com status, code, message, retryAfter) e incluem um verificador de webhook. O código-fonte TypeScript de arquivo único ainda está em /sdk/qrflow.ts se você preferir incorporá-lo.
TypeScriptPythonChamadas
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)O endereço da imagem (busque com a chave)
verifyWebhook(raw, header, secret)verify_webhook(raw, header, secret)Verificação de assinatura para entregas

Referência de endpoints

Base URL https://qrflow.codes/api/v1. Corpos e respostas são JSON. Datas são ISO 8601 em UTC. Envie apenas os campos que mudam no PATCH.

GETprofile

Plano, feature flags e os limites que se aplicam a esta conta, além dos escopos da chave.

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) e os cerca de 50 subtipos (Instagram, Google review, Wi-Fi, app store…), cada um com os campos que precisa e o plano que exige. Nenhuma chave é necessária.

curl https://qrflow.codes/api/v1/catalog

GETcodes:read

Mais recentes primeiro.?limit= até 100,?q= pesquisa rótulos.

curl "https://qrflow.codes/api/v1/codes?limit=20&q=menu" \
  -H "Authorization: Bearer $QRFLOW_KEY"

POSTcodes:write

Mesmas regras do Salvar no site: códigos url, phone, email, sms e location são dinâmicos em planos pagos; Wi-Fi, vCard e text carregam seu conteúdo no padrão. Coloque um id de subtipo em destination_data.subtype para fazer, por exemplo, um código de Google review. O domain_id opcional escolhe com qual dos seus domínios de link o código é impresso (GET /domains os 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

O código com sua contagem de scans, link curto e endereço da imagem.

curl https://qrflow.codes/api/v1/codes/$ID \
  -H "Authorization: Bearer $QRFLOW_KEY"

PATCHcodes:write

Qualquer um de: destination_data (apenas códigos dinâmicos, a impressão continua válida), label, paused, expires_at (ISO ou null), slug (um nome de link no seu domínio), domain_id (com qual dos seus domínios de link este código é impresso; null = o padrão da conta), fg_color, bg_color, frame_style, frame_caption, frame_caption2. Envie apenas os campos que está alterando.

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

Removido para sempre, incluindo seu histórico de scans. O link impresso de um código dinâmico para de resolver. Prefira paused: true se a impressão ainda estiver por aí.

curl -X DELETE https://qrflow.codes/api/v1/codes/$ID \
  -H "Authorization: Bearer $QRFLOW_KEY"
204 No Content

POSTcodes:write

O padrão impresso muda (agora ele codifica o link curto), então re-renderize a imagem depois.

curl -X POST https://qrflow.codes/api/v1/codes/$ID/dynamic \
  -H "Authorization: Bearer $QRFLOW_KEY"

GETcodes:read

SVG pronto para impressão com moldura e cores.?size= define a largura da grade de módulos em px; o arquivo escala sem perda mesmo assim.

curl https://qrflow.codes/api/v1/codes/$ID/image.svg \
  -H "Authorization: Bearer $QRFLOW_KEY" -o code.svg

GETanalytics:read

?from= e?to= (datas ISO, até 92 dias, padrão os últimos 30) e?group= day, device, country, city, browser, os ou referrer. Os mesmos números da página de analytics.

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

Até 2.000 códigos URL em uma chamada, todos dinâmicos. Conta contra a mesma cota mensal de envio em massa da página Bulk (10.000 no Business). Linhas que não são endereços web voltam em rejected; o restante é criado.

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

Seus domínios conectados, seu status e com qual deles códigos dinâmicos são impressos por padrão (default_base). Passe o id de um domínio como domain_id em um código para imprimi-lo com um diferente.

curl https://qrflow.codes/api/v1/domains \
  -H "Authorization: Bearer $QRFLOW_KEY"

GETwebhooks:manage

Seus webhooks com seus eventos, último status e contagem de falhas.

curl https://qrflow.codes/api/v1/webhooks \
  -H "Authorization: Bearer $QRFLOW_KEY"

POSTwebhooks:manage

url deve ser https em um host público; events é qualquer um de scan, code.created, code.updated, code.deleted. O segredo de assinatura é retornado uma 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

Envia um ping assinado agora e relata a resposta.

curl -X POST https://qrflow.codes/api/v1/webhooks/$WEBHOOK_ID \
  -H "Authorization: Bearer $QRFLOW_KEY"

DELETEwebhooks:manage

Interrompe todas as entregas, incluindo novas tentativas na fila.

curl -X DELETE https://qrflow.codes/api/v1/webhooks/$WEBHOOK_ID \
  -H "Authorization: Bearer $QRFLOW_KEY"
204 No Content

GETpublic

Ids de molduras e o que cada uma precisa (legenda, segunda linha), agrupados como no personalizador. Nenhuma chave é necessária.

curl https://qrflow.codes/api/v1/frames

O objeto Code

Todo endpoint que toca um código retorna a mesma forma. Ignore campos que você não conhece; novos são adicionados ao longo do tempo.

CampoTipoSignificado
iduuidId estável. Use-o em todas as outras chamadas.
labelstring | nullO nome no painel. Até 120 caracteres. Pesquisável com?q=.
kindstringO id do catálogo: url, wifi, instagram, googlereview,... kind_label é o nome legível.
typestringA codificação: url, text, wifi, vcard, email, phone, sms, location.
destination_dataobjectOs campos que você enviou (mais subtype para kinds). destination é um resumo de uma linha disso.
dynamicbooleanVerdadeiro quando os scans passam pelo QRFLOW e o destino pode mudar. dynamic_capable diz se este tipo poderia ser dinâmico em um plano pago.
short_codestringSete caracteres, nunca muda.
short_urlstringO que imprimir para um código dinâmico. Inclui seu domínio e slug quando definidos.
domain_iduuid | nullCom qual domínio de link este código é impresso; null significa o padrão da conta.
fg_color, bg_colorhexCores do módulo e do fundo.
has_logobooleanUm logo foi adicionado no painel; image.svg o inclui.
frame_style, frame_caption, frame_caption2string | nullId da moldura e legendas, como no personalizador.
scansintegerContagem de scans ao longo da vida.
created_at, updated_atISO 8601UTC.
manage_urlurlA página do código no painel, para um link 'Abrir no QRFLOW'.
image_urlurlGET /codes/:id/image.svg. Precisa do cabeçalho Authorization; não é um URL de imagem público.
svg_download_url, png_download_urlurlO mesmo SVG (moldura, cores, logo) e um PNG simples através de links assinados que funcionam por 24 horas sem cabeçalho: para tags <img>, scripts e assistentes salvando um arquivo. download_expires_at diz quando expiram; qualquer leitura do código retorna novos.

Outras 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, mais secret uma vez), Me (id, email, plan, paid, features, limits, auth, scopes) e Error (error, message). O documento OpenAPI tem cada propriedade tipada.

Business

Webhooks

O QRFLOW chama seu URL https quando algo acontece. Crie um em Account › Webhooks ou com POST /webhooks; você recebe o segredo de assinatura uma vez. Até 10 por conta.

EventoQuandodados
scanEm lote: a cada poucos minutos, todos os novos scans desde a última entrega, até 500 por chamada.count, from, to, scans[] com code_id, label, short_code, slug, scanned_at, device, country, city, referrer, browser, os, language
code.createdImediatamente pela API e assistentes; em minutos pelo painel. Uma solicitação em massa envia um evento com bulk: true e codes[].code, source
code.updatedMesmo tempo. Cobre destino, rótulo, pausa, expiração, nome do link, domínio, cores, moldura e conversão para dinâmico.code, changed[] (os nomes dos campos que mudaram)
code.deletedMesmo tempo.code: { id, label, short_code, slug }
pingQuando você pressiona Testar.webhook_id, message

O que chega

Todo payload é { id, event, created_at, data }. id é estável entre novas tentativas de uma entrega, então você pode deduplicar com base nele.

{
  "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

Verifique a assinatura

X-QRFLOW-Signature: t=<unix seconds>,v1=<hex>. Calcule HMAC-SHA256 sobre ${t}.${rawBody} com seu segredo e compare com v1 em tempo constante; rejeite se t tiver mais de cinco minutos. Use os bytes brutos que você recebeu, nunca um objeto re-serializado. Receptores funcionais para Next.js e Python estão nas receitas, e ambos os SDKs incluem o auxiliar.

Regras de entrega

  • Responda qualquer 2xx dentro de 8 segundos. Faça o trabalho depois de responder.
  • Qualquer outra coisa é tentada novamente após 1, 5, 15, 60, 240 e 720 minutos.
  • Vinte falhas consecutivas desligam o webhook e enviam um e-mail ao proprietário da conta. Ligue-o novamente assim que o receptor estiver corrigido; novas tentativas na fila são retomadas.
  • URLs devem ser https em um host público. localhost, faixas privadas e o próprio qrflow.codes são recusados. Use um túnel durante o desenvolvimento.
  • Pressione Testar para receber um ping assinado e ver o status que seu servidor respondeu.

Erros

Todo erro é { "error": "<code>", "message": "<what to do>" } com o status abaixo. A mensagem é escrita para uma pessoa; mostre-a.

StatuserrorSignificado

Sintoma, causa, correção

Solução de problemas

401 invalid_token em toda chamada

Por quêO cabeçalho está errado ou a chave não está ativa.

CorreçãoEnvie exatamente Authorization: Bearer qrf_live_… (um espaço, não dois pontos). Verifique se a chave não foi revogada em Account › API keys. Se você a copiou de um chat ou documento, cuidado com um ponto final ou aspas inteligentes.

401 em /api/v1 com um token que funciona no servidor MCP

Por quêTokens emitidos para https://qrflow.codes/mcp estão vinculados a ele.

CorreçãoUse uma chave de API para REST, ou execute um segundo fluxo OAuth sem resource= para obter um token para a API REST.

402 upgrade_required ao criar uma chave ou webhook

Por quêAmbos são recursos do Business.

CorreçãoFaça upgrade em /pricing, ou use o servidor MCP, que funciona em todos os planos através do login.

402 em POST /codes de um aplicativo OAuth

Por quêO plano do usuário não inclui o que o aplicativo pediu (um código dinâmico, um domínio).

CorreçãoLeia os recursos de GET /me primeiro e adapte: crie o código mesmo assim (será estático no Free) ou diga ao usuário o que o plano precisa.

403 insufficient_scope

Por quêOs escopos são fixados quando uma chave é criada.

CorreçãoCrie uma nova chave com os escopos que você precisa e revogue a antiga. Para OAuth, peça o escopo na solicitação de autorização.

400 not_dynamic quando faço PATCH em destination_data

Por quêO código é estático: criado no Free, ou um tipo Wi-Fi/vCard/text.

CorreçãoPara url/phone/email/sms/location em um plano pago, POST /codes/:id/dynamic, depois baixe e imprima novamente (a imagem muda). Wi-Fi, vCard e text nunca podem ser dinâmicos; crie um código url que abre uma página em vez disso.

400 no_domain quando defino slug

Por quêNomes de link vivem no seu domínio.

CorreçãoConecte e verifique um domínio na página Account primeiro. Em qrflow.codes/q o caminho é sempre o short_code.

409 conflict em slug

Por quêOutro dos seus códigos tem esse nome.

CorreçãoGET /codes?q= para encontrá-lo, ou escolha outro nome. Nomes são por conta, não globais.

Erro CORS no console do navegador

Por quêA API só aceita chamadas servidor-para-servidor (e Canva). Isso é deliberado: uma chave em uma página web é uma chave vazada.

CorreçãoMova a chamada para um route handler, server action, edge function ou backend e chame isso a partir da página.

A imagem mostra uma marca d'água do QRFLOW

Por quêA conta está no plano Free.

CorreçãoPlanos pagos a removem. O Free é destinado ao gerador do próprio site.

short_url ainda diz qrflow.codes/q/… depois que adicionei meu domínio

Por quêO domínio ainda não está verificado, ou seu CNAME está errado.

CorreçãoVerifique o status na página Account ou GET /domains (status deve ser verified). Códigos existentes mudam automaticamente assim que estiver.

image_url dá 401 em uma tag <img>

Por quêEle precisa do cabeçalho Authorization, que um <img> não pode enviar.

CorreçãoUse svg_download_url ou png_download_url do mesmo objeto Code: links assinados que funcionam por 24 horas sem cabeçalho. Para algo permanente, faça proxy de image_url através do seu servidor (veja a receita do Next.js) ou codifique short_url você mesmo.

Preciso de um PNG, não um SVG

Por quêO SVG carrega moldura e logo; o PNG é o código simples.

CorreçãoGET /codes/:id/image.png (bearer ou o png_download_url assinado) retorna um PNG, de 256 a 2048 px. Para um PNG com moldura, converta o SVG com sharp ou resvg (sharp(svgBuffer).png().toBuffer()).

Meu assistente disse que o endpoint de imagem o recusou e desenhou o código ele mesmo

Por quêEle buscou image_url, que precisa de um bearer.

CorreçãoTodo código agora carrega png_download_url e svg_download_url, e get_qr_image os retorna; o assistente pode usar curl neles sem login. Um código desenhado localmente que codifica o mesmo short_url ainda funciona e ainda conta scans, mas não tem moldura e logo.

Webhook nunca chega

Por quêRegras de URL ou o receptor.

CorreçãoO URL deve ser https em um host público (sem localhost, sem IPs privados, não qrflow.codes). Pressione Testar no webhook: o resultado mostra o status que seu servidor respondeu. Eventos de scan são em lote e podem levar até cerca de cinco minutos; eventos code.* do painel também entram na fila por alguns minutos, enquanto escritas via API e MCP entregam imediatamente.

Assinatura nunca verifica

Por quêVocê assinou um corpo re-serializado. FixVerify contra os bytes brutos exatos que você recebeu, antes do parsing JSON. No Express use express.raw({ type: 'application/json' }) nessa rota; no Next.js App Router use await req.text(); no Flask request.get_data(). Depois calcule HMAC-SHA256 de ${t}.${raw}\.

O webhook se desativou sozinho

Por queVinte falhas consecutivas.

CorreçãoCorrija o receptor e depois ative-o novamente (Conta › Webhooks, ou exclua e recrie). Você recebeu um e-mail quando isso aconteceu. As tentativas na fila são retomadas.

Entregas duplicadas de webhook

Por queUm 2xx lento (mais de 8 segundos) conta como falha e é repetido.

CorreçãoResponda primeiro, processe depois. Deduplique pelo id do payload, que é estável entre tentativas.

429 rate_limited durante uma importação

Por que600 requisições por minuto por chave.

CorreçãoUse POST /codes/bulk (2.000 códigos em uma requisição) em vez de um POST por código, ou aguarde os segundos do Retry-After.

Excluí um código e o pôster impresso agora mostra 'Code not found'

Por queA exclusão é permanente e mata o link.

CorreçãoNão há desfazer. Da próxima vez use PATCH { paused: true }; um código pausado mostra uma página amigável e pode ser retomado.

Minha chave parou de funcionar depois que fiz downgrade

Por queAs chaves continuam funcionando 30 dias após sair do Business e depois respondem 402.

CorreçãoAssine novamente; nada foi excluído e as mesmas chaves voltam a funcionar.

Algo não está na lista? hello@qrflow.codes, com a requisição exata e o erro JSON que você recebeu. Contas Business têm prioridade.

Limites e uso justo

Limite
Requisições por minuto, por chave600. 429 com Retry-After além disso.
Chaves por conta10
Webhooks por conta10
Códigos salvos (uso justo)1.000 Premium, 25.000 Business; a API para no dobro disso.
Em massa500/mês (500 por requisição) Premium; 10.000/mês (2.000 por requisição) Business.
Domínios de link1 Premium, 5 Business
EscaneamentosIlimitados. Um e-mail de check-in a 100.000 escaneamentos por código por mês no Premium, 1.000.000 no Business; nada é limitado.
Janela de análise de escaneamentos92 dias por requisição
Tamanho da página de listagem100 (?limit=)
Rótulo / legendas / slug120 / 60 / 40 caracteres
Timeout e tentativas do webhook8 segundos; tentativas após 1, 5, 15, 60, 240 e 720 minutos; desliga após 20 falhas consecutivas.
Após sair do BusinessChaves e webhooks continuam funcionando por 30 dias, depois 402. Nada é excluído.

Uso justo é o que o plano cobre no preço. Nada é limitado no número; a API para no dobro e uma pessoa envia e-mail antes. Volumes maiores: hello@qrflow.codes.

Segurança, para você e para quem escaneia

Como o QRFLOW protege sua conta

  • As chaves são mostradas uma vez e armazenadas como hashes SHA-256. Ninguém no QRFLOW pode ler uma chave de volta; se você a perder, crie uma nova.
  • Cada requisição é limitada à conta à qual a chave pertence. Um id de código de outra conta é um 404, nunca um vazamento.
  • Os escopos são fixos por chave, então uma chave para um painel de relatórios não pode criar ou excluir códigos.
  • 600 requisições por minuto por chave; acima disso é um 429 limpo, não uma lentidão para todos.
  • Os destinos são permitidos por lista: http, https, mailto, tel, sms, geo e uma lista curta de esquemas de aplicativos (whatsapp, tg, signal, spotify, lojas de aplicativos). javascript:, data: e file: são rejeitados na criação, então uma integração comprometida não pode transformar seus códigos em um ataque.
  • URLs de webhook devem ser https em hosts públicos; o QRFLOW nunca chama redes privadas ou a si mesmo. Cada entrega é assinada e cada payload tem um id estável.
  • Clientes OAuth se registram com PKCE S256 e os tokens são vinculados ao servidor para o qual foram emitidos; um token para o servidor MCP não pode ser reproduzido na API REST.
  • Qualquer pessoa pode revogar uma chave ou desconectar um aplicativo na página Conta; o efeito é imediato.

O que fazer do seu lado

  • Variáveis de ambiente, nunca código-fonte. Se uma chave cair no histórico do git, revogue-a.
  • Somente no servidor. A API recusa origens de navegador, mas seus próprios endpoints que a envolvem também precisam de autenticação, ou qualquer pessoa pode criar códigos na sua conta.
  • Dê a cada integração sua própria chave com os escopos que ela precisa, nomeada conforme a integração. Revogar uma afeta apenas uma coisa.
  • Verifique as assinaturas dos webhooks e rejeite timestamps mais antigos que cinco minutos.
  • Se os dados dos seus usuários forem para rótulos ou destinos, lembre-se de que o QRFLOW os armazena; mantenha dados pessoais fora dos rótulos quando possível.

O que os escaneamentos registram e o que não registram

  • Cada redirecionamento armazena tipo de dispositivo, país, cidade, referenciador, navegador, sistema operacional e idioma, derivados da requisição, e um hash de mão única de código + dia + IP + user agent para que o proprietário possa contar visitantes únicos. O hash não pode ser revertido em um endereço.
  • Nenhum cookie é definido na pessoa que escaneia e o endereço IP em si não é mantido com o escaneamento. Bots conhecidos e rastreadores de pré-visualização de links são ignorados.
  • Excluir um código exclui seus escaneamentos. Excluir a conta exclui tudo.
  • Texto completo: https://qrflow.codes/privacy e https://qrflow.codes/terms.

Versionamento e estabilidade

A API é versionada no caminho: /api/v1. Dentro da v1 adicionamos campos, endpoints, tipos e eventos; não removemos nem renomeamos nada, e campos desconhecidos nas respostas devem ser ignorados pelo seu código.

Se uma mudança precisar quebrar a v1, ela sai como /api/v2 e a v1 continua funcionando por pelo menos doze meses. Os proprietários de chaves são avisados por e-mail sobre depreciações com 90 dias de antecedência.

O servidor MCP segue a mesma regra para suas ferramentas: argumentos são apenas adicionados e toda ferramenta mantém seu nome.

O documento OpenAPI em /api/v1/openapi.json e o Markdown em /llms-full.txt são gerados a partir do código que serve a API, então descrevem o que está ativo hoje.

Perguntas que os desenvolvedores fazem

Preciso pagar para usar a API do QRFLOW?

As chaves de API vêm com o plano Business, $29 por mês, mensal. O servidor MCP (Claude, ChatGPT, Cursor, Claude Code) e o OAuth para seu próprio aplicativo funcionam em todos os planos via login, e o que eles podem criar segue o plano. GET /catalog, GET /frames e /preview.svg não precisam de chave alguma.

Posso gerar códigos QR gratuitamente pela API?

Não com uma chave. Para uma imagem estática simples, o gerador gratuito em qrflow.codes ou qualquer biblioteca QR de código aberto resolve. A API é para códigos dinâmicos, seu próprio domínio, análises, em massa e webhooks, que são o que uma conta paga oferece.

Posso chamar a API do navegador?

Não. Ela recusa origens de navegador para que uma chave nunca acabe em uma página web. Chame-a de um manipulador de rota, server action, edge function ou backend, e chame isso da sua página.

Quais formatos de imagem eu recebo?

SVG com suas cores, moldura, legendas e logotipo (GET /codes/:id/image.svg, 256 a 4096 px nominais) e um PNG simples (GET /codes/:id/image.png, 256 a 2048 px). Ambos aceitam um bearer, ou o svg_download_url / png_download_url assinado que todo objeto Code carrega, que funcionam por 24 horas sem cabeçalho. A ferramenta MCP get_qr_image retorna o PNG inline mais ambos os links.

Posso alterar um código QR depois de impresso?

Sim, se for dinâmico (url, phone, email, sms, location em um plano pago). PATCH /codes/:id com um novo destination_data; a imagem não muda, o próximo escaneamento vai para o novo lugar. Códigos Wi-Fi, vCard e texto carregam seu conteúdo na imagem e não podem mudar.

Qual é a diferença entre short_url e o destino?

short_url é o link dentro da imagem (go.example.com/menu). O destino é para onde esse link redireciona (https://example.com/menu-fall). Você imprime short_url uma vez e muda o destino quantas vezes quiser.

Os códigos podem usar meu próprio domínio?

Sim. Premium conecta 1 domínio, Business 5; você adiciona um CNAME e verifica na página Conta. Business escolhe um domínio por código com domain_id. Nomes de link (slug) criam caminhos legíveis nele.

O que um escaneamento registra sobre a pessoa que escaneia?

Tipo de dispositivo, país, cidade, referenciador, navegador, sistema operacional e idioma, da requisição, mais um hash diário de mão única para contagens de visitantes únicos. Sem cookies, e o endereço IP não é armazenado. Suficiente para um gráfico, não suficiente para identificar alguém. Detalhes: https://qrflow.codes/privacy#scans

Existe um pacote npm ou PyPI?

npm: npm install qrflow\ (https://www.npmjs.com/package/qrflow), zero dependências, ESM e CommonJS, tipos TypeScript completos, roda em Node 18+, Bun, Deno e Workers; ele envolve todos os endpoints, repete 429s e inclui verifyWebhook/parseWebhook. Python: um cliente de arquivo único em https://qrflow.codes/sdk/qrflow.py (apenas biblioteca padrão) com verify_webhook; um pacote PyPI virá em seguida.

Funciona com Claude, ChatGPT, Cursor e Claude Code?

Sim. O QRFLOW é um servidor MCP em https://qrflow.codes/mcp. Adicione-o como conector, faça login uma vez e peça em palavras simples. Onze ferramentas cobrem criação, edição, pausa, nomeação, em massa, análises, imagens e domínios.

Meus próprios usuários podem conectar suas contas QRFLOW ao meu aplicativo?

Sim, com OAuth 2.0. Registre um cliente em /api/oauth/register (sem necessidade de conta), envie usuários para /oauth/authorize com PKCE e chame a API com o token deles. Os códigos caem na conta deles sob o plano deles.

Como testo webhooks no localhost?

Exponha seu servidor de desenvolvimento com um túnel (cloudflared ou ngrok) e use o endereço https dele como URL do webhook, depois pressione Test em Conta › Webhooks para receber um ping assinado. URLs de webhook devem ser https públicos; localhost e endereços privados são recusados.

O que acontece com minha integração se eu cancelar o Business?

Chaves e webhooks continuam funcionando por 30 dias e depois respondem 402. Códigos, escaneamentos e domínios permanecem na conta. Reassinar reativa tudo com as mesmas chaves.

Como faço um código de avaliação do Google, Instagram, Wi-Fi ou PDF pela API?

GET /catalog lista todos os tipos com seus campos. Depois POST /codes com o tipo e os campos do tipo, adicionando 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' } }.

A API pode enviar um logotipo para um código?

Ainda não. Adicione o logotipo no painel; image.svg o inclui e has_logo informa que está lá. Cores, molduras e legendas são configuráveis pela API.

A API é estável?

A v1 apenas adiciona; nunca remove ou renomeia. Uma mudança que quebre sairia como v2 com a v1 mantida por pelo menos doze meses e aviso de 90 dias por e-mail.

Para agentes, ferramentas e assistentes que leem isto

Legível por máquina

Tudo nesta página existe em uma forma que o software pode buscar. Tudo é gerado a partir do código que serve a API, então nunca está desatualizado.

URLO que é
llms-full.txthttps://qrflow.codes/llms-full.txtEsta referência completa em Markdown: conceitos, todos os endpoints, todas as ferramentas MCP, receitas, solução de problemas, FAQ. Gerada a partir da mesma fonte desta página.
developers.mdhttps://qrflow.codes/developers.mdO mesmo documento, para ferramentas que buscam um .md.
llms.txthttps://qrflow.codes/llms.txtO índice do site para assistentes, apontando para cá.
A API em uma páginahttps://qrflow.codes/qr-code-apiO que a API faz, quando uma biblioteca do lado do cliente é a melhor resposta, e quanto custa. A versão resumida desta referência, para decidir em vez de construir.
openapi.jsonhttps://qrflow.codes/api/v1/openapi.jsonOpenAPI 3.1. Importe no Postman, Insomnia, um gerador de código ou uma Ação do ChatGPT.
Servidor MCPhttps://qrflow.codes/mcpHTTP transmitível, OAuth com registro dinâmico ou uma chave de negócios como portador.
server.jsonhttps://qrflow.codes/.well-known/mcp/server.jsonO manifesto do registro MCP.
Descoberta OAuthhttps://qrflow.codes/.well-known/oauth-authorization-serverMetadados RFC 8414; o documento de recurso protegido está ao lado.
Pacote npmhttps://www.npmjs.com/package/qrflownpm install qrflow. Cliente tipado, zero dependências, verificação de webhook. Cliente Python de arquivo único em https://qrflow.codes/sdk/qrflow.py.
GitHubhttps://github.com/nativecodeapps/qrflow-sdkClientes, snapshot OpenAPI e exemplos executáveis para webhooks Next.js, Workers, Express, FastAPI e Flask. Issues e PRs são bem-vindos.

Perguntas, ideias, um tipo de código que devemos adicionar: hello@qrflow.codes. Termos: /terms. Privacidade: /privacy.