お取引先各位 / 発行:株式会社弥栄 / 2026年8月4日
まず認証が通ることだけ確認します。カタログの取得は副作用がないので、何度実行しても安全です。
| 環境 | オリジン | 用途 |
|---|---|---|
| 検証 | https://stg-api.tokupuri.com | 実際には印刷されません |
| 本番 | https://api.tokupuri.com | 印刷・出荷されます |
すべてのパスは /partner/v1/... で始まります。API キーとシークレットは環境ごとに別のものをお渡しします。検証用のキーで本番に通ることはありません。
export TOKUPURI_ORIGIN="https://stg-api.tokupuri.com"
export TOKUPURI_API_KEY="pk_test_..."
export TOKUPURI_API_SECRET="sk_test_..."
REQ_PATH="/partner/v1/catalog"
TS=$(date +%s)
BODY_HASH=$(printf '' | openssl dgst -sha256 -hex | awk '{print $NF}')
CANON=$(printf 'GET\n%s\n%s\n%s' "$REQ_PATH" "$TS" "$BODY_HASH")
SIG=$(printf '%s' "$CANON" | openssl dgst -sha256 -hmac "$TOKUPURI_API_SECRET" -hex | awk '{print $NF}')
curl -sS "$TOKUPURI_ORIGIN$REQ_PATH" \
-H "X-Tokupuri-Key: $TOKUPURI_API_KEY" \
-H "X-Tokupuri-Timestamp: $TS" \
-H "X-Tokupuri-Signature: $SIG"
// npm 不要。Node.js 20 以降の標準機能だけで動きます。
import { createHash, createHmac } from 'node:crypto'
const ORIGIN = process.env.TOKUPURI_ORIGIN ?? 'https://stg-api.tokupuri.com'
const KEY = process.env.TOKUPURI_API_KEY!
const SECRET = process.env.TOKUPURI_API_SECRET!
// query は署名に含めません(パスのみ署名します)
export async function call(method: string, path: string, body?: unknown, query = '') {
// 署名は「実際に送るバイト列」に対して行う。ここで作った payload をそのまま送ること。
const payload = body === undefined ? '' : JSON.stringify(body)
const ts = Math.floor(Date.now() / 1000).toString()
const bodyHash = createHash('sha256').update(payload).digest('hex')
const canonical = [method, path, ts, bodyHash].join('\n')
const signature = createHmac('sha256', SECRET).update(canonical).digest('hex')
const res = await fetch(ORIGIN + path + query, {
method,
headers: {
'Content-Type': 'application/json',
'X-Tokupuri-Key': KEY,
'X-Tokupuri-Timestamp': ts,
'X-Tokupuri-Signature': signature,
},
body: payload === '' ? undefined : payload,
})
const json = await res.json()
if (!res.ok) throw Object.assign(new Error(json.code ?? 'REQUEST_FAILED'), { status: res.status, body: json })
return json
}
console.log(await call('GET', '/partner/v1/catalog'))
# pip 不要。標準ライブラリだけで動きます。
import hashlib, hmac, json, os, time, urllib.request, urllib.error
ORIGIN = os.environ.get("TOKUPURI_ORIGIN", "https://stg-api.tokupuri.com")
KEY = os.environ["TOKUPURI_API_KEY"]
SECRET = os.environ["TOKUPURI_API_SECRET"]
# query は署名に含めません(パスのみ署名します)
def call(method, path, body=None, query=""):
# 署名は「実際に送るバイト列」に対して行う。payload をそのまま送ること。
payload = b"" if body is None else json.dumps(body).encode("utf-8")
ts = str(int(time.time()))
body_hash = hashlib.sha256(payload).hexdigest()
canonical = "\n".join([method, path, ts, body_hash]).encode("utf-8")
sig = hmac.new(SECRET.encode("utf-8"), canonical, hashlib.sha256).hexdigest()
req = urllib.request.Request(
ORIGIN + path + query,
data=payload or None,
method=method,
headers={
"Content-Type": "application/json",
"X-Tokupuri-Key": KEY,
"X-Tokupuri-Timestamp": ts,
"X-Tokupuri-Signature": sig,
},
)
try:
with urllib.request.urlopen(req) as r:
return json.load(r)
except urllib.error.HTTPError as e:
raise RuntimeError(f"{e.code} {e.read().decode('utf-8')}") from None
print(call("GET", "/partner/v1/catalog"))
200 が返れば認証は通っています。401 が返る場合はまず時刻を疑ってください — タイムスタンプのずれが ±5分を超えると弾かれます。
注文1件あたり、API 呼び出し2回と画像のアップロードだけです。
POST /partner/v1/intakes
注文内容(組み合わせ・写真・配送先)を送ると、写真ごとのアップロード先 URL が返ります。
PUT <返却された署名付き URL> × 枚数分
S3 互換です。当社のサーバを経由せずストレージへ直接入るため、枚数が増えても遅くなりません。並列アップロード可。
POST /partner/v1/intakes/{intake_id}/commit
全画像を実測検証し、合格した時点で受注確定です。以降は当社の既存ラインに乗ります。
| status | 意味 | 次にやること |
|---|---|---|
| awaiting_upload | 登録済み。画像待ち | 署名付き URL へ PUT |
| accepted | 検証通過。受注確定 | なし(待つ) |
| rejected | 検証で不合格 | 該当画像を差し替えて再度 commit |
| in_production | 印刷・梱包中 | なし |
| shipped | 出荷済み | Webhook で追跡番号を受領 |
| expired | 期限内に commit されず失効 | 作り直し |
awaiting_upload の有効期限は 24時間です。過ぎると expired になり、署名付き URL も失効します。
API キーと共有シークレットをお渡しします。リクエストごとに HMAC-SHA256 の署名を付けてください。
| ヘッダ | 内容 |
|---|---|
| X-Tokupuri-Key | API キー(公開しても直接の被害はありませんが、公開しないでください) |
| X-Tokupuri-Timestamp | UNIX 秒。現在時刻との差が ±5分を超えると拒否されます |
| X-Tokupuri-Signature | 下記の署名(小文字 16 進) |
canonical = HTTPメソッド + "\n"
+ パス(クエリを含まない) + "\n"
+ タイムスタンプ + "\n"
+ hex(sha256(リクエストボディ))
signature = hex(hmac_sha256(シークレット, canonical))
| 項目 | 規定 |
|---|---|
| メソッド | 大文字(GET / POST) |
| パス | /partner/v1 から始まる全体。クエリ文字列は含めない |
| ボディ | 無い場合は空文字列を sha256 する(e3b0c442... になります) |
| アルゴリズム | HMAC-SHA256、出力は小文字 16 進 |
署名は「実際に送信するバイト列」に対して行ってください。署名用に JSON を作り、送信時に別途シリアライズし直すと、キーの順序や空白の違いでハッシュが変わり 401 になります。ここが最も多い躓きどころです。
| 対象 | 方式 |
|---|---|
| 送信元 IP | ご希望があれば許可リストを設定します。固定 IP をお知らせください(任意) |
| 画像アップロード | 有効期限つきの署名付き URL。長期の認証情報はお渡ししません。書き込み先も当社が指定したパスに限定されます |
| 画像の保管 | 御社専用のバケットに分離します。他社・当社自社サービスの領域とは分かれます |
ストレージの長期鍵をお預けしないため、失効・ローテーション・ご担当者の交代に伴う運用が発生しません。実装の手間は「S3 互換ストレージに PUT する」だけです。
5本だけです。すべて application/json、文字コードは UTF-8。
サイズも配送方法も、すべて 注文を登録するとき(POST /intakes)に指定します。使える値はこれだけです。
| 指定したいもの | フィールド | 指定できる値 |
|---|---|---|
| サイズ | size | L(L判)/ KG(KG判)/ 2L(2L判)/ square(ましかく) |
| 用紙 | paper | standard(標準)/ premium(富士フイルム) |
| フチ | border | false(フチなし)/ true(フチあり) |
| 配送方法 | shipping_method | nekopos(ネコポス)/ takkyubin(ヤマト宅急便) |
| 写真ごとの枚数 | photos[].quantity | 1 以上の整数 |
| 印刷する順番 | photos[].index | 1 始まりの連番 |
| 配送先 | recipient | 氏名・郵便番号・住所・電話番号 |
{
"external_order_id": "SO-2026-000123",
"size": "L",
"paper": "standard",
"border": false,
"shipping_method": "nekopos",
…
}
サイズ・用紙・フチは3つセットで判定します。組み合わせによっては生産できないものがあるため、自由に組み合わせることはできません。生産可能な組み合わせは GET /catalog がすべて返しますので、その中からお選びください。対象外の組み合わせは 409 SIZE_PAPER_UNAVAILABLE でお返しします。
現在お選びいただける配送方法は、ネコポス(nekopos)とヤマト宅急便(takkyubin)の2種類です。ゆうパック・ゆうメールは対象外です。
選べる配送方法はサイズと合計枚数で決まります。上限を超えると、その配送方法は選べません。
| サイズ | nekopos | takkyubin |
|---|---|---|
| L判 | 300 枚まで | 5,000 枚まで |
| ましかく | 300 枚まで | 5,000 枚まで |
| KG判 | 150 枚まで | 5,000 枚まで |
| 2L判 | 150 枚まで | 5,000 枚まで |
この数値をコードに書き込まないでください。梱包仕様は直近3か月で3回改訂されており、2026年9月にもペーパーの紙厚変更にともなう見直しが予定されています。上の表は設計の見当をつけていただくための参考値です。実装では必ず GET /catalog が返す max_quantity をお使いください。そうしていただければ、当社側の変更で御社の改修は発生しません。
GET /partner/v1/catalog
生産可能な組み合わせと、それぞれで使える配送方法・上限枚数を返します。この結果に無い組み合わせは注文できません。
{
"combinations": [
{
"size": "L",
"paper": "standard",
"border": false,
"intake_px": { "short": 1075, "long": 1524 },
"shipping": [
{ "method": "nekopos", "max_quantity": 300 },
{ "method": "takkyubin", "max_quantity": 5000 }
]
}
]
}
上限枚数を御社の画面に直接書かないでください。資材や紙厚の改訂で変動します。この API の値をそのままお使いいただければ、当社側の変更で御社の改修は発生しません。
POST /partner/v1/intakes
{
"external_order_id": "SO-2026-000123",
"size": "L",
"paper": "standard",
"border": false,
"shipping_method": "nekopos",
"recipient": {
"name": "山田 太郎",
"postal_code": "1000001",
"prefecture": "東京都",
"city": "千代田区",
"address_line1": "千代田1-1-1",
"address_line2": "サンプルマンション101",
"phone": "0312345678"
},
"photos": [
{
"index": 1,
"quantity": 2,
"width": 1075,
"height": 1524,
"byte_size": 812345,
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}
]
}
| フィールド | 必須 | 内容 |
|---|---|---|
| external_order_id | 必須 | 御社の注文 ID。冪等キーとして扱います。1〜64文字 |
| size | 必須 | L / KG / 2L / square(ましかく) |
| paper | 必須 | standard / premium |
| border | 必須 | true = フチあり/false = フチなし |
| shipping_method | 必須 | カタログでその variant に許可されているもの |
| recipient.postal_code | 必須 | ハイフン無しの7桁 |
| recipient.phone | 必須 | ハイフン無し。配送業者からの連絡に使います |
| photos[].index | 必須 | 1 始まりの連番。印刷順になります |
| photos[].quantity | 必須 | 同じ写真の焼き増し枚数 |
| photos[].width / height | 必須 | アップロードする画像の画素寸法 |
| photos[].sha256 | 必須 | アップロードするファイルの SHA-256(小文字16進) |
{
"intake_id": "itk_01K3XQ8ZP4M2",
"status": "awaiting_upload",
"expires_at": "2026-08-05T01:22:33Z",
"uploads": [
{
"index": 1,
"method": "PUT",
"url": "https://...署名付き...",
"headers": { "Content-Type": "image/jpeg" },
"expires_at": "2026-08-05T01:22:33Z"
}
]
}
PUT 返却された url
返された headers をそのまま付けて PUT してください。この呼び出しに API キーや署名は不要です(URL 自体が署名されています)。
curl -sS -X PUT "$UPLOAD_URL" \
-H "Content-Type: image/jpeg" \
--data-binary @001.jpg
import { readFile } from 'node:fs/promises'
async function upload(u: { url: string; headers: Record<string, string> }, file: string) {
const res = await fetch(u.url, {
method: 'PUT',
headers: u.headers,
body: await readFile(file),
})
if (!res.ok) throw new Error(`upload failed: ${res.status}`)
}
// 並列度は 8 程度を推奨(多すぎても速くなりません)
const intake = await call('POST', '/partner/v1/intakes', order)
await Promise.all(intake.uploads.map((u) => upload(u, `${u.index}.jpg`)))
await call('POST', `/partner/v1/intakes/${intake.intake_id}/commit`)
import urllib.request
def upload(u, path):
with open(path, "rb") as f:
req = urllib.request.Request(u["url"], data=f.read(), method="PUT", headers=u["headers"])
with urllib.request.urlopen(req) as r:
if r.status not in (200, 201):
raise RuntimeError(f"upload failed: {r.status}")
intake = call("POST", "/partner/v1/intakes", order)
for u in intake["uploads"]:
upload(u, f"{u['index']}.jpg")
call("POST", f"/partner/v1/intakes/{intake['intake_id']}/commit")
POST /partner/v1/intakes/{intake_id}/commit
ボディは不要です。全画像を実測し、登録時に申告された寸法・ハッシュと照合します。
{
"intake_id": "itk_01K3XQ8ZP4M2",
"external_order_id": "SO-2026-000123",
"status": "accepted",
"accepted_at": "2026-08-04T02:41:10Z"
}
GET /partner/v1/intakes/{intake_id}
特定の注文を調べたいときに使います。
{
"intake_id": "itk_01K3XQ8ZP4M2",
"external_order_id": "SO-2026-000123",
"status": "shipped",
"shipping_method": "nekopos",
"tracking_number": "123456789012",
"shipped_at": "2026-08-05T08:15:00Z"
}
継続的な状態監視にこれを使わないでください。注文1件につき1リクエストになり、繁忙期に破綻します。次の /events をお使いください。
GET /partner/v1/events?since={cursor}&limit=100
前回の続きから、新しいイベントだけを返します。注文が何件あっても1リクエストです。
| クエリ | 既定 | 内容 |
|---|---|---|
| since | — | 前回の next_cursor。省略すると保持期間内の最古から |
| limit | 100 | 最大 500 |
{
"events": [
{
"event_id": "evt_01K3XR2A7Q",
"type": "intake.shipped",
"created_at": "2026-08-05T08:15:00Z",
"data": {
"intake_id": "itk_01K3XQ8ZP4M2",
"external_order_id": "SO-2026-000123",
"shipping_method": "nekopos",
"tracking_number": "123456789012"
}
}
],
"next_cursor": "evt_01K3XR2A7Q",
"has_more": false
}
created_at の昇順で返します。has_more が true の間は next_cursor を渡して繰り返してください。イベントの保持期間は 30日です。
当社は画像を一切加工しません。印刷機はフチなし時に用紙の外周を裁ち落とすため、その分を含めた寸法でお送りください。
| 項目 | 確定値 |
|---|---|
| 形式 | JPEG(image/jpeg) |
| 色空間 | sRGB |
| 露光解像度 | 300dpi |
| ケラレ量 | 四辺それぞれ 1mm(300dpi 換算で 12px) |
| 1ファイル上限 | 30 MB |
| サイズ | 仕上がり | 入稿(塗り足し込み) | 入稿画素 |
|---|---|---|---|
| L判 | 89 × 127 mm | 91 × 129 mm | 1075 × 1524 px |
| KG判 | 102 × 152 mm | 104 × 154 mm | 1228 × 1819 px |
| 2L判 | 127 × 178 mm | 129 × 180 mm | 1524 × 2126 px |
| ましかく | 89 × 89 mm | 91 × 91 mm | 1075 × 1075 px |
横位置の写真は縦横を入れ替えてください。例:L判の横位置は 1524 × 1075 px。向きの判定は、画像の物理的な画素寸法で行ってください。
外周 12px は必ず失われます。顔・文字・日付など欠けては困る要素は、この内側に収めてください。サイズによらず同じ値です。
塗り足しは比例ではなく固定 1mm のため、入稿比と仕上がり比は一致しません(0.45〜0.66% の差)。トリミング枠は必ず入稿比で作成し、その内側を仕上がり領域として扱ってください。仕上がり比で切ってから外側へ広げる順序は、写真の端で枠が原本をはみ出して破綻します。
EXIF の Orientation タグは付けずにお送りください。見た目の向きと画素寸法が一致した状態にしてください。回転タグが残っていると機械側で回転され、構図が崩れます。
フチあり指定の場合、ケラレは発生しません(縮小して四辺に余白を作るため)。ただし寸法体系を1つに保つため、上表と同じ寸法でお送りいただいて構いません。
出荷時に追跡番号をお知らせします。受け取り方は2つあり、どちらでも構いません。
| ポーリング | Webhook | |
|---|---|---|
| 位置づけ | 既定(おすすめ) | 任意 |
| 公開エンドポイントの新設 | 不要 | 必要 |
| 署名検証の実装 | 不要 | 必要 |
| 24時間の可用性 | 不要 | 必要 |
| 順序 | 昇順を保証 | 保証なし |
| 即時性 | ポーリング間隔ぶんの遅れ | 即時 |
| 着手までの時間 | 即日 | 社内調整しだい |
まずポーリングで繋いで、あとから Webhook へ移ることができます。返ってくるイベントの形はどちらも同一なので、処理部分は書き換え不要です。受信経路だけ差し替えてください。
GET /partner/v1/events を定期的に呼び、カーソルを進めるだけです。推奨間隔は60秒(最短10秒)。
# 前回の続きから取得する。cursor は自社側で保存しておく。
CURSOR=$(cat ./cursor.txt 2>/dev/null)
REQ_PATH="/partner/v1/events"
# 署名にクエリ文字列は含めません(パスのみ)
TS=$(date +%s)
BODY_HASH=$(printf '' | openssl dgst -sha256 -hex | awk '{print $NF}')
CANON=$(printf 'GET\n%s\n%s\n%s' "$REQ_PATH" "$TS" "$BODY_HASH")
SIG=$(printf '%s' "$CANON" | openssl dgst -sha256 -hmac "$TOKUPURI_API_SECRET" -hex | awk '{print $NF}')
curl -sS "$TOKUPURI_ORIGIN$REQ_PATH?since=$CURSOR&limit=100" \
-H "X-Tokupuri-Key: $TOKUPURI_API_KEY" \
-H "X-Tokupuri-Timestamp: $TS" \
-H "X-Tokupuri-Signature: $SIG"
// cursor は自社の DB などに永続化してください。
async function poll(cursor?: string) {
let next = cursor
for (;;) {
const qs = next ? `?since=${encodeURIComponent(next)}&limit=100` : '?limit=100'
// 署名にクエリ文字列は含めないため、パスだけを渡します
const page = await call('GET', '/partner/v1/events', undefined, qs)
for (const ev of page.events) {
if (await alreadyHandled(ev.event_id)) continue
await handle(ev) // 発送通知の処理
await markHandled(ev.event_id)
}
// ⚠️ 処理し終えてからカーソルを進める
next = page.next_cursor
await saveCursor(next)
if (!page.has_more) return next
}
}
# cursor は自社の DB などに永続化してください。
def poll(cursor=None):
nxt = cursor
while True:
qs = f"?since={nxt}&limit=100" if nxt else "?limit=100"
# 署名にクエリ文字列は含めないため、パスだけを渡します
page = call("GET", "/partner/v1/events", query=qs)
for ev in page["events"]:
if already_handled(ev["event_id"]):
continue
handle(ev) # 発送通知の処理
mark_handled(ev["event_id"])
# ⚠️ 処理し終えてからカーソルを進める
nxt = page["next_cursor"]
save_cursor(nxt)
if not page["has_more"]:
return nxt
カーソルは処理し終えてから進めてください。受け取った時点で進めると、処理中に落ちたぶんが永久に失われます。再取得で同じイベントが返るのは正常なので、event_id で重複を弾いてください。
受信先 URL を事前にお知らせください。イベントの中身はポーリングと同一です。
{
"event_id": "evt_01K3XR2A7Q",
"type": "intake.shipped",
"created_at": "2026-08-05T08:15:00Z",
"data": {
"intake_id": "itk_01K3XQ8ZP4M2",
"external_order_id": "SO-2026-000123",
"shipping_method": "nekopos",
"tracking_number": "123456789012"
}
}
| type | いつ |
|---|---|
| intake.accepted | 検証を通過し受注確定したとき |
| intake.rejected | 検証で不合格になったとき |
| intake.shipped | 出荷し追跡番号が確定したとき |
API 呼び出しとは署名対象が異なります(当社は御社のパスを知らないため)。
# canonical = タイムスタンプ + "\n" + hex(sha256(受信したボディ))
# signature = hex(hmac_sha256(webhook_secret, canonical))
#
# 送られるヘッダ:
# X-Tokupuri-Timestamp: 1785808500
# X-Tokupuri-Signature: 3f2a...
import { createHash, createHmac, timingSafeEqual } from 'node:crypto'
export function verify(rawBody: Buffer, ts: string, sig: string, secret: string) {
// 5分より古いものは再送攻撃として捨てる
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false
const canonical = ts + '\n' + createHash('sha256').update(rawBody).digest('hex')
const expected = createHmac('sha256', secret).update(canonical).digest('hex')
const a = Buffer.from(expected, 'hex')
const b = Buffer.from(sig, 'hex')
return a.length === b.length && timingSafeEqual(a, b)
}
// ⚠️ rawBody は JSON パース前の生バイト列を渡すこと。
// パース後に再シリアライズすると検証は必ず失敗します。
import hashlib, hmac, time
def verify(raw_body: bytes, ts: str, sig: str, secret: str) -> bool:
# 5分より古いものは再送攻撃として捨てる
if abs(time.time() - int(ts)) > 300:
return False
canonical = (ts + "\n" + hashlib.sha256(raw_body).hexdigest()).encode("utf-8")
expected = hmac.new(secret.encode("utf-8"), canonical, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, sig)
# ⚠️ raw_body は JSON パース前の生バイト列を渡すこと。
# パース後に再シリアライズすると検証は必ず失敗します。
| 成功条件 | 2xx を返すこと。ボディは見ません |
|---|---|
| タイムアウト | 10 秒。重い処理は非同期にしてすぐ 2xx を返してください |
| 再送 | 最大 8 回・指数バックオフ・最長 24 時間 |
| 順序 | 保証しません。created_at で判断してください |
| 重複 | 同じイベントが複数回届くことがあります。event_id で冪等に処理してください |
Webhook を採用される場合も、GET /events を保険として実装しておいてください。受信側の停止が24時間を超えると再送は尽きます。そのとき復旧手段が無いと、出荷済みなのに追跡番号が届いていない注文が残ります。
受注応答に追跡番号は含まれません。ネコポスは受注時に番号が確定しますが、宅急便は出荷手配の後に確定するためです。追跡番号は必ず Webhook 側で受け取る作りにしてください。
エラーは共通の形で返します。retryable をそのまま再送判定に使えます。
{
"code": "INTAKE_VALIDATION_FAILED",
"message": "3 枚目の寸法が規定と一致しません",
"retryable": true,
"photos": [
{
"index": 3,
"code": "DIMENSION_MISMATCH",
"expected": "1075x1524",
"actual": "1080x1440"
}
]
}
| HTTP | code | 再送 | 対処 |
|---|---|---|---|
| 401 | SIGNATURE_INVALID | 不可 | 署名対象の文字列を見直す |
| 401 | TIMESTAMP_SKEW | 可 | サーバの時刻を合わせる |
| 400 | VALIDATION_ERROR | 不可 | リクエストを修正する |
| 409 | IDEMPOTENCY_CONFLICT | 不可 | 同じ ID で内容の違う注文が既にある |
| 409 | SIZE_PAPER_UNAVAILABLE | 不可 | カタログを取り直す |
| 422 | INTAKE_VALIDATION_FAILED | 可 | 該当画像を差し替えて再度 commit |
| 429 | RATE_LIMITED | 可 | Retry-After 秒だけ待つ |
| 429 | DAILY_LIMIT_EXCEEDED | 可 | 翌営業日に再送(当日枠の上限) |
| 5xx | INTERNAL | 可 | 指数バックオフで再送 |
| 状況 | 結果 |
|---|---|
同じ external_order_id・同じ内容で再送 | 200 で既存の intake を返す(二重生産されません) |
同じ external_order_id・違う内容で再送 | 409 IDEMPOTENCY_CONFLICT |
| commit を二度呼ぶ | 200。状態は変わりません |
タイムアウトしたときは、そのまま同じ内容で再送してください。登録されたかどうかを確認してから送り直す必要はありません。
印刷・梱包・出荷・追跡番号の採番は当社側で完結します。御社の作業はこの4点です。
取得した組み合わせの中から選ばせる作りにしてください。サイズ・用紙・フチを別々に自由選択させると、生産できない組み合わせが送られてきます。
サイズ・用紙・フチを、独立した3つの選択肢として持たないでください。組み合わせによっては生産できないものがあり、独立させると成立しない注文が発生します。
入稿寸法に合わせて仕上げ、ファイルごとに SHA-256 を計算して登録時に添えてください。アップロード後に当社が実測と照合します。
external_order_id を必ず付けてください。タイムアウト時はそのまま再送で構いません。
ポーリングなら GET /events を60秒ごとに呼び、処理し終えてからカーソルを保存するだけです。公開エンドポイントは不要です。
Webhook なら署名検証・event_id による重複排除・10秒以内の 2xx 応答の3点が必要です。
| 領域 | 御社 | 当社 |
|---|---|---|
| 販売 | 担当 | 対象外 |
| 決済・返金 | 担当 | 対象外 |
| お客様対応(一次) | 担当 | 対象外 |
| 画像の権利処理 | 担当 | 対象外 |
| 画像のトリミング | 担当 | 対象外 |
| 入稿データの規定適合 | 担当 | 検証・受理判定 |
| 印刷品質 | 対象外 | 担当 |
| 梱包・出荷・追跡番号 | 対象外 | 担当 |
| 印刷起因の不良再印刷 | 対象外 | 担当 |
DAILY_LIMIT_EXCEEDED でお返しします。暗黙に翌日へ回すことはしません。繁忙期の見込み枚数は事前に共有をお願いします。この3点で、初期スコープと必要な追加開発の有無が決まります。
確定版は OpenAPI として同じサイトで公開します。仕様の数値は実装コードから生成するため、「ドキュメントには書いてあるが API では弾かれる」という食い違いが起きません。