QRFLOW.codes

官方

生成二维码,重新指向已打印的动态码,在您自己的域名上命名链接,并查看扫描分析。

你可以用 QRFLOW Codes MCP 做什么?

  • 创建动态二维码 — 请求为任何 URL 生成可打印的 short_url,且打印后目的地可随时更改。
  • 重新指向已打印的代码 — 请求将代码的 destination_data 更新为新 URL;已打印的代码无需重新打印即可继续使用。
  • 获取扫描分析 — 请求按天、国家或设备查看 Scans 细分,以了解打印代码的表现。
  • 暂停或使代码过期 — 在活动或优惠结束时,指示暂停代码或设置过期日期。
  • 在您的域名上命名链接 — 请求在您自己的域名上设置可读的 slug,使代码打印为 go.yourbrand.com/menu 而不是随机路径。

托管 MCP 服务器

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

可安装到 Claude Code、Codex、Cursor 等客户端

文档

QRFLOW.codes 面向开发者

从这里开始

QRFLOW.codes 让您可以在打印后更改二维码,使用您自己的域名,提供扫描分析,并让您的软件和 AI 助手通过一个 API 完成所有操作。

二维码是一段字符串的图片。大多数生成器将您的网址放入图片中;如果网址发生变化,印刷品就失效了。QRFLOW 将短链接放入图片中(go.yourbrand.com/menu 或 qrflow.codes/q/x7k2p9a)并进行重定向。您可以随时更改链接指向的位置,每次扫描都会被计数。这就是动态码,也是 API 的价值所在。

API 的功能与仪表盘完全一致:创建任意类型的码、将其指向新位置、为链接命名、选择打印时使用的域名、获取扫描数据、批量创建数千个码,并在事件发生时收到通知(webhooks)。同样的工具也以 MCP 服务器的形式提供,因此 Claude、ChatGPT、Cursor 和 Claude Code 可以用自然语言驱动您的账户。

访问方式:REST API 使用 Business API 密钥(每月 $29,在账户页面创建)或 OAuth 令牌。MCP 服务器通过 OAuth 登录适用于所有套餐;助手可以创建的内容遵循套餐限制,与其他方式一致。

还在犹豫是否使用?二维码 API 页面 是简要版本:它能做什么、客户端库在哪些方面做得更好、以及费用是多少。

如果您是帮助他人使用 QRFLOW 构建的 AI 助手

欢迎。本页面既为您编写,也为您所帮助的人编写。以下是优质协助的标准:

  1. 确认账户:API 密钥需要 Business 套餐。如果他们没有,请引导他们访问 https://qrflow.codes/pricing 和 账户 › API 密钥;不要编造不存在的密钥或免费套餐。
  2. 将密钥保存在服务器端。从环境变量(QRFLOW_KEY)中读取,用于路由处理器、服务器操作、边缘函数或后端。API 有意拒绝浏览器来源(CORS),因此纯前端代码无法调用它。
  3. 使用类型 "url" 和 destination_data.url 创建动态码。将返回的 id 和 short_url 保存到他们的数据库中。打印或显示 short_url;之后可以通过 PATCH /codes/:id 更改目标地址,印刷品仍然有效。
  4. 显示或保存图片:每个 Code 对象都有 png_download_url 和 svg_download_url,这些签名链接 24 小时内有效,无需请求头(适合一次性保存或预览)。如果需要在应用中永久使用,请通过您的服务器使用密钥代理 GET /codes/:id/image.svg,或在客户端使用任意二维码库编码 short_url;跟踪仍然有效,因为链接经过 QRFLOW。
  5. 在 PATCH 时只发送更改的字段。打印后切勿更改 slug 或 domain_id;两者都会改变打印的链接。
  6. 当存在印刷品时,优先使用 paused: true 而不是 DELETE。删除会破坏链接。
  7. 对于事件,使用 webhooks(账户 › Webhooks 或 POST /webhooks),并使用原始请求体验证 X-QRFLOW-Signature 请求头。不要循环轮询 GET /codes。
  8. 如果他们想从聊天中使用 QRFLOW 而不是代码,请在 https://qrflow.codes/mcp; 连接 MCP 服务器,无需密钥。
  9. 当出现故障时,阅读 JSON 响应体中的 error 和 message。下面的故障排除部分将每个错误代码映射到对应的修复方法。
  10. 完整的 Markdown 参考文档位于 https://qrflow.codes/llms-full.txt,OpenAPI 3.1 文档位于 https://qrflow.codes/api/v1/openapi.json.。两者均与本页面同源生成。

人类读者:这个区块是我们确保您配对的助手给您每个问题的安全答案的方式。它恰好也是一个很好的摘要。

您需要哪个套餐

API 密钥是唯一仅限 Business 的功能。助手通过 MCP 服务器所做的一切,以及第三方应用通过 OAuth 所做的一切,都适用于任何套餐,并遵循该套餐的功能限制。完整定价和合理使用政策:/pricing。

免费Premium $4Business $29
API 密钥(从您的代码调用 REST)否否是,最多 10 个密钥
MCP 服务器(Claude、ChatGPT、Cursor、Claude Code)是,登录是是,登录或密钥
为您自己的应用提供 OAuth(用户连接他们的 QRFLOW)是是是
动态码(打印后更改目标地址)否,仅静态是是
您自己的链接域名否1 个域名5 个域名,每个码可选
链接名称(go.brand.com/menu)否是是
扫描分析否是是
Webhooks否否是,最多 10 个
批量创建否每月 500 个,每次请求 500 个每月 10,000 个,每次请求 2,000 个
已保存的码(合理使用)少量1,00025,000
团队席位115
价格$0每月 $4每月 $29

十二个词完成所有工作

核心概念

阅读一次,下面的每个端点就都容易理解了。

Static code

内容在图片内部。Wi-Fi、联系人卡片(vCard)和纯文本码始终是静态的,免费套餐上的所有码也都是静态的。静态码不需要服务器,永不过期,但无法更改或计数。

Dynamic code

