写真プリント API 連携 — 技術仕様

お取引先各位 / 発行:株式会社弥栄 / 2026年8月4日

クイックスタート

まず認証が通ることだけ確認します。カタログの取得は副作用がないので、何度実行しても安全です。

環境

環境オリジン用途
検証https://stg-api.tokupuri.com実際には印刷されません
本番https://api.tokupuri.com印刷・出荷されます

すべてのパスは /partner/v1/... で始まります。API キーとシークレットは環境ごとに別のものをお渡しします。検証用のキーで本番に通ることはありません。

1分で疎通確認

カタログを取得する
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"

200 が返れば認証は通っています。401 が返る場合はまず時刻を疑ってください — タイムスタンプのずれが ±5分を超えると弾かれます。

連携の全体像

御社システム とくプリ(印刷・出荷) 注文を登録 POST /partner/v1/intakes 写真ごとの署名付き URL を返却 画像をアップロード PUT(S3 互換・直接) 当社のサーバを経由しないため枚数が増えても遅くならない 確定 POST /partner/v1/intakes/{id}/commit 全画像を実測検証 印刷 → 梱包 → 出荷 出荷後 — 通知の受け取り方(どちらでも構いません) GET /partner/v1/events 定期的に問い合わせる 発送通知・追跡番号(新着をまとめて返却) 既定 または Webhook で受け取る(任意)

注文1件あたり、API 呼び出し2回と画像のアップロードだけです。

  1. 注文を登録する

    POST /partner/v1/intakes

    注文内容(組み合わせ・写真・配送先)を送ると、写真ごとのアップロード先 URL が返ります。

  2. 画像をアップロードする

    PUT <返却された署名付き URL> × 枚数分

    S3 互換です。当社のサーバを経由せずストレージへ直接入るため、枚数が増えても遅くなりません。並列アップロード可。

  3. 確定する

    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-KeyAPI キー(公開しても直接の被害はありませんが、公開しないでください)
X-Tokupuri-TimestampUNIX 秒。現在時刻との差が ±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 する」だけです。

