18. API 集成
Inkica 的 API 让您自己的系统自动向 Inkica 发送订单——无需登录控制台逐个手动创建订单。适用于以下情况:
- 您在自己的网站或 Inkica 尚未预置连接的渠道上销售。
- 您有自己的工具/软件管理订单,并希望直接推送给工厂。
- 您想全面自动化:订单进入 → 创建 Inkica 订单 → 跟踪 tracking。
如果您在 eBay、Etsy、TikTok Shop、Shopify 或 Amazon 上销售,更简单的方式是连接店铺——订单自动进入,无需编程。API 适用于其余情况。
本页面既写给您,也写给负责集成的开发人员——您可以直接把本页链接发给他们。
工作原理概览
- 在 Inkica 控制台创建 API 令牌(只需做一次)。
- 查询目录,获取想要销售的产品的
variant_id(T 恤款式、颜色、尺码)。 - 创建订单——提交收件人地址 + 印刷文件。订单以草稿状态创建,尚未扣款。
- 提交生产——Inkica 从钱包扣款,并将订单转交给工厂。
- 跟踪订单——读取状态,以及订单交给承运商后的 tracking 单号;或注册 webhook,由 Inkica 主动通知。
1. 创建 API 令牌
- 打开账户菜单(侧边栏最底部)→ API。
- 点击 创建令牌,起一个好记的名称(例如 “Website sync”)。
- 立即复制令牌——令牌形如
ink_pat_...,只显示 1 次。关闭对话框后就无法再查看,只能创建新令牌。
🔒 请像保管密码一样保管令牌。 任何拥有令牌的人都可以创建订单并花费您钱包里的钱。不要把令牌放进在浏览器中运行的代码里,不要通过聊天工具发送,不要提交到 GitHub。怀疑泄露时,进入 设置 → API 点击 撤销 立即吊销,该令牌会马上失效。
每个账户同时最多可有 10 个令牌。建议为每个系统/工具各创建 1 个令牌,这样需要吊销其中一个时,其他的仍可继续运行。
2. 调用 API
| Base URL | https://api.inkica.com/api/v1 |
| 身份验证 | 请求头 Authorization: Bearer ink_pat_... |
| 格式 | JSON(Content-Type: application/json),货币为 USD |
| 完整参考文档(可直接试用 API) | api.inkica.com/api/v1/docs |
通过获取产品列表来快速测试令牌:
curl https://api.inkica.com/api/v1/catalog/blueprints?limit=5 \
-H "Authorization: Bearer ink_pat_xxxxxxxxxxxx"如果返回了产品列表,说明令牌可以使用。
3. 选择产品(目录)
目录分为 2 层:
- Blueprint——原始产品模板,例如 “US Unisex Heavy Cotton Tee”。
- Variant——该 blueprint 的某个具体颜色 × 尺码组合,例如 Black / L。创建订单时,您通过
variant_id来选择。
Blueprint 列表
GET /v1/catalog/blueprints?q=tee&page=1&limit=20还可以按 category 筛选(例如 clothing)。每个 blueprint 包含 id、name、product_type、图片以及 print_areas——可印刷的位置。
单个 blueprint 的详情
GET /v1/catalog/blueprints/1{
"id": 1,
"name": "US Unisex Heavy Cotton Tee 2D",
"colors": [{ "name": "Black", "hex": "#000000" }],
"sizes": ["S", "M", "L", "XL", "2XL"],
"included_print_area": "front",
"print_areas": [
{ "position": "front", "fee": 4.2, "width": 38, "height": 58, "unit": "cm" },
{ "position": "back", "fee": 4.2, "width": 38, "height": 58, "unit": "cm" },
{ "position": "sleeve_left", "fee": 3.35, "width": 4, "height": 3.5, "unit": "inch" }
]
}print_areas[].width/height/unit——印刷区域的尺寸,用于按正确比例制作印刷文件。fee——该位置的印刷费。
Variant 列表(含价格)
GET /v1/catalog/blueprints/1/variants?page=1&limit=100{
"data": [
{ "id": 20139, "sku": "IUSG5000-S-ASH", "color": "ASH", "size": "S", "is_available": true, "price": 7.58 }
],
"page": 1, "limit": 100, "total": 821
}价格计算方式: variant 的 price 已包含 1 个印刷位置(即 included_print_area 指定的位置,通常是正面)。如果再印其他位置,需加上该位置的印刷费。以上面的例子为例:只印正面 = $7.58;同时印正面 + 背面 = $7.58 + $4.20 = $11.78。
每一行的准确价格始终以创建订单时返回的
unit_price为准——请以该数字为标准。
只使用 is_available: true 的 variant。大型 blueprint 有数百个 variant——请调大 limit(最大 1000)或翻页 page 来获取全部。
4. 估算运费(非必需)
想在下单前向客户报运费时:
POST /v1/orders/shipping{
"line_items": [{ "variant_id": 20139, "quantity": 2 }],
"address_to": { "country": "US", "region": "CA", "zip": "90001" },
"shipping_method": "standard"
}返回 { "shipping": 7.39, "currency": "USD", "is_estimate": true }。寄往美国的订单必须提供 zip。
如果您自行提供运单,请加上 "shipping_mode": "self"——此时返回的 shipping 是固定处理费(代替运费),is_estimate: false。
5. 创建订单
POST /v1/orders{
"external_id": "WEB-10023",
"shipping_method": "standard",
"address_to": {
"first_name": "John",
"last_name": "Smith",
"email": "john@example.com",
"phone": "+1 555 123 4567",
"country": "US",
"region": "CA",
"address1": "123 Main St",
"address2": "Apt 4",
"city": "Los Angeles",
"zip": "90001"
},
"line_items": [
{
"variant_id": 20139,
"quantity": 2,
"print_areas": {
"front": "https://your-cdn.com/designs/front.png",
"back": "https://your-cdn.com/designs/back.png"
},
"mockups": {
"front": "https://your-cdn.com/mockups/front.jpg"
}
}
]
}| 字段 | 是否必填 | 说明 |
|---|---|---|
external_id | 否(建议提供) | 您自己系统中的订单号,最多 100 个字符。必须与您的其他订单不重复。如果用某个仍为草稿的订单的相同 external_id 再次提交,该草稿会被更新,而不会新建订单——因此当您的系统因网络错误而重发时是安全的。已提交生产的订单,其 external_id 会被锁定(报错 409),即使订单已被拆分为 -F1、-F2 也一样——以确保同一个订单永远不会被创建并付款 2 次。 |
shipping_method | 否 | standard(默认)、priority 或 express——见快速发货。 |
shipping_mode | 否 | inkica(默认——由 Inkica 购买运单并收取运费)或 self(您自行提供运单,只收取固定处理费)——见自行提供运单。 |
shipping_label.label_url | self 时必填 | 运单文件(PDF)的公开链接——工厂下载、打印并贴在包裹上。 |
shipping_label.tracking_number | self 时必填 | 该运单的 tracking 单号,最多 128 个字符。 |
shipping_label.carrier | 否 | 运单所属的承运商,例如 USPS。 |
address_to.first_name | 是 | |
address_to.country | 是 | 2 位国家代码,例如 US。 |
address_to.region | 是 | 州/省代码,例如 CA。对于美国,填写完整州名(California)也可以。 |
address_to.address1 | 是 | 最多 50 个字符——更长的内容请拆到 address2。 |
address_to.city, zip | 是 | |
address_to.email, phone | 否 | |
line_items | 是 | 每个订单最多 50 行。 |
line_items[].variant_id | 是 | 在第 3 步中获取。 |
line_items[].quantity | 是 | 1 – 999。 |
line_items[].print_areas | 是 | 每个位置的印刷文件:{ "位置": "URL" }。位置必须在该 blueprint 的 print_areas 中(第 3 步)。有正面/背面的产品(T 恤、连帽衫……)至少需要这两面中的 1 面。URL 必须能直接下载到原始图片文件(不能是网页,也不能是预览图)——Inkica 会在创建订单时试下载,如果链接失效或图片长边小于 600px,则返回 400 错误。 |
line_items[].mockups | 否 | 每个位置的效果图。留空则直接使用印刷文件。 |
line_items[].personalization | 否 | 个性化文字(例如首饰上的刻字),最多 255 个字符。 |
对印刷文件的要求: URL 必须是公开链接(https://...),无需登录即可直接下载,并且在订单送达之前保持该链接不变——工厂在生产时会从该链接下载文件。文件为透明背景的 PNG,且分辨率须满足第 3 步中印刷区域尺寸的要求。
返回结果是 draft 状态的订单:
{
"id": "4821",
"external_id": "WEB-10023",
"status": "draft",
"line_items": [
{ "id": "9150", "variant_id": 20139, "sku": "IUSG5000-S-ASH", "quantity": 2, "unit_price": 11.78, "print_areas": { "front": "...", "back": "..." } }
],
"subtotal": 23.56,
"shipping": 0,
"total": 23.56,
"currency": "USD",
"shipment": null
}草稿订单尚未扣款,也尚未生产。此时运费始终为
0——运费在提交生产时计算。通过 API 创建的订单也会显示在控制台的 订单 栏目中,与其他订单在同一处。
自行提供运单(self-ship)
已经有运单了(自己购买,或由平台提供)?随订单一起发送——工厂打印您的运单并贴在包裹上,Inkica 不购买运单,也不收取运费,只收取一笔小额的固定处理费(具体金额可通过 POST /v1/orders/shipping 或 dry_run 查看)。
{
"external_id": "WEB-10024",
"shipping_mode": "self",
"shipping_label": {
"label_url": "https://your-cdn.com/labels/WEB-10024.pdf",
"tracking_number": "9400111899223197428490",
"carrier": "USPS"
},
"address_to": { "...": "..." },
"line_items": [ { "...": "..." } ]
}shipping_mode: "self"和shipping_label必须同时提供——只提供其中一个会返回400。- 运单必须寄往订单完全相同的
address_to。Inkica 不读取运单内容:地址错误、已过期或已使用过的运单会导致包裹寄错,责任由您承担。 label_url必须可以直接下载(无需登录),并且在订单送达前保持链接不变,与印刷文件相同。- 仅支持标准发货——与
priority/express一起使用会返回400(送达速度由您的运单决定)。 - 一张运单只能贴在一个包裹上,因此订单中所有产品必须由同一家工厂制作;而且并非每家工厂都接受外部运单。订单不满足条件时,提交生产会返回
400——请先用dry_run检查。此时可拆分为多个订单,或去掉shipping_mode/shipping_label,由 Inkica 购买运单。 - 订单仍是草稿时想改主意:用同一个
external_id重新发送且不带shipping_label→ 订单改回由 Inkica 购买运单,旧运单被丢弃。 - 返回结果包含
"shipping_mode": "self",shipment会立即显示您的运单和 tracking。如果使用 webhook,该订单的order:shipment:created事件会在创建/提交订单后立即到达,无需等待工厂发货。
6. 提交生产
POST /v1/orders/4821/send_to_productionInkica 会计算运费,从钱包扣除总金额,并将订单转交给工厂。钱包余额不足时 API 会报错,订单仍保持草稿状态,请充值后再重新提交。
正式提交前试运行(dry_run)
正在做集成,不想花真钱?请附带 "dry_run": true:
POST /v1/orders/4821/send_to_production{ "dry_run": true }Inkica 会执行与正式提交完全相同的所有检查步骤——地址、印刷文件、是否有货、是否有工厂能印该位置、运费——然后返回将要扣除的金额,但不扣款、不生产任何东西,订单仍是草稿:
{
"dry_run": true,
"orders": [
{ "external_id": "WEB-10023", "subtotal": 23.56, "shipping": 7.39, "total": 30.95 }
],
"total": 30.95,
"currency": "USD",
"wallet_balance": 26.66,
"sufficient_balance": false
}total是此刻立即正式提交时将被扣除的准确金额。sufficient_balance: false→ 钱包余额不足,正式提交会被拒绝——请先充值。- 订单有问题(缺货、印刷位置无法制作……)→ 会收到与正式提交时完全相同的
400错误。
试运行可以执行任意多次。一切正常后,再次提交时不带 dry_run(或带 "dry_run": false)即可正式提交生产。
试运行不会经过提交之后的各个步骤(订单变为
in_production→shipped、产生 tracking)。如需测试状态/tracking 的读取部分,请使用 1 个小额的真实订单——需要时立即取消,款项会退回钱包。
⚠️ 此步骤之后订单会有新的
id。 草稿订单会被正式订单取代,因此旧的id(4821)不能再使用。请保存返回结果中的orders[].id,以便后续跟踪:
{
"orders": [
{ "id": "4822", "external_id": "WEB-10023", "status": "in_production", "shipping": 7.39, "total": 30.95 }
]
}通常 orders 中只有 1 个订单。如果订单中的产品必须在多个不同的工厂制作,订单会被拆分为多个订单——此时 external_id 会加上后缀 -F1、-F2……
不小心丢失了返回结果(例如网络中途断开)?不要重新提交——请按 external_id 查找订单:
GET /v1/orders?external_id=WEB-10023如果订单已被拆分,结果中也会包含 -F1、-F2 等部分。
7. 跟踪订单
GET /v1/orders/4822
GET /v1/orders?status=shipped&page=1&limit=50status | 含义 |
|---|---|
draft | 草稿订单——尚未提交生产,尚未扣款。 |
pending | 已提交,Inkica 正在选择工厂。 |
in_production | 工厂正在生产。 |
on_hold | 暂停——Inkica 需要进一步核查(例如地址有问题)。如果持续时间较长,请联系客服。 |
shipped | 已交给承运商——已有 tracking 单号。 |
delivered | 已送达收件人。 |
cancelled | 已取消。 |
rejected | 被拒绝(例如印刷文件不合格)——请在控制台查看订单或联系客服。 |
split | 订单已按工厂拆分——请在 split_orders 中查看子订单。 |
订单交给承运商后,shipment 字段会包含 tracking 信息,供您发给客户:
"shipment": {
"carrier": "usps",
"tracking_number": "9400111899223456789012",
"tracking_url": "https://tools.usps.com/go/TrackConfirmAction?tLabels=9400111899223456789012",
"label_url": "https://cdn.inkica.com/labels/4822.pdf",
"status": "in_transit",
"delivered_at": null
}label_url 是贴在包裹上的运单文件(PDF)——当工厂自行购买运单并且只回传 tracking 单号时为 null。tracking_url 也可能为 null;此时请在 carrier 对应的网站上按 tracking_number 查询。
对于自行提供运单的订单,shipment 就是您发送的运单和 tracking 单号,从创建订单起即可看到。
想让 Inkica 在订单每次变更状态或有 tracking 时主动通知,而不是让您的系统不停地轮询?请使用 webhook。如果不使用 webhook,建议大约每 1 小时检查一次处于
pending/in_production的订单。
8. 取消订单
POST /v1/orders/4822/cancel只有在工厂尚未开始生产时才能取消。已付款的订单会退回钱包。取消草稿订单则无需退款,因为尚未扣款。超过可取消的时间点后,API 会返回错误——如仍需处理,请联系客服。
9. Webhook——自动接收通知
在您的服务器上注册 1 个地址(URL)——每当有变化时,Inkica 都会自动发送(POST)到该地址,无需不停地轮询。
注册
最快的方式: 打开账户菜单 → API → Webhook 部分 → 添加 Webhook,输入 URL 并选择事件。在这里您还可以看到每个 webhook 的状态(运行中 / 已被停用及原因)、最近一次投递成功还是失败、发送测试 按钮和删除按钮。
或者通过 API 注册:
POST /v1/webhooks{
"url": "https://your-server.com/inkica/webhooks",
"events": ["order:updated", "order:shipment:created"]
}| 事件 | 触发时机 |
|---|---|
order:updated | 订单状态变更(例如 in_production → shipped)。附带 previous_status。 |
order:shipment:created | 订单有了 tracking 单号——可以立即发给客户。 |
events 留空 = 接收全部事件。返回结果中包含形如 whsec_... 的 secret——只显示 1 次,请保存下来用于验证签名(见下文)。
url必须是公开的https://——不接受内部地址(localhost、局域网 IP……)。- 每个账户最多 5 个 webhook,每个 URL 只能注册 1 次。查看列表:
GET /v1/webhooks· 删除:DELETE /v1/webhooks/{id}。 - 立即发送测试:
POST /v1/webhooks/{id}/test——Inkica 会发送 1 个ping事件并返回结果(delivered、status_code、error)。
Inkica 发送的内容
{
"id": "dlv_1042",
"event": "order:updated",
"created_at": "2026-10-17T08:06:10.000Z",
"data": {
"previous_status": "in_production",
"order": { "id": "4822", "external_id": "WEB-10023", "status": "shipped", "shipment": { "tracking_number": "9400111899223456789012", "...": "..." }, "...": "..." }
}
}data.order 的结构与 GET /v1/orders/{id} 完全相同——可以复用现有的订单处理代码。
您的服务器需要在 10 秒内返回 2xx 状态码。建议先保存再立即返回 200,之后再处理。
验证签名(强烈建议)
为确认请求确实来自 Inkica(而非有人冒充),每个请求都带有以下请求头:
| 请求头 | 内容 |
|---|---|
X-Inkica-Event | 事件名称 |
X-Inkica-Delivery | 本次投递的编号(与内容中的 id 相同) |
X-Inkica-Timestamp | 发送时间(Unix 时间戳,单位为秒) |
X-Inkica-Signature | sha256= + 对字符串 {timestamp}.{原始内容} 做 HMAC-SHA256 的结果,密钥为 secret |
请基于原始内容(尚未解析 JSON)重新计算签名并比较:
// Node.js (Express) — 使用 express.raw() 以保留原始内容
const crypto = require('crypto');
app.post('/inkica/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
const ts = req.get('X-Inkica-Timestamp');
const expected = 'sha256=' + crypto.createHmac('sha256', process.env.INKICA_WEBHOOK_SECRET)
.update(`${ts}.${req.body}`).digest('hex');
const got = req.get('X-Inkica-Signature') || '';
const valid = got.length === expected.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected));
if (!valid || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return res.sendStatus(401);
const event = JSON.parse(req.body);
// ... 保存 event,稍后处理
res.sendStatus(200);
});# Python (Flask)
import hmac, hashlib, time
from flask import request, abort
@app.post("/inkica/webhooks")
def inkica_webhook():
ts = request.headers.get("X-Inkica-Timestamp", "")
body = request.get_data() # 原始内容(bytes)
expected = "sha256=" + hmac.new(SECRET.encode(), f"{ts}.".encode() + body, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, request.headers.get("X-Inkica-Signature", "")) or abs(time.time() - int(ts or 0)) > 300:
abort(401)
event = request.get_json()
return "", 200// PHP
$body = file_get_contents('php://input');
$ts = $_SERVER['HTTP_X_INKICA_TIMESTAMP'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $ts . '.' . $body, $secret);
if (!hash_equals($expected, $_SERVER['HTTP_X_INKICA_SIGNATURE'] ?? '') || abs(time() - (int)$ts) > 300) {
http_response_code(401); exit;
}
$event = json_decode($body, true);
http_response_code(200);另外请检查 X-Inkica-Timestamp 与当前时间相差不超过 5 分钟,以防止有人重放旧请求。
出错时重发
- 您的服务器没有返回 2xx(出错、超过 10 秒、无法访问)→ Inkica 会在 1、5、15、60 分钟,然后 3、6、12 小时后自动重发——约 1 天内共 8 次。
- 同一个事件可能会收到不止 1 次,事件也可能乱序到达——请用
id忽略重复项,并始终以最新的data.order.status为准(或调用GET /v1/orders/{id}),不要假定顺序。 - 持续出错时间过长(连续 20 次彻底投递失败)→ webhook 会自动停用(
active: false)。修复服务器后,在 API 页面(账户菜单)点击 发送测试(或调用POST /v1/webhooks/{id}/test)——ping 成功后会重新启用。webhook 停用期间发生的变更不会补发——重新启用后,请调用GET /v1/orders更新您正在跟踪的订单状态。 - 您通过控制台创建的订单或来自平台(eBay、Etsy……)的订单也会被通知——如果只关心通过 API 创建的订单,请按
external_id筛选。
常见错误
出错时,API 会返回 HTTP 状态码,并在 message 中给出说明:
{ "statusCode": 400, "error": "Bad Request", "message": "line_items[0].print_areas: this blueprint has no \"hood\" print position" }| 状态码 | 含义 | 处理方法 |
|---|---|---|
400 | 提交的数据有误/缺失,或无法执行(钱包余额不足、订单已不能取消……) | 阅读 message——对于数据校验错误,message 可能是包含多个错误的列表。修正后重新提交。 |
401 | 缺少令牌、令牌错误或令牌已被吊销 | 检查 Authorization 请求头,必要时创建新令牌。 |
404 | 未找到(id 错误、属于其他账户的订单,或草稿订单的 id 在提交生产后已被替换) | 检查 id,或按 external_id 查找。 |
409 | external_id 已被某个已提交生产的订单使用 | 该订单已存在——请用 GET /v1/orders?external_id=... 重新找到它,而不是新建。如果确实是另一个订单,请使用不同的 external_id。 |
429 | 调用过快 | 等待 1 分钟后再继续调用。 |
限制
- 每分钟 600 次调用。
- 每个订单最多 50 个产品行,每个账户最多 10 个令牌。
- 只能基于 Inkica 目录(按
variant_id)创建订单;在 我的产品 栏目中已创建的产品暂时不能通过 API 直接使用。 - 每个账户最多 5 个 webhook。
- 暂无独立的测试环境——请使用
dry_run在不花钱的情况下检查订单。
◀ 通知 · 目录 · 下一篇:推荐计划 ▶