图片中包含一个 QRFLOW 重定向的短链接。url、phone、email、sms 和 location 码在 Premium 和 Business 套餐上是动态的。您可以重新指向、暂停、设置过期、重命名和计数,而无需改动印刷品。

short_url

动态码中编码的确切字符串,也是需要打印的内容。在您连接域名之前是 https://qrflow.codes/q/<short_code>,之后是 https://<your domain>/<slug or short_code>.。每个 Code 对象都携带它。

short_code

七个随机字符,每个码唯一,创建时分配,永不更改。当码没有链接名称时的回退路径。

slug (link name)

您自己域名上的可读路径:go.example.com/menu。3 到 40 个小写字母、数字和连字符,在您的账户内唯一,仅适用于已连接的域名。在打印前设置:更改它会改变打印的链接。

Link domain

您拥有的主机名(go.example.com),通过 CNAME 指向 QRFLOW,并在账户页面验证。Premium 获得一个;Business 获得五个,并可以通过 domain_id 为每个码选择。最旧的活跃域名是默认域名。

Kind, type and subtype

type 是编码类型:url、text、wifi、vcard、email、phone、sms、location。kind 是用户需求的友好名称(instagram、googlereview、whatsapp、pdf、menu、appstore 等)。大多数 kind 是设置了 destination_data.subtype 的 url 码。GET /catalog 列出每个 kind 及其字段;Code 上的 kind 告诉您它是哪一种。

destination_data

该类型对应的字段,均为字符串:网站用 { url },Wi-Fi 用 { ssid, password, encryption },Google 评论用 { placeId },Instagram 用 { handle }。在动态码上,您可以随时替换它。

Scans

每次重定向都会记录请求本身的设备类型、国家、城市、来源、浏览器、操作系统和语言,以及用于统计独立访客的单向每日哈希。不设置 Cookie,不存储 IP 地址。Code 上的 scans 是累计总数;GET /codes/:id/scans 提供详细分解。

Source

每个码都记录其创建来源:dashboard、api:<key name>、mcp、canva 或 bulk。它显示在仪表盘和 webhook 负载中,因此您可以区分您的集成创建的码和手动创建的码。

Workspace

Business 所有者可以邀请最多四位队友。密钥和 webhooks 属于所有者账户;工作区中任何人创建的码对整个团队可见。

五分钟

快速开始

获取密钥

  1. 在 Business 套餐 上,打开 账户 › API 密钥。
  2. 为其命名以表明用途("店铺后端"、"报表"),并选择其权限范围。权限范围之后无法更改;如果需要更多权限,请创建新密钥。
  3. 只复制一次。它看起来像 qrf_live_…。将其放入名为 QRFLOW_KEY 的环境变量中。
  4. 在每次请求中将其作为 Authorization: Bearer $QRFLOW_KEY 发送。这就是完整的身份验证说明。

密钥仅供服务器使用。切勿将其放入网页、移动应用或共享电子表格中;如果泄露,请撤销并重新签发。

同样的五个步骤,分别使用 curl、TypeScript 和 Python。每个示例都会创建一个动态码、下载其图片、更改其指向位置并读取其扫描数据。

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

身份验证

API 密钥(Business)

每个账户最多 10 个,每个密钥每分钟 600 次请求,以哈希形式存储,仅显示一次。密钥携带创建时分配的范围:

范围允许的操作
profileGET /me:套餐、功能、限制。每个密钥都有。
codes:read列出和读取码,下载图片。
codes:write创建、更改、设为动态、删除、批量创建。
analytics:readGET /codes/:id/scans。
domains:readGET /domains(合理使用 domain_id 所需)。
webhooks:manage列出、创建、测试和删除 webhooks。

OAuth 2.0(任何套餐,适用于应用和助手)

当码应属于您的用户的账户而非您的账户时,或当聊天助手是客户端时,请使用 OAuth。客户端自行注册;公共客户端必须使用 PKCE S256;令牌可以通过 resource= 绑定到 MCP 服务器。OAuth 指南 提供了详细步骤。

端点URL说明
授权https://qrflow.codes/oauth/authorize将用户发送到此;他们登录并点击允许。
令牌https://qrflow.codes/api/oauth/token支持 authorization_code 和 refresh_token 授权类型。访问令牌有效期 1 小时,刷新令牌有效期 90 天。
撤销https://qrflow.codes/api/oauth/revokeRFC 7009。用户也可以在 账户 › 已连接的应用 中断开连接。
注册客户端https://qrflow.codes/api/oauth/registerRFC 7591 动态注册,无需账户。公共客户端获得 dyn_ client_id,必须使用 PKCE S256。
发现https://qrflow.codes/.well-known/oauth-authorization-serverRFC 8414。MCP 资源文档位于 /.well-known/oauth-protected-resource。

访问令牌有效期 1 小时,刷新令牌有效期 90 天。用户可以在 账户 › 已连接的应用 中查看已连接的应用并随时断开。为 https://qrflow.codes/mcp 铸造的令牌在 /api/v1 上会被拒绝,反之亦然。

人们真正需要的东西

构建示例

每个示例都是完整的,直接取自可运行的代码。选择与您的技术栈匹配的示例;结构始终相同:使用密钥进行服务器端调用,保存 id 和 short_url,显示图片。

Next.js:创建码的路由和显示码的路由

适用场景:您有一个 Next.js 应用(App Router),需要一个创建二维码的按钮和一个显示二维码的页面。

  1. 将您的密钥放入 .env.local 中,变量名为 QRFLOW_KEY。切勿使用 NEXT_PUBLIC_ 前缀。
  2. 添加一个 POST 路由处理器,创建码并返回 id 和 short_url。
  3. 添加一个 GET 路由,代理图片请求,使浏览器永远不会看到密钥。
  4. 将 id 和 short_url 存储在您自己的记录中(订单、餐桌、产品、活动)。
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)的工作方式相同:在操作内部使用密钥调用 fetch。
  • 对于 Pages Router,相同的代码放在 pages/api/qr.ts 中,使用 req/res。

不通过代理显示二维码:自行渲染 short_url