API リファレンス

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[].quantity1 以上の整数
印刷する順番photos[].index1 始まりの連番
配送先recipient氏名・郵便番号・住所・電話番号
L判・標準・フチなしをネコポスで送る例
{
  "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種類です。ゆうパック・ゆうメールは対象外です。

配送方法と枚数の上限

選べる配送方法はサイズと合計枚数で決まります。上限を超えると、その配送方法は選べません。

合計プリント枚数の上限。2026年8月4日時点の参考値です(下記の注意をお読みください)。
サイズnekopostakkyubin
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

生産可能な組み合わせと、それぞれで使える配送方法・上限枚数を返します。この結果に無い組み合わせは注文できません。

レスポンス 200
{
  "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進)
レスポンス 201
{
  "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

確定する

POST /partner/v1/intakes/{intake_id}/commit

ボディは不要です。全画像を実測し、登録時に申告された寸法・ハッシュと照合します。

レスポンス 200
{
  "intake_id": "itk_01K3XQ8ZP4M2",
  "external_order_id": "SO-2026-000123",
  "status": "accepted",
  "accepted_at": "2026-08-04T02:41:10Z"
}

1件の状態を問い合わせる

GET /partner/v1/intakes/{intake_id}

特定の注文を調べたいときに使います。

レスポンス 200
{
  "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。省略すると保持期間内の最古から
limit100最大 500
レスポンス 200
{
  "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_moretrue の間は next_cursor を渡して繰り返してください。イベントの保持期間は 30日です。

入稿データの仕様(ケラレ)

当社は画像を一切加工しません。印刷機はフチなし時に用紙の外周を裁ち落とすため、その分を含めた寸法でお送りください。

L判の入稿画像 — 縦位置と横位置 仕上がり 89 × 127 mm 縦位置 入稿 1075 × 1524 px (91 × 129 mm) 仕上がり 127 × 89 mm 横位置 入稿 1524 × 1075 px (129 × 91 mm) けられ 12 px(1 mm)— 四辺それぞれ失われます 仕上がり領域 — 顔・文字・日付はこの内側に収めてください ※ 仕上がりは 1051 × 1500 px 相当(横位置は 1500 × 1051 px)。図はけられを誇張しています(実際は幅の約 1%)
項目確定値
形式JPEG(image/jpeg
色空間sRGB
露光解像度300dpi
ケラレ量四辺それぞれ 1mm(300dpi 換算で 12px)
1ファイル上限30 MB
サイズ別の入稿寸法(縦位置の場合)。いずれも仕上がり寸法に四辺 1mm の塗り足しを加えた値です。
サイズ仕上がり入稿(塗り足し込み)入稿画素
L判89 × 127 mm91 × 129 mm1075 × 1524 px
KG判102 × 152 mm104 × 154 mm1228 × 1819 px
2L判127 × 178 mm129 × 180 mm1524 × 2126 px
ましかく89 × 89 mm91 × 91 mm1075 × 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"

カーソルは処理し終えてから進めてください。受け取った時点で進めると、処理中に落ちたぶんが永久に失われます。再取得で同じイベントが返るのは正常なので、event_id で重複を弾いてください。

Webhook で受け取る(任意)

受信先 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 呼び出しとは署名対象が異なります(当社は御社のパスを知らないため)。

Webhook 署名の検証
# canonical = タイムスタンプ + "\n" + hex(sha256(受信したボディ))
# signature = hex(hmac_sha256(webhook_secret, canonical))
#
# 送られるヘッダ:
#   X-Tokupuri-Timestamp: 1785808500
#   X-Tokupuri-Signature: 3f2a...

配信の挙動

成功条件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"
    }
  ]
}
HTTPcode再送対処
401SIGNATURE_INVALID不可署名対象の文字列を見直す
401TIMESTAMP_SKEWサーバの時刻を合わせる
400VALIDATION_ERROR不可リクエストを修正する
409IDEMPOTENCY_CONFLICT不可同じ ID で内容の違う注文が既にある
409SIZE_PAPER_UNAVAILABLE不可カタログを取り直す
422INTAKE_VALIDATION_FAILED該当画像を差し替えて再度 commit
429RATE_LIMITEDRetry-After 秒だけ待つ
429DAILY_LIMIT_EXCEEDED翌営業日に再送(当日枠の上限)
5xxINTERNAL指数バックオフで再送

冪等性

状況結果
同じ external_order_id・同じ内容で再送200 で既存の intake を返す(二重生産されません)
同じ external_order_id・違う内容で再送409 IDEMPOTENCY_CONFLICT
commit を二度呼ぶ200。状態は変わりません

タイムアウトしたときは、そのまま同じ内容で再送してください。登録されたかどうかを確認してから送り直す必要はありません。

実装チェックリスト

印刷・梱包・出荷・追跡番号の採番は当社側で完結します。御社の作業はこの4点です。

  1. カタログの取得と、注文画面への反映

    取得した組み合わせの中から選ばせる作りにしてください。サイズ・用紙・フチを別々に自由選択させると、生産できない組み合わせが送られてきます。

    サイズ・用紙・フチを、独立した3つの選択肢として持たないでください。組み合わせによっては生産できないものがあり、独立させると成立しない注文が発生します。

  2. 画像の準備とハッシュ計算

    入稿寸法に合わせて仕上げ、ファイルごとに SHA-256 を計算して登録時に添えてください。アップロード後に当社が実測と照合します。

  3. 注文送信(登録 → アップロード → 確定)

    external_order_id を必ず付けてください。タイムアウト時はそのまま再送で構いません。

  4. 発送通知の受け取り

    ポーリングなら GET /events を60秒ごとに呼び、処理し終えてからカーソルを保存するだけです。公開エンドポイントは不要です。

    Webhook なら署名検証・event_id による重複排除・10秒以内の 2xx 応答の3点が必要です。

役割の分担

お客様への販売・決済・一次対応はすべて御社に残ります。当社が御社のお客様と直接やり取りすることはありません。
領域御社当社
販売 担当 対象外
決済・返金 担当 対象外
お客様対応(一次) 担当 対象外
画像の権利処理 担当 対象外
画像のトリミング 担当 対象外
入稿データの規定適合 担当 検証・受理判定
印刷品質 対象外 担当
梱包・出荷・追跡番号 対象外 担当
印刷起因の不良再印刷 対象外 担当

設計時に踏まえていただきたい制約

  1. 1注文 = 1つの組み合わせ = 1配送先 = 1荷物です。複数サイズを含む注文は、注文自体を分けてお送りください。1箱にまとめるかどうかは商品設計の問題として御社側でご判断いただく形になります。
  2. 選べる配送方法は「サイズ × 合計枚数」で変わります。枚数が上限を超えると、その配送方法は選べなくなります。上限値は資材や紙厚の改訂で変動するため、御社の画面に数値を直接書かず、必ずカタログの返り値をお使いください。
  3. 配送ラベルは当社が生成します。API から指定・変更はできません。ここを可変にするとバーコードが欠けて出荷スキャンが通らなくなるためです。
  4. 日次の受入上限があります。超過分は DAILY_LIMIT_EXCEEDED でお返しします。暗黙に翌日へ回すことはしません。繁忙期の見込み枚数は事前に共有をお願いします。

導入前に確認させていただくこと

この3点で、初期スコープと必要な追加開発の有無が決まります。

  1. 納品の形態は、1か所への一括納品ですか。エンドユーザー様への個別配送ですか。 個別配送の場合、注文の粒度と件数が大きく変わるため、流量設計を先に詰める必要があります。
  2. 必要なプリントサイズを教えてください。 現行ラインは L判・KG判・2L判・ましかくに対応しています。これ以外のサイズが必要な場合、生産設備側の確認からになります。
  3. 年間の想定枚数と、繁忙期のピークを教えてください。 日次の受入上限と、当日投入の締め時刻(SLA)をこの数字から設定します。

次のステップ

  1. 本方式についてご合意をいただく
  2. 検証環境の API キーと Webhook シークレットを発行
  3. クイックスタートで疎通確認
  4. 小ロットでの試験印刷を経て本番稼働

確定版は OpenAPI として同じサイトで公開します。仕様の数値は実装コードから生成するため、「ドキュメントには書いてあるが API では弾かれる」という食い違いが起きません。