あなたのサービスを HANAUTA へつなぐための API です。 経路は3本あり、それぞれ鍵が違います — あなたのサーバーから叩くもの、 利用者の端末から叩くもの、HANAUTA から届くもの。 取り違えると危ないので、口ごとに「誰が叩くか」を貼ってあります。
あなたに書いていただくのは、第2節と第4節だけです。 第3節は撮影画面が自分で叩くもので、実装は要りません。
どの口も同じ土台(https://api.humatch.vows-match.com)にありますが、
認可の材料が違います。API キーを端末へ配らないこと、
撮影のトークンをサーバー間の用途に使わないこと — この2つが守れていれば、
経路は混ざりません。
| 経路 | 鍵 | 叩くのは | できること |
|---|---|---|---|
| API キー | Authorization: Bearer <あなたの API キー> |
あなたのサーバー | セッションを作る/状態を引く/画像を取る |
| セッショントークン | URL に載っている1本(/s/{token}) |
利用者の端末(撮影画面) | 撮影画面を開く/画像を上げる/提出する |
| 署名 | X-Kyc-Signature(HANAUTA が署名する) |
HANAUTA → あなたのサーバー | 結果を届ける |
POST /v1/verification_sessions
Authorization: Bearer <あなたの API キー>
Idempotency-Key: <任意>
{
"assurance_level": "document", // age_only / document / identity_match
"client_reference_id": "user_abc123", // 256文字以内・個人情報は入れない
"minimum_age": 18, // 省略時 18
"expires_in": 900, // 秒。60〜604800(7日)・省略時 900
"metadata": { "plan": "take" } // 任意。10キー・値は256文字以内
}
201 Created
{
"id": "vs_01JQ…",
"object": "verification_session",
"status": "requires_input",
"url": "https://…/s/<トークン>", // これをアプリ内で開く
"expires_at": "2026-09-15T12:15:00.000Z"
}
url に載るトークンは、平文で存在する唯一の場所です。
HANAUTA 側にはハッシュしか残りません。控えておかないと二度と出せません
(そのときは新しいセッションを作り直します)。
401 unauthorized | API キーが無い・不正 |
|---|---|
403 assurance_level_not_allowed | ご契約で許していない水準 |
400 invalid_request | 入力が不正(error.param がどの入力かを指す) |
200 | Idempotency-Key の再送。同じセッションが返るが、URL のトークンは振り直される |
409 idempotency_conflict | 予約だけ残ってセッションが消えている |
expires_in が上限を超えたときは、黙って15分に丸めず
400 を返します。「7日のリンクを送ったつもり」が静かに壊れるのを避けるためです。
200 OK
{
"id": "vs_01JQ…",
"object": "verification_session",
"status": "verified",
"assurance_level": "document",
"minimum_age": 18,
"client_reference_id": "user_abc123",
"created_at": "2026-09-15T12:00:00.000Z",
"expires_at": "2026-09-15T12:15:00.000Z",
"metadata": { "plan": "take" },
"first_opened_at": "2026-09-15T12:01:10.000Z", // 撮影画面を開いた時刻
"submitted_at": "2026-09-15T12:03:42.000Z",
"reviewed_at": "2026-09-15T15:42:00.000Z",
"verified_outputs": ["age_over_18", "document_valid"]
}
requires_input | まだ撮っていない(却下されて撮り直しに戻ったときも、ここへ戻る) |
|---|---|
processing | 出し終わって、人の承認を待っている |
verified | 人が承認した。verified_outputs が付く |
canceled | 撮らないまま期限が切れた |
却下されたときは requires_input に戻り、last_error
に理由(category / code / message)が付きます。
他社のセッションは 404 です
(403 にすると ID が存在することだけ漏れるため)。
front / back / selfie)
200 OK
{
"object": "artifact_download",
"slot": "front",
"url": "https://<取得用のホスト>/…?Expires=…&Signature=…",
"expires_at": "2026-09-15T15:44:00.000Z", // 120秒
"content_type": "image/jpeg"
}
画像は結果に添えて送りません。取りに来てもらいます。 送りつけると、受け取るつもりの無いサーバーにも届いてしまい、 経路上のログにも乗るためです。
403 ip_not_allowed | 許可リストに無い IP(契約ごとに設定。空なら制限なし) |
|---|---|
429 rate_limit_exceeded | 契約ごとのレート制限(既定 60回/分) |
403 output_not_retrievable | 契約で取得を許していない種別 |
410 artifact_expired | 保持期間を過ぎて消えた |
404 not_found | 他社のもの・無いセッション・一度も提出されていない |
呼び出しは成功も失敗も1行ずつ監査ログに残ります(1年)。 記録が書けなければ URL を出しません。
ここはあなたが実装するものではありません。
HANAUTA が返した url をアプリ内の Web 画面で開けば、
以下は撮影画面が自分で叩きます。載せてあるのは、
何が起きているかを把握していただくためです。
API キーは要りません — 認可は URL に載っているトークン1本だけです。
| 期限内で未提出 | 200 撮影画面 |
|---|---|
| 期限切れ | 200「有効期限が切れました」 |
| 提出済み | 200「審査中です」 |
| 審査済み・取り消し済み | 200「この手続きは終了しています」 |
| トークンが不正 | 404(理由を書き分けない) |
「一回限り」は1セッションにつき1本の意味で、 「1回開いたら無効」ではありません。撮り直しもリロードもあるので、 期限内なら何度でも開けます。
{ "slot": "front", "content_length": 123456, "document_type": "residence_card" }
→ 署名付きの PUT 先(5分で切れる)と、付けるべきヘッダが返る
画像はサーバーのプログラムを通りません。 返した URL でブラウザが置き場へ直接送ります。 置き場の鍵と長さ・型は署名に含まれているので、 宣言と違うものを送ると弾かれます。
| 運転免許証 | 表面のみ |
|---|---|
| マイナンバーカード | 表面のみ(裏面は個人番号なので受け付けません) |
| 在留カード | 表面 + 裏面(在留期間の更新履歴があるため) |
| パスポート | 顔写真ページのみ |
この表は画面ではなくサーバーが持っています。2箇所に分けると、
画面には枠が出るのに切符が降りてこないという直しようのない状態になります。
その種別で撮らない面を求めると 403 slot_not_allowed です。
{ "faces": [{ "x": 0.31, "y": 0.22, "w": 0.38, "h": 0.44, "score": 0.98 }] }
位置と大きさは画像に対する比率です。「枠に収まっているか」を決めるのは画面のほうで、 サーバーは事実だけ返します。画像は保存しませんし、この口は保存する権限を持ちません。 顔写真の照合が要る水準のときだけ動きます。
202 Accepted
{ "object": "capture_submission", "status": "processing", … }
提出した時点でトークンは無効になります(撮影画面もアップロードも自動的に閉じます)。
必要な画像が置き場に無ければ 400 documents_missing で、
どの面が足りないかを返します。
判定があなたへ渡る唯一の経路です。
登録していただいた webhook_url へ、HANAUTA から POST します。
POST <登録した webhook_url>
X-Kyc-Signature: t=1757308800,v1=5f2b8c…,v1=9a1d…
X-Kyc-Event-Id: evt_01JQ8F5N2P3Q4R5S6T7U8V
{
"id": "evt_01JQ8F5N2P3Q4R5S6T7U8V",
"type": "verification_session.verified",
"created_at": "2026-09-15T15:42:00.000Z",
"data": { "object": { /* GET で返るものと同じ形 */ } }
}
verification_session.processing | 出し終わって審査待ちになった |
|---|---|
verification_session.verified | 人が承認した |
verification_session.requires_input | 却下された、または HANAUTA 側の不調で撮り直しに戻った |
verification_session.canceled | 撮らないまま期限が切れた |
運用者が Slack で /ekyc-config →「署名鍵を生成」を押すと、10分・1回だけ開ける受け取りリンクが本人にだけ出ます。
鍵の値は Slack を通りません。2本まで並行して持てるので、入れ替えは止めずにできます(導入マニュアルの四)。
署名の対象 "{t}.{受け取った本文の生バイト列}"
計算 HMAC-SHA256(あなたの署名鍵)の16進
ヘッダ X-Kyc-Signature: t=<epoch秒>,v1=<hex>[,v1=<hex>]
v1= が2つ並ぶことがあります。 どちらか一方が一致すれば通してください
— 署名鍵を止めずに入れ替えるための仕掛けですt が5分より古いものは捨ててください(同じ本文の使い回しを防ぐため)X-Kyc-Event-Id は再試行でも変わりません。
これを鍵にして、二度目以降を捨ててください
直後・1分・5分・30分・2時間・6時間・24時間の7回、送り直します。
2xx を返せば成功です。
| 408 / 429 | 送り直す(「今は無理」と読む) |
|---|---|
| それ以外の 4xx | 送り直さない(何度送っても直らないため) |
| 3xx | 送り直さない(署名付きの本文を別のホストへ運ばないため) |
| 5xx・応答なし・タイムアウト | 送り直す(1回あたり10秒で切ります) |
7回使い切ったら諦めます。そのときの回復手段が
GET /v1/verification_sessions/{id} です
— だから両者は同じ形を返すように、1つの部品から作っています。
https: でない届け先へは送りません。
署名は本文が途中で書き換えられていないことしか守らないので、
盗み見られる経路そのものは塞げないためです。
client_reference_id と metadata に個人情報を入れないでください。
氏名・メールアドレス・電話番号・住所のいずれも入れてはいけません。
HANAUTA が「持たない」と決めた個人情報が、参照 ID の形で恒久的に残ってしまいます
(セッションは判定の記録として残り続けます)。
あなたの中でだけ意味を持つ、それ自体では何も分からない識別子をお渡しください。
機械では個人情報かどうかを見分けられないので、HANAUTA は長さで抑えるだけを行います
(client_reference_id は256文字・metadata は10キー×256文字)。
守られる前提で、破ったときの被害の上限だけ切ってあります。
{ "error": { "code": "invalid_request", "message": "…", "param": "expires_in" } }
param は、どの入力が悪いかを指します。
存在しないものと、権限が無いものは書き分けません(どちらも 404)
— 書き分けると、ID が存在することだけが漏れるためです。