适用场景:您希望图片立即在浏览器中显示,并且不需要 QRFLOW 的边框或徽标。 任何二维码库都可以使用,因为二维码本身就是短链接

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.
  • 这是预览的最快路径。如需打印,请下载 /codes/:id/image.svg:它包含已保存的颜色、边框、标题和徽标,而且是矢量图。
  • 如果之后更改 slug 或 domain_id,short_url 会变化;需要重新渲染。

Express 或任意 Node 服务器

适用场景:普通的 Node 后端。

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

适用场景:仅支持 fetch 的运行时,没有 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_...
  • npm 包 qrflow\ 仅使用 fetch 和 WebCrypto,因此可以在所有这些环境中原样运行。

Python:FastAPI、Flask、Django 或脚本

适用场景:你的后端是 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")

每个订单、餐桌、产品、门票或活动一个二维码

适用场景:表中每一行都需要自动生成自己的二维码。

  1. 在表中添加两列:qrflow_code_id(uuid)和 qr_short_url(text)。
  2. 创建行时,POST /codes,传入该行的公开 URL 和一个标识该行的标签(如“订单 10432”、“餐桌 7”)。保存 id 和 short_url。
  3. 当行的页面移动时(新域名、新路径),PATCH destination_data。已打印的二维码继续有效。
  4. 当行被停用时,如果已有打印物,PATCH { paused: true };只有在没有任何打印物时才 DELETE。
  5. 需要一次生成数千个(比如 300 家餐厅每桌一个菜单)?使用 POST /codes/bulk 分批提交,然后按标签或顺序将返回的代码映射回你的行。
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 });
  • Business 计划的合理使用上限为 25,000 个已保存代码;API 在达到两倍时停止。如果你需要永久为每张收据生成一个代码,请先联系我们:hello@qrflow.codes。

更改已打印代码的跳转目标

适用场景:活动结束、页面迁移、PDF 被替换、季节更替。

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

暂停它,或给它一个结束日期

# 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" }'
  • 只有动态代码可以重新指向。在 Free 账户上创建的 url 代码,或 Wi-Fi/vCard/text 代码,会返回 400 not_dynamic。对于付费计划上的 url/phone/email/sms/location 代码,POST /codes/:id/dynamic 可以转换它,但你必须重新渲染并重新打印,因为图片会变化。
  • 扫描者会在下一次扫描时看到新的目标地址。无需等待缓存过期。

在你自己的域名上打印代码

适用场景:你希望二维码中包含 go.example.com/menu 而不是 qrflow.codes/q/x7k2p9a。

  1. 在账户页面,在“你自己的链接域名”下,添加 go.example.com 并在 DNS 提供商处创建它显示的 CNAME。验证通常几分钟内完成。
  2. 此后,每个新动态代码的 short_url 都使用该域名。现有代码也会切换:它们的图片编码的是 qrflow.codes/q/...,这仍然会重定向,所以已打印的内容不会失效。
  3. 使用 slug 为代码设置可读名称:PATCH { "slug": "menu" } 会生成 go.example.com/menu。请在打印前执行此操作。
  4. 在 Business 计划上使用多个域名时,GET /domains 会列出它们及其 id;在 POST 或 PATCH 时传入 domain_id 可为每个代码选择域名。

命名链接并选择域名

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

在你自己的管理后台中显示扫描图表

适用场景:你希望在自己数据旁边看到每日、每国家或每设备的扫描量。

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
  • 一次请求最多覆盖 92 天;更长时间范围请循环请求。日期为 UTC。
  • 如需实时数据而无需轮询,可将 webhook 订阅到 scan 事件:你会每隔几分钟收到一批包含详细信息的扫描记录。

在 Next.js 中接收 webhook 并验证

适用场景:你希望近乎实时地在自己数据库中知道代码被扫描或更改。

  1. 在“账户 › Webhooks”或通过 POST /webhooks 创建 webhook。将密钥(whsec_...)一次性复制到 QRFLOW_WEBHOOK_SECRET。
  2. 在解析之前将原始请求体作为文本读取;签名覆盖的是精确的字节。
  3. 验证后,再根据事件类型进行分支处理。快速返回 2xx;耗时操作请在响应之后或放入队列中执行。
  4. 在 webhook 上按“测试”以接收带签名的 ping 并确认连接正常。
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 });
}
  • 在本地,使用隧道暴露你的开发服务器(cloudflared tunnel --url http://localhost:3000, 或 ngrok),并在开发期间使用该 https URL 作为 webhook 地址。
  • npm 包会为你处理这些:import { parseWebhook } from "qrflow"\ 一次调用即可完成验证和解析(基于 WebCrypto,因此也适用于 Workers 和 Deno)。Python 客户端提供了 verify_webhook。

在 Python 中接收 webhook

适用场景:Flask、FastAPI 或 Django 接收相同的事件。

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

从 CSV 批量生成数千个代码

适用场景:每个 SKU、座位、资产标签、邮件一个代码。

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");
}
  • 批量接口只生成动态 url 代码,且颜色相同。代码按发送顺序返回,减去被拒绝的行;如有疑问,请按标签匹配。
  • Webhook 会为每个批量请求收到一个 code.created 事件,其中包含 codes[] 而不是每个代码一个事件。

让用户连接他们自己的 QRFLOW 账户(OAuth)

适用场景:你在为他人构建产品,希望代码落入他们的 QRFLOW 账户而不是你的。

  1. 注册一次客户端:POST https://qrflow.codes/api/oauth/register,传入 client_name 和 redirect_uris。你会获得 client_id(机密客户端还会获得 client_secret)。
  2. 将用户引导至 /oauth/authorize,带上 response_type=code、client_id、redirect_uri、scope、state 和 PKCE(code_challenge、code_challenge_method=S256)。
  3. 在 /api/oauth/token 交换代码。保存 refresh token;access token 有效期为一小时。
  4. 调用 /api/v1 时使用 Authorization: Bearer <access_token>。一切与使用密钥完全相同,且受用户计划的约束。
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
  • 作用域与 API 密钥的六个作用域相同。只请求你所需的最小权限;同意页面会列出它们。
  • 如果你的应用是聊天助手或代理,请在授权请求中添加 resource=https://qrflow.codes/mcp 并改用 MCP 服务器;令牌将绑定到该服务器。

