发票生成 API
从你自己的服务器以编程方式生成专业 PDF 发票,适用于内部系统、市场平台或 SaaS 产品。
1. 认证#
在 app.invosend.com/settings/api-keys 创建 API key。需要付费工作区套餐和 owner 角色。
每个请求都在 Authorization header 中以 Bearer token 发送 key:
Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx403。免费、暂停或过期的工作区也会被拒绝。2. Endpoint#
Base URL: https://app.invosend.com
机器可读的 API 描述:OpenAPI 3.1 和 Postman collection。可将其中任意一个导入 Postman、Insomnia 或 SDK 生成器。
3. Request Body#
完整请求示例:
curl -X POST https://app.invosend.com/api/invoices/generate-external \
-H "Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"customer_name": "Acme Corp",
"customer_email": "billing@acme.com",
"items": [
{ "description": "Consulting Services", "quantity": 2, "price": 3500, "tax_rate": 11 }
],
"discount_total": 350,
"currency": "CNY",
"notes": "Thank you for your business."
}'定期发票示例(现在开出第一张,之后每月一张,共 6 张):
curl -X POST https://app.invosend.com/api/invoices/generate-external \
-H "Authorization: Bearer isk_live_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{
"customer_name": "Acme Corp",
"customer_email": "billing@acme.com",
"items": [
{ "description": "Monthly retainer", "quantity": 1, "price": 17500 }
],
"currency": "CNY",
"recurring_interval": "monthly",
"recurring_count": 6
}'Idempotency-Key 请求头(1-255 个字符,例如为每张发票生成的 UUID)。如果第一次请求已成功,重试会返回相同的响应并带有 Idempotent-Replayed: true 头,不会再创建发票,也不会再消耗额度(credits_remaining 是第一次调用时的值)。密钥按工作区保留 24 小时。只有成功的响应会被保存,因此失败的请求可以使用同一密钥重试。4. 响应#
200 OK 表示发票已创建且 PDF 已生成:
{
"data": {
"id": "cjld2cjxh0000qzrmn831i7rn",
"invoice_number": "INV-1-202609-0016",
"pdf_url": "https://cdn.invosend.com/company/1/pdf/Invoice_INV_1_202609_0016_UNPAID_0da2beeabb.pdf",
"credits_remaining": 24,
"is_recurring": false,
"template": {
"id": "k3p9x2m7c0000abcd1234efgh",
"name": "Classic",
"source": "system_default"
}
}
}credits_remaining 为 null。id 用于调用下方的附加接口。通过 API 创建的发票也会出现在工作区发票列表中,并使用同一编号序列。 发票编号形如 INV-{工作区 ID}-{YYYYMM}-{序号}。对于定期发票,响应还会包含一个 recurring 对象:schedule_id、interval、occurrences、next_issue_date、end_date。 recurring.schedule_id 是下方定期接口所用的 ID。5. 错误代码#
6. 速率限制#
X-RateLimit-Limit 和 X-RateLimit-Remaining 头。工作区的 API 配额按 Invosend 的会员套餐配置。需要更高的生产用量?请通过你的工作区账户联系我们。 已撤销的密钥不计入密钥数量上限。7. 附加接口#
同一个 API key 也可以读取和管理通过 generate-external 创建的发票:
generate-external 中设置 payment_gateway(也可设置 payment_account),然后把发票的公开付款页面发给客户:https://app.invosend.com/p/invoice/{id},其中 id 为 API 返回的值。客户在该页面通过已连接的网关付款,发票会自动标记为已付。结账由该页面处理,没有需要另外调用的公开结账端点。8. Webhook#
无需轮询:注册一个 HTTPS 地址后,每当发票发生变化,Invosend 都会向该地址发送带有 JSON 正文的 POST 请求。请在 设置 > Webhooks 中添加端点(需要工作区所有者、具备 API 访问权限的套餐,最多 5 个端点)。每个端点都有自己的签名密钥,仅显示一次。
在仪表板中创建的发票和通过此 API 创建的发票都会发送事件。定期发票模板不会发送。
所有事件使用相同的信封结构。data.object 是发票,格式与 GET /api/invoices/external/:id 相同;状态事件还包含 data.previous_status:
{
"id": "evt_9f2c1b7a4e3d5c6b8a1f0e2d",
"object": "event",
"type": "invoice.paid",
"created": "2026-10-01T09:30:12.000Z",
"data": {
"object": {
"id": "cjld2cjxh0000qzrmn831i7rn",
"invoice_number": "INV-1-202610-0007",
"customer_name": "Acme Corp",
"customer_email": "billing@acme.com",
"currency": "USD",
"subtotal": 500,
"discount_total": 0,
"tax_rate": 0,
"tax_amount": 0,
"amount": 500,
"amount_paid": 500,
"amount_credited": 0,
"amount_received": 500,
"invoice_status": "paid",
"is_archived": false,
"is_recurring": false,
"recurring_interval": "none",
"issue_date": "2026-10-01T02:15:40.000Z",
"due_date": "2026-10-15",
"paid_at": "2026-10-01T09:30:11.000Z",
"pdf_url": "https://cdn.invosend.com/company/1/pdf/Invoice_INV_1_202610_0007_PAID.pdf",
"pdf_status": "ready"
},
"previous_status": "unpaid"
}
}每个请求都带有以下请求头。验证方法:使用端点密钥对 {t}.{原始正文} 计算 HMAC-SHA256,并与 v1 比较;如果 t 早于 5 分钟前则拒绝:
POST /your/webhook/url
Content-Type: application/json
Invosend-Event: invoice.paid
Invosend-Delivery: evt_9f2c1b7a4e3d5c6b8a1f0e2d
Invosend-Signature: t=1790847012,v1=5b1d...e9a2
// Node.js
import crypto from "crypto";
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(parts.t + "." + rawBody)
.digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}2xx 状态码。否则最多再重试 7 次(分别在 1 分钟、5 分钟、30 分钟、2 小时、6 小时、12 小时和 24 小时后)。持续失败的端点会被关闭,并通知您。事件可能重复或乱序到达:请用事件 id 去重,并以 invoice_status 为准。投递日志保留 30 天,可在设置 > Webhooks 中重新发送任意事件。9. 限制#
此 API 有意限定在发票生成和轻量管理范围内,以下功能尚不支持:
- Webhook 仅涵盖发票创建、已付款、逾期和已取消。其他变更(如编辑或归档)不会发送:请使用
GET /invoices/external/:id读取当前状态。 - 每次请求创建的发票不会通过邮件发送:此 API 只生成 PDF,将其送达客户由你自己负责(或使用控制台)。例外是定期系列:调度器生成的每张后续发票都会自动发送到
customer_email。 - 发票创建后不可编辑 —— 通过此 API 只能修改状态和归档状态。
- 使用支付网关前必须已在仪表盘中连接该网关;此 API 只是将其关联到发票,不会自动连接网关。
- 定期系列可以通过 API 暂停、恢复、延长、调整频率并跳过后续期次,但其内容(项目、客户、金额)无法编辑,也不能通过 API 删除系列;请在控制台中操作。
常见问题#
如何获取 Invosend API key?
在 app.invosend.com 创建账号,将工作区升级到付费套餐,然后打开 Settings > API Keys 并点击 Generate key。原始 key 只会在创建时显示一次。
每次生成发票 API 调用消耗多少?
每个成功请求会从工作区余额扣除 1 个发票额度,与通过仪表盘创建发票使用同一额度池。
API 的速率限制是多少?
工作区的速率限制取决于你的付费套餐配置。上方“Rate Limit”部分列出了各套餐当前的限制。