Zapier、Make、n8n:完全无需代码

适用场景:你希望扫描记录或新代码落入电子表格、Slack 频道或 CRM。

  1. 创建一个 catch-hook 触发器(Zapier:Webhooks by Zapier › Catch Hook;Make:Custom webhook;n8n:Webhook node)并复制其 https URL。
  2. 在“账户 › Webhooks”中添加该 URL 并选择事件。按“测试”;ping 会出现在工具中并为其提供负载结构。
  3. 将 data.scans[](用于 scan)或 data.code(用于 code.*)映射到你的表格、消息或记录。
  4. 要从这些工具创建代码,请使用它们的 HTTP 模块调用 POST /codes 并带上 Authorization 头。将密钥保存在工具的凭据存储中。
  • 这些工具无法验证签名。它们给你的 URL 是不可猜测的,这就是你拥有的保护;不要将其发布到任何地方。

氛围编程

可粘贴的提示词

编码助手在事先被告知规则时才能构建正确的东西。这些提示词承载了规则。粘贴一个,填写方括号中的内容,助手会在写一行代码之前阅读 Markdown 参考文档。

为我的应用添加二维码

Claude、ChatGPT、Cursor、Codex、Windsurf、Copilot 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"].

让你的代码仓库了解 QRFLOW

放入 CLAUDE.md、AGENTS.md、.cursorrules 或 .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.

在 Lovable、Bolt、v0、Replit 和其他应用构建器中

前端优先的构建器,为你提供后端(Supabase、无服务器函数)

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

一个管理我的代码的自定义 GPT

ChatGPT › 创建 GPT › Actions

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

连接器就绪后要说的话

Claude、ChatGPT、Claude Code(已连接 QRFLOW MCP 服务器)

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

MCP 服务器

从 Claude、ChatGPT、Cursor 和 Claude Code 中使用

QRFLOW 是一个 MCP 服务器,地址为 https://qrflow.codes/mcp。连接一次、登录,然后就可以说诸如*“为我们的秋季菜单页面制作一个二维码,命名为 menu,放在我的域名上”、“把大堂海报的二维码指向新页面”或“上周传单按国家统计有多少次扫描?”*。助手获得与此 API 相同的工具,受相同规则约束,代码会以 mcp 为来源落入你的仪表板。

不是开发者?面向普通语言的版本,包含每个助手的确切点击步骤,请访问 使用你的 AI 助手制作二维码。

连接

Customize › Connectors › Add custom connector › 粘贴地址,或按 QRFLOW 目录列表上的 Connect。Claude 会打开 QRFLOW 登录页面;按 Allow。适用于所有计划。

https://qrflow.codes/mcp

Settings › Security and login › 打开 Developer mode,然后 Settings › Plugins › + › 粘贴地址;按提示登录。适用于 Plus、Pro、Team、Enterprise 和 Edu。

https://qrflow.codes/mcp

一条命令,然后 /mcp 登录。添加 Business 密钥作为请求头可跳过登录。

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、任何 MCP 客户端

在该 URL 添加 HTTP 服务器。OAuth 登录在浏览器中完成;或传入带有密钥的 Authorization 请求头。

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

你自己的代理(Anthropic 或 OpenAI SDK)

将 MCP 连接器或工具指向该 URL,并使用 Business 密钥作为 bearer;无需浏览器流程。

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

助手可以做什么

工具功能作用域
list_code_kinds每种代码类型及其字段和所需计划。助手在创建不常见内容之前会调用此工具。profile
list_domains你的链接域名、默认域名及其 id。domains:read
get_qr_image助手可以显示或保存的 PNG,以及用于打印的 SVG URL。codes:read
get_account当前登录用户、计划、限制。profile

安全性如何保障

  • 助手只持有你的账户的令牌,该令牌是在你在 QRFLOW 页面上按 Allow 后签发的。随时可在“账户 › 已连接的应用”中断开连接。
  • 令牌绑定到 MCP 服务器;无法针对 REST API 重放。
  • 每次写入都经过与仪表板相同的验证:允许列表目标、计划检查、合理使用上限。
  • 破坏性工具会仔细描述自身:delete_qr_code 会告诉模型在存在打印物时优先暂停。
  • 发现文档位于 /.well-known/oauth-authorization-server 和 /.well-known/oauth-protected-resource;注册遵循 RFC 7591;仅支持 PKCE S256。

SDK 和 OpenAPI 规范

  • TypeScript / JavaScript:npm install qrflow(npm)。零依赖,支持 ESM 和 CommonJS,完整类型;可在 Node 18+、Bun、Deno 和 Workers 中运行。自动重试 429 错误,并附带 verifyWebhook / parseWebhook。
  • Python 3.9+,仅标准库:qrflow.py。
  • OpenAPI 3.1:/api/v1/openapi.json。导入 Postman 或 Insomnia,生成任意语言的客户端,或附加到 ChatGPT Action。
  • 两个客户端都会抛出类型化错误(QRFlowError,包含 status、code、message、retryAfter),并附带 webhook 验证器。单文件 TypeScript 源码仍在 /sdk/qrflow.ts,如果你更愿意自行引入。
TypeScriptPython调用
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)图片地址(使用密钥获取)
verifyWebhook(raw, header, secret)verify_webhook(raw, header, secret)投递内容的签名验证

端点参考

Base URL https://qrflow.codes/api/v1。请求体和响应均为 JSON。日期采用 ISO 8601 格式,时区为 UTC。在 PATCH 请求中,仅发送发生变化的字段。

GETprofile

当前账户适用的套餐、功能开关和限制,以及密钥的作用域。

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

原生类型(url、wifi、vcard、email、phone、sms、text、location)以及 50 多种子类型(Instagram、Google 评论、Wi-Fi、应用商店等),每种类型都包含其所需字段和所需套餐。无需密钥。

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

GETcodes:read

最新的排在最前。?limit= 最多 100 条,?q= 可搜索标签。

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

POSTcodes:write

与网站上“保存”的规则相同:付费套餐中,url、phone、email、sms 和 location 类型的码是动态的;Wi-Fi、vCard 和 text 类型的内容则包含在图案中。在 destination_data.subtype 中放入子类型 ID,即可创建例如 Google 评论码。可选的 domain_id 用于选择该码打印时使用你的哪个链接域名(GET /domains 可列出这些域名)。

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

返回该码及其扫描次数、短链接和图片地址。

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

PATCHcodes:write

可修改以下任意字段:destination_data(仅限动态码,已打印的图案仍然有效)、label、paused、expires_at(ISO 格式或 null)、slug(你域名下的链接名称)、domain_id(该码打印时使用你的哪个链接域名;null 表示账户默认)、fg_color、bg_color、frame_style、frame_caption、frame_caption2。仅发送你要更改的字段。

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

永久删除,包括其扫描历史。动态码已打印的链接将停止解析。如果打印的图案仍在流通,建议优先使用 paused: true。

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

POSTcodes:write

打印的图案会发生变化(现在编码的是短链接),因此之后需要重新渲染图片。

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

GETcodes:read

返回带有边框和颜色的、可直接打印的 SVG。?size= 可设置模块网格宽度(像素);文件无论如何缩放都不会失真。

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

GETanalytics:read

?from= 和 ?to=(ISO 日期,最多 92 天,默认为最近 30 天),以及 ?group= 可按 day、device、country、city、browser、os 或 referrer 分组。数字与分析页面相同。

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

一次调用最多可创建 2,000 个 URL 码,全部为动态码。计入与“批量”页面相同的月度批量配额(Business 套餐为 10,000 个)。不是网址的行会出现在 rejected 中;其余行会被创建。

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

你已连接的域名、其状态,以及动态码默认打印时使用的域名(default_base)。在创建码时,将某个域名的 ID 作为 domain_id 传入,即可让该码使用不同的域名进行打印。

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

GETwebhooks:manage

你的 webhook 及其事件、上次状态和失败次数。

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

POSTwebhooks:manage

url 必须是公共主机上的 https 地址;events 可以是 scan、code.created、code.updated、code.deleted 中的任意组合。签名密钥只返回一次。

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

立即发送一个带签名的 ping,并报告响应。

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

DELETEwebhooks:manage

停止所有投递,包括排队的重试。

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

GETpublic

边框 ID 及其各自需要的字段(标题、第二行),按自定义工具的方式分组。无需密钥。

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

Code 对象

每个涉及码的端点都返回相同的结构。忽略你不认识的字段;随着时间推移会添加新字段。

字段类型含义
iduuid稳定 ID。在所有其他调用中使用它。
labelstring | null仪表板中的名称。最多 120 个字符。可通过 ?q= 搜索。
kindstring目录 ID:url、wifi、instagram、googlereview 等。kind_label 是人类可读的名称。
typestring编码类型:url、text、wifi、vcard、email、phone、sms、location。
destination_dataobject你发送的字段(对于某些 kind 还包括 subtype)。destination 是其单行摘要。
dynamicboolean当扫描通过 QRFLOW 进行且目标可以更改时为 true。dynamic_capable 表示该类型在付费套餐上是否可以变为动态。
short_codestring七个字符,永不改变。
short_urlstring动态码要打印的内容。包含你的域名和 slug(如果已设置)。
domain_iduuid | null该码打印时使用的链接域名;null 表示账户默认。
fg_color, bg_colorhex模块和背景颜色。
has_logoboolean已在仪表板中添加了 logo;image.svg 中包含该 logo。
frame_style, frame_caption, frame_caption2string | null边框 ID 和标题,与自定义工具中的一样。
scansinteger累计扫描次数。
created_at, updated_atISO 8601UTC 时间。
manage_urlurl该码在仪表板中的页面,用于“在 QRFLOW 中打开”链接。
image_urlurlGET /codes/:id/image.svg。需要 Authorization 头;不是公开的图片 URL。
svg_download_url, png_download_urlurl相同的 SVG(边框、颜色、logo)和纯 PNG,通过签名链接提供,24 小时内无需请求头即可使用:适用于 <img> 标签、脚本和保存文件的助手。download_expires_at 表示它们何时过期;任何对码的读取都会返回新的链接。

其他结构: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,外加一次性的 secret)、Me(id, email, plan, paid, features, limits, auth, scopes)和 Error(error, message)。OpenAPI 文档中每个属性都有类型定义。

Business

Webhooks

当有事件发生时,QRFLOW 会调用你的 https URL。你可以在 Account › Webhooks 或通过 POST /webhooks 创建一个;签名密钥只会显示一次。每个账户最多 10 个。

事件触发时机data
scan批量:每隔几分钟,发送自上次投递以来的所有新扫描,每次调用最多 500 条。count、from、to、scans[],其中包含 code_id、label、short_code、slug、scanned_at、device、country、city、referrer、browser、os、language
code.created通过 API 和助手立即触发;通过仪表板在几分钟内触发。批量请求会发送一个事件,其中包含 bulk: true 和 codes[]。code、source
code.updated相同的时机。涵盖目标、标签、暂停、过期、链接名称、域名、颜色、边框以及转换为动态码。code、changed[](发生变化的字段名)
code.deleted相同的时机。code: { id、label、short_code、slug }
ping当你按下“测试”时。webhook_id、message

到达的内容

每个负载都是 { id, event, created_at, data }。id 在一次投递的重试之间是稳定的,因此你可以用它来去重。

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

验证签名

X-QRFLOW-Signature: t=<unix seconds>,v1=<hex>。使用你的密钥对 ${t}.${rawBody} 计算 HMAC-SHA256,并与 v1 进行恒定时间比较;如果 t 超过五分钟,则拒绝。使用你收到的原始字节,切勿使用重新序列化的对象。可用的接收器示例见 Next.js 和 Python 的配方,两个 SDK 也都包含该辅助函数。

投递规则

  • 在 8 秒内响应任何 2xx 状态码。在响应后再执行后续工作。
  • 其他任何情况都会在 1、5、15、60、240 和 720 分钟后重试。
  • 连续二十次失败会关闭该 webhook 并通过电子邮件通知账户所有者。修复接收器后重新开启;排队的重试会恢复。
  • URL 必须是公共主机上的 https 地址。localhost、私有网段和 qrflow.codes 本身会被拒绝。开发时请使用隧道。
  • 按下 测试 可收到一个带签名的 ping,并查看你的服务器响应的状态。

错误

每个错误都是 { "error": "<code>", "message": "<what to do>" },并带有以下状态码。消息是写给人类看的;请直接展示。

状态error含义

症状、原因、修复

故障排除

每次调用都返回 401 invalid_token

原因请求头错误或密钥未激活。

修复请准确发送 Authorization: Bearer qrf_live_…(注意是空格,不是冒号)。检查密钥是否在 Account › API keys 中被撤销。如果你是从聊天或文档中复制的,注意末尾可能有多余的句点或智能引号。

使用在 MCP 服务器上有效的令牌调用 /api/v1 返回 401

原因为 https://qrflow.codes/mcp 铸造的令牌绑定到该服务。

修复请使用 API 密钥调用 REST API,或者运行一个不带 resource= 的 OAuth 流程,以获取用于 REST API 的令牌。

创建密钥或 webhook 时返回 402 upgrade_required

原因两者都是 Business 功能。

修复请在 /pricing 升级,或使用 MCP 服务器——它通过登录即可在所有套餐上使用。

从 OAuth 应用调用 POST /codes 返回 402

原因用户的套餐不包含应用请求的功能(动态码、域名)。

修复请先读取 GET /me features 并做出调整:无论如何都创建该码(在 Free 套餐上它将是静态的),或告知用户所需套餐。

403 insufficient_scope

原因创建密钥时作用域就已固定。

修复请使用你所需的作用域创建一个新密钥,并撤销旧密钥。对于 OAuth,请在授权请求中请求该作用域。

当我 PATCH destination_data 时返回 400 not_dynamic

原因该码是静态的:在 Free 套餐上创建的,或者是 Wi-Fi/vCard/text 类型。

修复对于付费套餐上的 url/phone/email/sms/location 类型,请先调用 POST /codes/:id/dynamic,然后重新下载并重新打印(图片会变化)。Wi-Fi、vCard 和 text 类型永远不能是动态的;请创建一个打开页面的 url 码来代替。

当我设置 slug 时返回 400 no_domain

原因链接名称位于你的域名下。

修复请先在 Account 页面连接并验证一个域名。在 qrflow.codes/q 上,路径始终是 short_code。

slug 冲突返回 409

原因你的另一个码已使用该名称。

修复请使用 GET /codes?q= 查找它,或选择另一个名称。名称是按账户区分的,不是全局的。

浏览器控制台出现 CORS 错误

原因API 只接受服务器到服务器的调用(以及 Canva)。这是有意为之:网页中的密钥就是泄露的密钥。

修复请将调用移到路由处理程序、服务器操作、边缘函数或后端中,并从页面调用这些服务。

图片显示 QRFLOW 水印

原因账户处于 Free 套餐。

修复付费套餐会移除水印。Free 套餐是供网站自己的生成器使用的。

添加域名后 short_url 仍显示 qrflow.codes/q/…

原因域名尚未验证,或其 CNAME 配置错误。

修复请检查 Account 页面或 GET /domains 中的状态(状态必须为 verified)。一旦验证通过,现有码会自动切换。

image_url 在 <img> 标签中返回 401

原因它需要 Authorization 头,而 <img> 无法发送。

修复请使用同一 Code 对象中的 svg_download_url 或 png_download_url:这些签名链接在 24 小时内无需请求头即可使用。如需永久链接,请通过你的服务器代理 image_url(参见 Next.js 配方),或自行编码 short_url。

我需要 PNG,而不是 SVG

原因SVG 包含边框和 logo;PNG 是纯码。

修复GET /codes/:id/image.png(使用 bearer 或签名的 png_download_url)返回 PNG,尺寸为 256 到 2048 像素。如需带边框的 PNG,请使用 sharp 或 resvg 转换 SVG(sharp(svgBuffer).png().toBuffer())。

我的助手说图片端点拒绝了它,于是自己画了码

原因它获取了 image_url,而该地址需要 bearer。

修复现在每个码都带有 png_download_url 和 svg_download_url,get_qr_image 会返回它们;助手无需登录即可 curl 这些地址。本地绘制的、编码相同 short_url 的码仍然有效且仍会计入扫描,但它缺少边框和 logo。

Webhook 从未到达

原因URL 规则或接收器问题。

修复URL 必须是公共主机上的 https 地址(不能是 localhost、私有 IP,也不能是 qrflow.codes)。在 webhook 上按下“测试”:结果会显示你的服务器响应的状态。扫描事件是批量发送的,最多可能需要大约五分钟;来自仪表板的 code.* 事件也会排队几分钟,而 API 和 MCP 的写入会立即投递。

签名始终无法验证

原因你对重新序列化的请求体进行了签名。 FixVerify 针对你收到的原始字节进行校验,时间点是在 JSON 解析之前。在 Express 中,在该路由上使用 express.raw({ type: 'application/json' });在 Next.js App Router 中使用 await req.text();在 Flask 中使用 request.get_data()。然后计算 ${t}.${raw}\ 的 HMAC-SHA256。

Webhook 自行关闭

原因连续 20 次失败。

修复修复接收端,然后重新将其设为活动状态(账户 › Webhooks,或删除后重新创建)。发生此情况时你会收到邮件。排队中的重试会恢复。

重复的 Webhook 投递

原因慢速的 2xx 响应(超过 8 秒)会被计为失败并重试。

修复先响应,后处理。根据 payload 的 id 去重,该 id 在重试期间保持稳定。

导入期间出现 429 rate_limited

原因每个密钥每分钟 600 次请求。

修复使用 POST /codes/bulk(一次请求 2000 个代码)而不是每个代码一次 POST,或按 Retry-After 的秒数休眠。

我删除了一个代码,打印的海报现在显示“未找到代码”

原因删除是永久性的,并且会终止链接。

修复没有撤销操作。下次使用 PATCH { paused: true };暂停的代码会显示友好页面,并且可以恢复。

降级后我的密钥停止工作

原因离开 Business 计划后,密钥继续工作 30 天,然后返回 402。

修复重新订阅;没有删除任何内容,相同的密钥会再次生效。

列表中没有你要找的内容?hello@qrflow.codes,附上确切的请求和你收到的 JSON 错误。Business 账户享有优先处理。

限制与合理使用

限制
每个密钥每分钟请求数600。超出后返回 429 并附带 Retry-After。
每个账户的密钥数10
每个账户的 Webhook 数10
已保存的代码(合理使用)Premium 1,000 个,Business 25,000 个;API 在其两倍数量时停止。
批量Premium 每月 500 个(每次请求 500 个);Business 每月 10,000 个(每次请求 2,000 个)。
链接域名Premium 1 个,Business 5 个
扫描次数无限制。Premium 每个代码每月 100,000 次扫描时发送一封签到邮件,Business 为 1,000,000 次;不会进行限流。
扫描分析窗口每次请求 92 天
列表页大小100(?limit=)
标签 / 说明 / 短链接名120 / 60 / 40 个字符
Webhook 超时与重试8 秒;在 1、5、15、60、240 和 720 分钟后重试;连续 20 次失败后关闭。
离开 Business 计划后密钥和 Webhook 继续工作 30 天,然后返回 402。不会删除任何内容。

合理使用是套餐定价所对应的使用范围。达到该数量时不会限流;API 在其两倍数量时停止,并且会有人先通过邮件联系你。更高用量:hello@qrflow.codes。

安全,为你和扫描者

QRFLOW 如何保护你的账户

  • 密钥仅显示一次,并以 SHA-256 哈希形式存储。QRFLOW 的任何人都无法读回密钥;如果你丢失了,请创建新密钥。
  • 每个请求都限定在密钥所属的账户范围内。来自其他账户的代码 id 会返回 404,绝不会泄露。
  • 每个密钥的权限范围是固定的,因此用于报表仪表盘的密钥无法创建或删除代码。
  • 每个密钥每分钟 600 次请求;超出后返回干净的 429,而不是拖慢所有人的速度。
  • 目标地址采用白名单:http、https、mailto、tel、sms、geo 以及少量应用协议(whatsapp、tg、signal、spotify、应用商店)。javascript:、data: 和 file: 在创建时即被拒绝,因此被攻破的集成无法将你的代码变成攻击工具。
  • Webhook URL 必须是公共主机上的 https;QRFLOW 从不调用私有网络或自身。每次投递都有签名,每个 payload 都有稳定的 id。
  • OAuth 客户端使用 PKCE S256 注册,令牌绑定到为其签发的服务器;用于 MCP 服务器的令牌无法在 REST API 上重放。
  • 任何人都可以在账户页面撤销密钥或断开应用;效果是即时的。

你这边需要做什么

  • 使用环境变量,绝不写入源代码。如果密钥进入 git 历史,请撤销它。
  • 仅限服务端使用。API 拒绝浏览器来源,但你自己的包装端点也需要认证,否则任何人都可以用你的账单创建代码。
  • 为每个集成分配独立的密钥,并赋予其所需的权限范围,以集成名称命名。撤销一个密钥只会影响一个集成。
  • 验证 Webhook 签名,并拒绝超过五分钟的时间戳。
  • 如果你的用户数据进入标签或目标地址,请记住 QRFLOW 会存储它们;尽可能不要在标签中放入个人数据。

扫描记录了什么,以及没有记录什么

  • 每次重定向都会存储设备类型、国家、城市、来源、浏览器、操作系统和语言(从请求中获取),以及代码 + 日期 + IP + 用户代理的单向哈希,以便所有者统计独立访客数。该哈希无法还原为地址。
  • 不会在扫描者身上设置 Cookie,IP 地址本身也不会与扫描记录一起保留。已知的机器人和链接预览爬虫会被跳过。
  • 删除代码会删除其扫描记录。删除账户会删除所有内容。
  • 全文:https://qrflow.codes/privacy 和 https://qrflow.codes/terms.

版本控制与稳定性

API 在路径中进行版本控制:/api/v1。在 v1 内,我们会添加字段、端点、类型和事件;我们不会删除或重命名任何内容,你的代码应忽略响应中的未知字段。

如果某个变更必须破坏 v1,它会以 /api/v2 发布,并且 v1 至少继续运行十二个月。密钥所有者会在弃用前 90 天收到邮件通知。

MCP 服务器对其工具遵循相同规则:只添加参数,每个工具都保留其名称。

位于 /api/v1/openapi.json 的 OpenAPI 文档和位于 /llms-full.txt 的 Markdown 文档都是从服务 API 的代码生成的,因此它们描述的是当前实际运行的内容。

开发者常见问题

使用 QRFLOW API 需要付费吗?

API 密钥随 Business 计划提供,每月 29 美元,按月计费。MCP 服务器(Claude、ChatGPT、Cursor、Claude Code)和你自己应用的 OAuth 在所有计划上都可以通过登录使用,它们可以创建的内容遵循计划限制。GET /catalog、GET /frames 和 /preview.svg 完全不需要密钥。

我可以通过 API 免费生成 QR 码吗?

使用密钥不行。对于普通的静态图片,qrflow.codes 的免费生成器或任何开源 QR 库都可以完成。API 用于动态代码、你自己的域名、分析、批量操作和 Webhook,这些是付费账户的用途。

我可以在浏览器中调用 API 吗?

不可以。API 拒绝浏览器来源,因此密钥永远不会出现在网页中。请从路由处理器、服务器操作、边缘函数或后端调用它,然后从你的页面调用这些服务。

我可以获得哪些图片格式?

SVG(包含你的颜色、边框、说明和 Logo,GET /codes/:id/image.svg,标称 256 到 4096 像素)和普通 PNG(GET /codes/:id/image.png,256 到 2048 像素)。两者都需要 Bearer 令牌,或者使用每个 Code 对象携带的签名 svg_download_url / png_download_url,这些链接在 24 小时内无需请求头即可使用。MCP 工具 get_qr_image 返回内联 PNG 以及两个链接。

打印后我可以更改 QR 码吗?

可以,如果它是动态的(付费计划上的 url、phone、email、sms、location)。使用新的 destination_data 调用 PATCH /codes/:id;图片不会改变,下一次扫描会转到新位置。Wi-Fi、vCard 和文本代码的内容包含在图片中,无法更改。

short_url 和 destination 有什么区别?

short_url 是图片中的链接(go.example.com/menu)。destination 是该链接重定向到的位置(https://example.com/menu-fall)。你打印一次 short_url,然后可以随意更改 destination。

代码可以使用我自己的域名吗?

可以。Premium 连接 1 个域名,Business 连接 5 个;你添加 CNAME 并在账户页面验证。Business 可以通过 domain_id 为每个代码选择域名。链接名称(slug)可以在其上创建可读的路径。

扫描会记录扫描者的哪些信息?

设备类型、国家、城市、来源、浏览器、操作系统和语言(来自请求),以及用于独立访客统计的单向每日哈希。不设置 Cookie,也不存储 IP 地址。足以绘制图表,不足以识别任何人。详情:https://qrflow.codes/privacy#scans

有 npm 或 PyPI 包吗?

npm:npm install qrflow\(https://www.npmjs.com/package/qrflow),零依赖,支持 ESM 和 CommonJS,完整的 TypeScript 类型,可在 Node 18+、Bun、Deno 和 Workers 中运行;它封装了每个端点,重试 429,并包含 verifyWebhook/parseWebhook。Python:位于 https://qrflow.codes/sdk/qrflow.py 的单文件客户端(仅标准库),包含 verify_webhook;PyPI 包将随后推出。

它适用于 Claude、ChatGPT、Cursor 和 Claude Code 吗?

适用。QRFLOW 是一个 MCP 服务器,位于 https://qrflow.codes/mcp.。将其添加为连接器,登录一次,然后用自然语言提问。十一个工具涵盖创建、编辑、暂停、命名、批量、分析、图片和域名。

我自己的用户可以将他们的 QRFLOW 账户连接到我的应用吗?

可以,使用 OAuth 2.0。在 /api/oauth/register 注册客户端(无需账户),将用户引导至 /oauth/authorize 并使用 PKCE,然后使用他们的令牌调用 API。代码会落在他们的账户中,并遵循他们的计划。

如何在 localhost 上测试 Webhook?

使用隧道(cloudflared 或 ngrok)暴露你的开发服务器,并将其 https 地址用作 Webhook URL,然后在账户 › Webhooks 中按测试以接收带签名的 ping。Webhook URL 必须是公共 https;localhost 和私有地址会被拒绝。

如果我取消 Business 计划,我的集成会怎样?

密钥和 Webhook 继续工作 30 天,然后返回 402。代码、扫描记录和域名保留在账户中。重新订阅后,一切都会使用相同的密钥恢复。

如何通过 API 创建 Google 评论、Instagram、Wi-Fi 或 PDF 代码?

GET /catalog 列出每种类型及其字段。然后使用该类型的 type 和 fields 调用 POST /codes,并为某些类型添加 subtype:{ type: 'url', destination_data: { subtype: 'googlereview', placeId: 'ChIJ…' } }、{ type: 'wifi', destination_data: { ssid, password, encryption: 'WPA' } }、{ type: 'url', destination_data: { subtype: 'instagram', handle: 'acme' } }。

API 可以将 Logo 上传到代码上吗?

目前还不行。请在仪表盘中添加 Logo;image.svg 会包含它,has_logo 会告诉你它是否存在。颜色、边框和说明都可以通过 API 设置。

API 稳定吗?

v1 只增加内容;从不删除或重命名。破坏性变更会以 v2 发布,v1 至少保持运行十二个月,并提前 90 天通过邮件通知。

面向阅读本文的代理、工具和助手

机器可读

此页面上的所有内容都以软件可获取的形式存在。所有内容都是从服务 API 的代码生成的,因此永远不会过时。

URL这是什么
llms-full.txthttps://qrflow.codes/llms-full.txt整个参考文档的 Markdown 版本:概念、每个端点、每个 MCP 工具、示例、故障排查、FAQ。由本页面的同一来源生成。
developers.mdhttps://qrflow.codes/developers.md同一文档,供获取 .md 文件的工具使用。
llms.txthttps://qrflow.codes/llms.txt面向助手的站点索引,指向此处。
API 单页概览https://qrflow.codes/qr-code-apiAPI 的功能、何时客户端库是更好的选择,以及其成本。本参考文档的简版,用于决策而非构建。
openapi.jsonhttps://qrflow.codes/api/v1/openapi.jsonOpenAPI 3.1。可导入 Postman、Insomnia、代码生成器或 ChatGPT Action。
MCP 服务器https://qrflow.codes/mcpStreamable HTTP,支持动态注册的 OAuth 或作为 bearer 的 Business 密钥。
server.jsonhttps://qrflow.codes/.well-known/mcp/server.jsonMCP 注册表清单。
OAuth 发现https://qrflow.codes/.well-known/oauth-authorization-serverRFC 8414 元数据;受保护资源文档位于其旁边。
npm 包https://www.npmjs.com/package/qrflownpm install qrflow。类型化客户端,零依赖,支持 webhook 验证。Python 单文件客户端位于 https://qrflow.codes/sdk/qrflow.py.
GitHubhttps://github.com/nativecodeapps/qrflow-sdk客户端、OpenAPI 快照以及适用于 Next.js、Workers、Express、FastAPI 和 Flask webhook 的可运行示例。欢迎提交 Issue 和 PR。

问题、想法、我们应添加的某种代码:hello@qrflow.codes。条款:/terms。隐私:/privacy。