第16巻 API契約定義(2) ― クロスボーダー送金・照会・継続収納

本巻は docs/specs/32_api_contracts.md のクロスボーダー送金〜OpenAPI仕様書まで(ZC Core APIの残り)を収める。前巻→第15巻の続き、続きは→第17巻。


クロスボーダー送金

POST /api/cross-border/send

クロスボーダー送金開始

Request:

{
  "direction": "OUTBOUND",
  "foreign_fps_id": "SGPAYNOW",
  "foreign_bank_bic": "DBSSSGSG",
  "foreign_account_id": "1234567890",
  "foreign_currency": "SGD",
  "foreign_amount": 100,
  "domestic_amount": 10000,
  "exchange_rate": 100.0,
  "settlement_bank_id": "001",
  "fatf_data": {
    "originator":            { "name": "...", "account_id": "...", "address": "optional" },
    "beneficiary":           { "name": "...", "account_id": "..." },
    "ordering_institution":  { "bank_id": "001", "bank_name": "...", "country": "JP" },
    "beneficiary_institution": { "bank_id": "...", "bank_name": "...", "country": "SG" },
    "is_cross_border": true,
    "fatf16_applicable": true
  },
  "domestic_txid": "TX-..."
}

fatf_data の構造は src/types/primitives.ts#FatfR16Data を正とする(当事者は
{name, account_id, address?, national_id?, date_of_birth?, place_of_birth?} の入れ子。
平坦な originator_name / beneficiary_name は受理しない)。検証は
src/shared/fatf_validator.ts で、不備は水際で FATF_VALIDATION_FAILED / FATF_DATA_REQUIRED。

GET /api/cross-border/:cbTxid

クロスボーダー送金照会

POST /api/cross-border/:cbTxid/callback

外国FPSからのステータス更新

Request: { "status": "SETTLED|FAILED", "foreign_ref": "..." }


照会

GET /api/transactions/:txid

QueryResponse(30_internal_design.md §13.6 準拠)

Response:

{
  "txid": "TX-...",
  "state": "TxState",
  "reason_code": "optional",
  "decision": { "status": "NONE|DECIDED_TO_SETTLE|DECIDED_CANCEL", "decision_proof_ref": "optional" },
  "execution": { "a": "NONE|OK|NG", "b": "NONE|OK|NG", "payer_bank_proof_ref": "optional", "payee_bank_proof_ref": "optional" },
  "case": { "case_id": "optional", "status": "optional" },
  "as_of": "RFC3339",
  "watermark": 12345,
  "watermark_detail": { "shards": { "TX:TX-2026-0001": 12345, "GT:GTID-7": 67890 } },
  "freshness_level": "GREEN|YELLOW|RED",
  "next_action_hint": "WAIT|RETRY_LATER|CONTACT_PAYER_BANK|OPEN_CASE",
  "next_retry_at": "RFC3339 | null",
  "public_message_id": "IGS_HOLD_YYYY-MM-DD (optional)",
  "dns_settlement_status": "HOLD_ACTIVE (optional)",
  "external_settlement": { "status": "IgsStatus", "retriable": true }
}
  • next_action_hint は閉じた 4 値(実装の正: src/types/api/transfers.ts の QueryResponse.next_action_hint:WAIT | RETRY_LATER | CONTACT_PAYER_BANK | OPEN_CASE)。事象固有の含意は reason_code が担い、事象ごとに新しい hint 値を作らない(30_internal_design.md §13.6)。
  • freshness_level の閾値・測定基準および現行実装との差分は 30_internal_design.md §13.6 を正とする。
  • watermark は watermark_detail.shards の最大値、watermark_detail.shards は当該取引に関する事実を
    載せている各チェーンの MAX(event_seq)。キー書式(TX: / GT: / DNS:)と「参加していて未記帳の
    チェーンは 0 を返す」規範は 30_internal_design.md §13.6 を正とする。
  • public_message_id / dns_settlement_status は清算サイクルが HOLD 中のときだけ付与される
    (20_method_design.md §9.4.4.1)。2 つの場合を混同しないこと:
    (A) IGS リングフェンスで止まっている HIGH_VALUE は取引自体が未完了なので public_message_id
    (IGS_HOLD_{business_date})を伴う。
    (B) 通常レーンの取引が既に SETTLED で、行間のネット清算だけが HOLD の場合は
    dns_settlement_status: "HOLD_ACTIVE" を参考情報として付すのみで、取引未完了として表示しては
    ならない
    。
  • external_settlement は中銀決済まで到達した取引にのみ付く(external_settlement_status != NONE)。
    status の値域(実装の正: src/types/states.ts#IgsStatus: REQUESTED | SETTLED | FAILED |
    HOLD | TIMEOUT)。retriable は「待てば進みうるか」で、HOLD / TIMEOUT が true。
    reason_code は HOLD と不成立を区別しないため、窓口の分岐はこのフィールドで行う
    (§状態 reason_code の注意、20_method_design.md §9.4.4.1 (A))。中銀の生の失敗理由は
    相手方の不足を名指しし得るので運ばない(閉域は hold_detail)。
  • 応答には上記のほか、UI 補助フィールド(lane / amount_value / 当事者 ID 等)が付随する。これらは QueryResponse 契約の一部ではなく、契約の対象は上記フィールドのみである。

GET /api/transactions

取引一覧

Query: ?state=...&payer_bank_id=...&payee_bank_id=...&lane=...&limit=50&offset=0

GET /api/transactions/:txid/events

取引イベントログ照会

GET /api/transactions/:txid/explain

人間可読な状態遷移サマリー + 改ざん検知付き。FinalityLog を辿って
イベントごとに日本語の reason / actors を付与し、integrity.chain_verified
で同 TX のハッシュチェーン健全性を返す。

Response(抜粋):

{
  "txid": "TX-...",
  "lane": "EXPRESS",
  "current_state": "SETTLED",
  "summary": "送金は正常に最終確定しました",
  "timeline": [
    { "seq": 1, "at": "...", "event": "PaymentInitiated",
      "state_from": null, "state_to": "RECEIVED",
      "reason": "送金リクエストを受け付けました", "actors": ["ZC"], "payload": {} },
    ...
  ],
  "integrity": {
    "chain_verified": true,
    "entries_checked": 7,
    "break_at_seq": null,
    "break_reason": null,
    "algorithm": "SHA-256 hash-chain v2"
  },
  "proofs": { "decision_proof_ref": "...", "payer_bank_proof_ref": "...",
              "payee_bank_proof_ref": "...", "finality_log_ref": "..." }
}

GET /api/transactions/:txid/story

/explain の構造化データに加えて、ナラティブ段落 + Mermaid sequenceDiagram +
健全性ヴァーディクト(OK | WATCH | STUCK | TERMINAL)を返す。オペレータが
1 件の TX を画面でレビューする用途。

Response(抜粋):

{
  "txid": "TX-...",
  "headline": "[EXPRESS] 001 → 002 の ¥5,000 は最終確定済み",
  "narrative": "09:00:01 JST に 001 から 002 への ¥5,000 の取引(EXPRESS)が動き出しました。...",
  "mermaid_sequence": "sequenceDiagram\n  autonumber\n  ...",
  "pacing": {
    "started_at": "...", "last_event_at": "...", "elapsed_ms": 123,
    "longest_gap": { "from_event": "...", "to_event": "...", "gap_ms": 80 }
  },
  "health": { "status": "TERMINAL", "message": "...", "next_expected": [] },
  "integrity": { "chain_verified": true, "entries_checked": 7 }
}

health.status = STUCK(最後のイベントから 60 秒以上経過し、まだ終端状態に
到達していない)が返ったら運用調査の合図。

GET /api/transactions/:txid/verify

TX チェーンのハッシュチェーン全件検証+副署必須化ポリシーの充足判定。

Response:

{
  "chain_id": "TX-...",
  "valid": true,
  "entries_checked": 7,
  "break_at_seq": null,
  "break_reason": null,
  "algorithm": "SHA-256 hash-chain v2",
  "cosign": {
    "chain_id": "TX-...",
    "chain_kind": "TX",
    "required": true,
    "min_cosigners": 2,
    "cosign_count": 2,
    "satisfied": true,
    "basis_kind": "IRREVERSIBILITY",
    "basis_entry_hash": "…"
  },
  "finality_confirmed": true
}

valid: false のとき break_reason は
LEGACY_UNCHAINED_ENTRY | PREV_HASH_MISMATCH | ENTRY_HASH_MISMATCH のいずれか。
finality_confirmed は ハッシュチェーン健全(valid)かつ副署必須化ポリシー充足
(cosign.satisfied)
のときだけ true。当該チェーン種別(TX/GTID/DNS)に
mandatory ポリシーが設定されていなければ cosign.required=false で常に充足扱い。

規範(副署は「基準エントリ」に対して行う。現 tip に対して行ってはならない)

副署の対象は動かないエントリでなければならない。定足数とは「相異なる k 者が

同一のハッシュに署名した」ことであり、tip は通常の業務追記のたびに動くから、

tip に署名させると 2 人目は別のハッシュに署名することになり、どの単一ハッシュに対する
計数も 1 を超えられない
。すなわち min_cosigners >= 2 の mandatory ポリシーは

原理的に充足不能になる。

したがって基準エントリ(basis entry)を次の優先で決める(resolveCosignBasis,

src/zc/finality/finality_anchor.ts)。

  1. 当該チェーンの不可逆点を記録したエントリ——TX は PAYEE_EXEC_CONFIRMED(b)、
    GTID は GT_SETTLED、DNS はサイクルの SETTLED。
  2. 無ければ、直近アンカーが固定した当該チェーンの tip(FinalityAnchor.chain_tips_json)。
  3. どちらも無ければ COSIGN_BASIS_NOT_FOUND。tip へフォールバックしてはならない
    ——上の欠陥をそのまま呼び戻すためである。

応答の cosign は basis_kind(IRREVERSIBILITY / ANCHOR)と basis_entry_hash を

併せて返す。参加行が何に署名すべきかは、この値が唯一の出所である。

この帰結として finality_confirmed は単調である。 基準エントリは動かないので、

k 者が署名し終えた後にチェーンへ追記があっても cosign_count は 0 に戻らない。

本節はかつて「tip が動けば cosign_count は 0 に戻る。これは実装上の欠落ではなく

意味論の帰結である」と書いていたが、これは誤りであった——「副署は署名した時点までの

履歴を覆う」という意味論は正しく、そこから導かれるのは「署名対象を履歴上の固定点に

取る」ことであって、「毎回 0 に戻る」ことではない。どの固定点を制度上の「外部確定」と

みなすか(不可逆点のエントリか日次アンカーか)は上記 1/2 の優先として本節が定める。

finality_confirmed=false を「ファイナリティが取り消された」と読んではならない。

決済の不可逆性(b)を表すのは取引の state であり、本フィールドは

「外部検証者に提示できる副署が、基準エントリに対して揃っているか」を表す。

GET /api/gtid/:gtid/verify

GET /api/dns/:cycle_id/verify

GTID/DNS チェーン専用の検証。Response 形は /transactions/:txid/verify
と同形(chain_id に gtid/cycle_id が入る)。

POST /api/finality/cosign

当該チェーンの当事者参加行(TX=payer/payee、GTID=leg 銀行、DNS=ネットポジション
銀行)が、チェーンの基準エントリ(上記 /verify の規範。cosign.basis_entry_hash
で取得する)に副署する。署名は KeyRegistry(owner_type='PARTICIPANT')で検証。Body:

{ "chainId": "DNS-2026-06-30", "participantId": "002", "signerKeyId": "KEY-002",
  "nonce": "n-002", "occurredAt": "2026-06-30T07:30:00.000Z", "signatureB64": "..." }

署名対象は {chain_id, entry_hash}(entry_hash = 基準エントリ)。ZC 側でも
resolveCosignBasis で同じ値を解決するため、tip に対する署名は検証に通らない。
失敗時は COSIGN_NOT_APPLICABLE | COSIGN_ENTRY_NOT_FOUND | COSIGN_BASIS_NOT_FOUND | COSIGN_PARTICIPANT_MISMATCH ほか KEY_* / EXTERNAL_SIGNATURE_INVALID /
SIGNATURE_REPLAYED / TIMESTAMP_SKEW。

PUT/GET /internal/cosign-policy/:kind (:kind = TX | GTID | DNS)

副署必須化ポリシーの設定/参照(運用 API、X-Cron-Secret 必須)。
PUT Body: { "min_cosigners": 2, "is_mandatory": true }。mandatory を設定すると、
その種別のチェーンは required 数の相異なる参加行が基準エントリに副署するまで
/verify の finality_confirmed が false になる。

GET /api/events

全体イベントログ(最近N件)

Query: ?limit=100&offset=0

Index: idx_fl_occurred_at。

GET /api/gtid/:gtid

GTID照会

GET /api/gtid

GTID一覧

Query: ?limit=20&offset=0

GET /api/gtid/:gtid/events

GTIDイベントログ照会

GET /api/htlc/:htlc_id

HTLC照会

GET /api/htlc

HTLC一覧

Query: ?limit=50&offset=0

GET /api/dns/:business_date/status

DNS状態照会

→ { "state": "OPEN|KICKED|SETTLED|HOLD_ACTIVE", "igs_mode": "NORMAL|STOP|RINGFENCED|RINGFENCED_PLUS", "cycle_id": "...", "business_date": "YYYY-MM-DD", "public_message_id": "DNS_HOLD_YYYY-MM-DD|null" }

  • state の値域(実装の正: src/types/states.ts#DnsState: OPEN | KICKED | SETTLED | HOLD_ACTIVE)。
  • 当該営業日のサイクルがまだ生成されていない場合のみ { "state": "NOT_STARTED", "business_date": "..." } を 200 で返す(NOT_STARTED は DnsState の値ではなく、「行が無い」ことを 404 ではなく状態として返すための応答専用の特例値。実装 src/zc/query/query.ts#handleGetDnsStatus)。
  • igs_mode の値域(実装の正: src/types/states.ts#IgsMode: NORMAL | STOP | RINGFENCED | RINGFENCED_PLUS)。
  • RECALC_EXCLUDING_BANK は本応答の値ではない(中央銀行が返す清算結果の種別。20_method_design.md §9.4.4 の規範)。
  • public_message_id は HOLD 中のみ非 NULL(DNS_HOLD_{business_date})。参加行はこの ID に
    対応する事前承認テンプレにのみ顧客表示を限定する(10_requirements.md §3.3.1-3/-4)。
    本応答は全参加主体向けであり、原因行・不足額は決して含めない——含まれるのは
    「HOLD である」という公式ステータスと、それに対応するテンプレ ID だけである。

GET /api/dns/:business_date/position

参加行ネットポジション照会

→ { "business_date": "YYYY-MM-DD", "positions": [ { "cycle_id": "DNS-...", "bank_id": "001", "net_position": -1200000, "gross_send": ..., "gross_receive": ..., "is_settled": 0 }, ... ] }
(net_position は +受取/−支払)

フィールド名の正(規範):ネットポジションの項目名は 本節を正とする(net_position / gross_send / gross_receive)。20_method_design.md §9.4.4 は「ネットポジション」「グロス送信/受信」という業務語で規範を述べ、項目名は本節を参照する形に整理してある。

未充足:20_method_design.md §9.4.4 (A) は本照会に as_of / watermark(鮮度)の付与を規範として求めているが、本契約は付与していない。同節の規範は「事務が回るための必須要件」として書かれているため、この差分を充足済みとして扱ってはならない(30_internal_design.md 第10章 Roadmap で追跡)。

GET /api/dns/:business_date/hold_detail

DNS_HOLD の閉域詳細照会。閉域認可を持つ主体(当該の負け参加行・監督当局・中央銀行・ZC運営)
のみ
が呼べる(20_method_design.md §9.4.4 (B) が「事務が回るための必須要件」として規範化)。

リクエストヘッダ:

X-Purpose-Code: P01|P02|P03|P04|P05|P06|P07   # 必須(`10_requirements.md` §3.3.2.2.1)
X-Bank-Id:      001                            # 参加行スコープ(当該の負け参加行のみ)
X-Cron-Secret:  <CRON_SECRET>                  # 運営スコープ(ZC運営・監督当局・中央銀行)
  • 認可が無い呼び出しは 403 ではなく 404 NOT_FOUND を返す。HOLD が発生しているか
    どうか自体が閉域情報であり、403 は「無い」と「見せない」を区別してしまう——
    「あの行は今日ショートしているのか」は取り付けの引き金そのものである。
  • 次の 4 つはすべて同一の 404 本文を返し、応答から HOLD の有無を推測できないようにする:
    目的コード欠落/呼び出し主体不明/当該営業日に HOLD 無し/当事者でない参加行。
  • 目的コードの無い呼び出しは実時間で遮断し、DataAccessViolationDetected を GLOBAL チェーンへ
    記録する
    (10_requirements.md §3.3.2.2.1.1-2。事後監査ではなく遮断が規範)。
  • 認可された読み取りも ClosedDomainAccessGranted として記録する。ZC が提供する中で最も機微な
    読み取り(原因行の特定情報と不足額)であるため、拒否だけでなく許可も監査対象とする。
  • スコープ差: 参加行は自行の不足額のみ、運営スコープはサイクル全体の合計を得る。
  • collateral_call_amount は不足額に復旧リザーブと同じバッファ率
    (DNS_RECOVERY_RESERVE_BUFFER_RATE)を乗じた値。「何を差し入れるべきか」と
    「ZC がこの HOLD に対して見込むリザーブ」を 1 つのバッファ率から導き、二重管理を避ける。
  • 実装: src/zc/query/query.ts#handleGetDnsHoldDetail・src/zc/settlement/dns/query.ts#getDnsHoldDetail、
    認可プリミティブは src/zc/platform/purpose.ts。

Response:

{
  "business_date": "2026-06-30",
  "cycle_id": "DNS-JPY-20260630-01",
  "shortfall_amount": 1200000000,
  "collateral_call_amount": 1500000000,
  "recommended_actions": ["MARKET_FUNDING", "LENDING_REQUEST", "COLLATERAL_PLEDGE"],
  "contact_channel": "...",
  "as_of": "RFC3339"
}

規範: 本エンドポイントの応答は閉域情報であり、GET /api/dns/:business_date/status

(全参加主体向け・公式ステータスと完全一致)には決して含めない

(10_requirements.md §3.2.5.1・20_method_design.md §9.4.4)。

GET /api/boj/positions

各参加行の日銀預け金勘定(BOJ)残高照会(公開API・プリファンド型RTGSの残高モニタ、
プリファンド型RTGSの残高可視化)。
→ { "positions": [ { "bank_id": "001", "boj_balance": 100000000000 }, ... ], "as_of": "RFC3339" }
(同等データの運用内部版は GET /internal/boj-positions。そちらは as_of を付けない)

GET /api/cases/:case_id

CASE照会

POST /api/cases/:case_id/update

CASE状態更新

Request: { "state": "IN_PROGRESS|RESOLVED|ESCALATED" }

  • 値域の正は実装の src/zc/cases/case.ts#CASE_UPDATE_STATES。それ以外は 400 INVALID_STATE。
  • OPEN は受け付けない。CASE が生まれる状態であり、そこへ戻すと「いつ開いたか」が曖昧になる——
    解決後に再び作業が要る事象は、同じ txid に紐づく新しい CASE として起票する
    (b 成立後の救済を別取引で行うのと同じ理由。10_requirements.md §4.3)。

GET /api/system-mode

ZC全体の運用モード照会(縮退モード)
→ { "mode": "NORMAL|BCP_READONLY|QUORUM_LOSS_READONLY", "reason": "...|null", "activated_at": "...|null", "updated_at": "..." }

mode の値域(実装の正: src/types/states.ts#SystemModeValue: NORMAL | BCP_READONLY | QUORUM_LOSS_READONLY)。

縮退モードは2種類。いずれも新規の資金移動(状態確定)を拒否し、照会系は影響を受けない。

  • BCP_READONLY:運用者が宣言するベンダー障害縮退(可搬性と縮退)。bcp-activate/bcp-deactivate で操作。
  • QUORUM_LOSS_READONLY:設計原則10の自動縮退。単一正本性を担保する合意ログが quorum を喪失した際、誤決定を避けるため自動的に read-only へ縮退し、quorum 回復で自動復帰する。運用者トグルでは解除できない(bcp-deactivate は当モード中は SYSTEM_QUORUM_LOSS_READ_ONLY で拒否)。

POST /internal/system-mode/bcp-activate

BCP_READONLY(ベンダー障害縮退モード)へ移行。冪等。

Request: { "reason": "Cloudflare regional outage" }

POST /internal/system-mode/bcp-deactivate

NORMAL へ復帰。冪等。QUORUM_LOSS_READONLY 中は拒否(quorum 回復まで縮退を維持)。

POST /internal/system-mode/quorum-report

合意ログのレプリカ到達性を報告し、ZC のモードを quorum 健全性と整合させる(設計原則10)。
運用の健全性監視がレプリカ集合の到達状況を POST し、ZC は quorum 喪失で QUORUM_LOSS_READONLY へ縮退、回復で NORMAL へ復帰する。運用者宣言の BCP_READONLY は上書きしない。

Request: { "reachable": ["tokyo", "osaka"] }(省略時はメンバ全到達=健全とみなす)
→ { "health": { "total": 3, "reachable": 2, "required": 2, "hasQuorum": true, ... }, "mode": {...}, "action": "DEGRADED|RESTORED|NO_CHANGE" }

メンバ集合は ZC_QUORUM_REPLICAS(カンマ区切り、未設定時は3レプリカ既定)。quorum は厳密過半数(floor(N/2)+1)。

GET /internal/system-mode

現在の運用モード照会(運用ダッシュボード用)。


SSE(Server-Sent Events)

GET /api/sse/events/:bankId

銀行宛リアルタイムイベントストリーム

レスポンス: text/event-stream 形式。EventStreamテーブルから未配信イベントをポーリング。


IGS

POST /api/igs/callback

日銀ネット即時グロス清算のコールバック

Request:

{
  "ext_instruction_id": "...",
  "status": "SETTLED|FAILED",
  "boj_settle_ref": "optional",
  "failed_reason": "optional"
}

先進的アーキテクチャ実験(Advanced Features)

GET /api/stream/connect

Rafiki風 ストリーミング・マイクロ決済 (WebSocket)
接続確立後、{ "type": "START", "gtid": "..." } を送信し、{ "type": "PACKET", "amount": 10 } 等を複数回送信可能。
一定間隔のDO AlarmによってD1にまとめてStateが記録される。

GET /api/als/lookup

Mojaloop風 O(1) エイリアス解決ディレクトリキャッシュ

Query: ?alias=phone:090xxxx
Response: { "bank_id": "001", "account_hash": "...", "pspr_ref": "..." }


Reversal(救済取引)

SETTLED 済み TX に対する苦情・誤送金・二重送金などへの補償フロー。
起票可否の判定は 10_requirements.md §4.3.0(Reversal の三層ゲート)、API と reason 区分は同 §4.3.1、データ構造は 31_schema.md § ReversalRecords。

POST /api/reversals

補償取引を起票する。一部の reason は approval_ref 必須(社内統制の事前
承認チケット番号など)。受理されると lane=STANDARD, purpose=REFUND の
新規 TX が生成される。

Request:

{
  "original_txid": "TX-...",
  "amount": 5000,
  "reason": "ReversalReason",
  "requested_by": "001 (bank_id) | OPS",
  "approval_ref": "string (required when reason ∈ APPROVAL_REQUIRED_REASONS)",
  "description": "optional"
}

reason の値域(実装の正: src/zc/cases/reversal.ts#ReversalReason: CUSTOMER_DISPUTE |
DUPLICATE_PAYMENT | INCORRECT_AMOUNT | INCORRECT_PAYEE | FRAUD | OPERATIONAL_ERROR)。
意味は 10_requirements.md §4.3.1 を正とする。値をここで散文的に例示しない——かつて本節は
WRONG_RECIPIENT という実在しない値を例に挙げ続けており(10_requirements.md 側では既に
除去済みだった)、宣言の書式に寄せることで CI の値域照合の対象になる。
Response 201:

{
  "result": "REVERSAL_CREATED",
  "reversal_id": "REV-...",
  "reversal_txid": "TX-...",
  "status": "TX_CREATED"
}

Response 422 の reason_code:

  • CREDIT_FAILED_PROOF_REQUIRED — 第1層未充足。POST /api/transfers/:txid/credit-failed-proof
    が未提出。この場合、要求は握り潰さず CASE として受理し、応答に case_id を返す
    (10_requirements.md §4.3.0-1「満たさない顧客異議は Reversal ではなく CASE として受理し、
    当事者間の解決へ接続する」)。顧客異議は実在する事象であって、それ自体は巻き戻しの根拠ではない。
  • APPROVAL_REF_REQUIRED — 第2層(内部統制版)未充足。APPROVAL_REQUIRED_REASONS に該当する
    reason に approval_ref が無い。
  • ORIGINAL_NOT_FOUND / ORIGINAL_NOT_SETTLED / INVALID_REVERSAL_AMOUNT / OVER_REVERSAL。

検査順(規範): 金額の妥当性・累計超過は第1層ゲートより前に評価する。逆順にすると、

単なる入力ミスのたびに CASE が起票され、誰かがそれを閉じる仕事が増える。ゲートは AND なので

順序は結果を変えないが、運用負荷は変える。

GET /api/reversals/:reversal_id

特定 Reversal の状態と関連 TX を返す。

GET /api/transactions/:txid/reversals

ある TX に紐づく Reversal 一覧。


Circuit Breaker(参加行疎通監視)

ZC→Bank 呼び出しの連続失敗に対するブレーカー。状態は CLOSED → OPEN → HALF_OPEN → CLOSED。
詳細は 31_schema.md § CircuitBreakerState / 10_requirements.md(制度・ガバナンス要件)。

GET /api/circuit-breaker

全行のサーキットブレーカー状態とメトリクスを一覧。

Response:

{ "circuit_breakers": [
  { "bank_id": "001", "state": "CLOSED", "consecutive_failures": 0,
    "total_requests": 1234, "total_successes": 1200, "total_failures": 34,
    "total_denied": 0, "half_open_inflight": 0,
    "last_success_at": "...", "last_failure_at": "..." },
  ...
] }

GET /api/circuit-breaker/:bank_id

特定行のサーキットブレーカー状態。未登録(メトリクスがまだ無い)行は
state: CLOSED の初期値が返る(404 ではない)。

POST /api/circuit-breaker/:bank_id/reset

強制 CLOSED へリセット(運用オペレーション)。

Response: { "result": "RESET", "bank_id": "..." }


継続収納(口座振替)

制度要件は 10_requirements.md §3.2.8、処理方式は 20_method_design.md §2.2.7、スキーマは 31_schema.md § ZC テーブル(継続収納)。

呼び出し主体は常に参加行である。 受取人(収納事業者)は ZC の参加者ではなく受取行の顧客であり、収納の起票も結果の受領も受取行を経由する(10_requirements.md §3.2.8.8-4)。ZC が受取人と直接やり取りする経路は設けない。

POST /api/debit-mandates

継続収納契約の登録。顧客が buildMandatePayload の正規ペイロードに署名し、KeyRegistry で検証する(registerMandate と同じ経路)。

Request:

{
  "payer_bank_id": "002",
  "payer_account_alias": "tel:+81-90-...",
  "payee_bank_id": "001",
  "payee_account_hash": "h:...",
  "product_ref": "◯◯ゴールドカード ****1234",
  "charge_mode": "PERIODIC",
  "period_cycle": "MONTHLY",
  "collection_mode": "SCHEDULED",
  "notice_days_min": 14,
  "amend_freeze_hours": 33,
  "ladder_max": 3,
  "caps": {
    "per_collection": 30000, "month_amount": 30000, "month_count": 2,
    "two_month_amount": 50000, "lifetime_amount": null, "lifetime_count": null,
    "pending_amount": 60000, "pending_count": 4,
    "latefee_month": 500, "latefee_rate_max": 0.146,
    "realtime_month_count": 0, "variance_ratio_max": 1.5
  },
  "eligibility_attestation": { "...": "受取行が署名した適格性証明" },
  "mandate": { "principal_key_id": "KEY-...", "nonce": "...", "occurred_at": "RFC3339",
               "signature": "base64", "valid_from": "RFC3339", "valid_to": "RFC3339" },
  "idempotency_key": "string"
}

Response: { "result": "REGISTERED", "dd_mandate_id": "DDM-...", "mandate_id": "MANDATE-...", "effective_collection_mode": "SCHEDULED", "demoted": false }

  • effective_collection_mode は払出行のプロファイルから導出する。 要求モードを提供できない行(Tier 1 等)では自動降格し、demoted: true と demotion_reason を返す(10_requirements.md §3.2.8.2-4)。黙って挙動を変えない。
  • 認可期限は必須ではない。 無期限は valid_to の番兵値で表現する(10_requirements.md §3.2.8.1-4)。
  • 宣言した上限が制度上限(PR-DD-*)を超える場合は 422 CAP_EXCEEDS_POLICY。
  • collection_mode='REALTIME' は既定で不許可(422 REALTIME_NOT_PERMITTED)。開放は制度判断による。

エラー: 401 MISSING_SIGNATURE / INVALID_SIGNATURE、409 KEY_REVOKED、422 CAP_EXCEEDS_POLICY / LADDER_MAX_EXCEEDS_POLICY / REALTIME_NOT_PERMITTED / MODE_UNSUPPORTED_BY_PAYER_BANK。

PATCH /api/debit-mandates/:dd_mandate_id/caps

上限の変更。方向により要件が非対称である(10_requirements.md §3.2.8.4-10)。

方向 顧客署名
引き下げ 不要
引き上げ 必要(実質的に新たな委任であるため)

Request: { "caps": { ... }, "mandate"?: { 引き上げ時のみ必須の署名一式 }, "idempotency_key": "string" }

Response: { "result": "CAPS_UPDATED", "dd_mandate_id": "DDM-...", "raised": ["month_amount"], "lowered": ["month_count"] }

  • 消費済みカウンタはリセットしない(引き上げ→引き下げの往復による消費の洗浄を防ぐ)。
  • 引き上げに署名がなければ 401 SIGNATURE_REQUIRED_FOR_RAISE。

DELETE /api/debit-mandates/:dd_mandate_id

契約の失効。顧客・受取行のいずれからも可能(顧客に不利益が生じないため)。冪等。

Response: { "result": "REVOKED", "dd_mandate_id": "DDM-...", "revoked_at": "RFC3339", "superseded_collections": 2, "already": false }

失効は以後の解錠を全滅させる。未確定の予告は SUPERSEDED ではなく LAPSED として終端し、件数を返す。

GET /api/debit-mandates/:dd_mandate_id

契約単位の照会。未来の予定を含む(10_requirements.md §3.2.8.8、G6)。

Response: 契約内容・累計枠の消費状況(残枠つき)・過去の収納履歴・次回以降の予定(ラダー全段)。当事者判定と目的コードは照会の認可に従う。

GET /api/debit-mandates?payer_account_alias=...

顧客の「私が許可している引き落とし一覧」。実物の口座振替が提供しない可視性であり、本制度の固有の価値にあたる。

POST /api/collections

収納予告の登録。ラダーの全段を一括で登録する。予告を経ない収納は存在しない。

Request:

{
  "dd_mandate_id": "DDM-...",
  "charge_ref": "2026年4月分",
  "edi_ref": "EDI-...",
  "rungs": [
    { "ladder_seq": 1, "amount": 9800, "latefee": 0,   "due_date": "2026-04-27" },
    { "ladder_seq": 2, "amount": 9800, "latefee": 50,  "due_date": "2026-04-28" },
    { "ladder_seq": 3, "amount": 9800, "latefee": 150, "due_date": "2026-05-13" }
  ],
  "idempotency_key": "string"
}

Response:

{ "result": "COLLECTION_NOTICED",
  "collections": [ { "collection_id": "COL-...", "ladder_seq": 1, "state": "SCHEDULED",
                     "due_date": "2026-04-27", "amend_freeze_at": "2026-04-26T15:00:00+09:00",
                     "confirm_deadline_at": "2026-04-28T00:00:00+09:00" } ],
  "budget_reserved": true }
  • 全段が予告時点で顧客に開示される(10_requirements.md §3.2.8.5-5)。再請求の時期と金額を顧客が事前に知り得ることが本制度の要件である。
  • 遅延損害金は元本と分離して登録する(latefee)。総額への溶かし込みは受理しない。段ごとに固定額であり、計算式は受け付けない。
  • 累計枠はこの時点で予約される(10_requirements.md §3.2.8.4-1)。枠は資金ではなく認可の配分であるため、予告時点で押さえても顧客は何も失わない。
  • 第 2 段以降を REALTIME にできない(10_requirements.md §3.2.8.5-7)→ 422 LADDER_RUNG_CANNOT_BE_REALTIME。
  • スコープ超過は却下せず、当該段を AWAITING_ADDITIONAL_AUTH として返す(state を見ること)。

エラー: 404 DD_MANDATE_NOT_FOUND、409 CHARGE_REF_ALREADY_COLLECTED(当該費目は既に CONFIRMED_OK)、422 BUDGET_RATE_EXCEEDED / BUDGET_EXHAUSTED / NOTICE_PERIOD_TOO_SHORT / LADDER_MAX_EXCEEDED / LATEFEE_EXCEEDS_POLICY / CHARGE_REF_INVALID(PERIODIC の構造検証違反・PR-DD-PERIOD-AHEAD-MAX 超過)。

PATCH /api/collections/:collection_id

予告の変更。凍結が止めるのは不利益変更のみである(10_requirements.md §3.2.8.5-3)。

操作 凍結前 凍結後
減額・予定日の後ろ倒し 可 可
増額・予定日の前倒し 可 不可(422 FROZEN_UNFAVOURABLE_CHANGE)

変更は上書きせず追記する(CollectionAmended を FinalityLog へ)。「先月いくらで予告されていたか」が消えてはならない。

DELETE /api/collections/:collection_id

予告の取下げ(WITHDRAWN)。顧客に有利な変更であるため凍結後も常に可能。他手段での入金があった場合の正当な運用経路である。予約済みの累計枠は解放する。

GET /api/collections/:collection_id

収納の状態照会。

Response:

{ "collection_id": "COL-...", "charge_ref": "2026年4月分", "ladder_seq": 1,
  "state": "FIRED", "settlement_status": "ACCEPTED", "result": null,
  "confirmed": false, "confirm_deadline_at": "2026-04-28T00:00:00+09:00",
  "retriable_today": true,
  "attempts": [ { "attempt_no": 1, "observed_at": "...", "result": "NG",
                  "reason_code": "INSUFFICIENT_FUNDS", "retriable_today": true } ],
  "txid": "TX-..." }

settlement_status の語義(規範):ACCEPTED は受理・照合済みであって収納完了ではない。この値で売掛の消し込みを行ってはならない。confirmed: false を必ず併せて返すのは、ACCEPTED を成功と読み違えた実装が消し込みを走らせる事故を防ぐためである。確定は CONFIRMED_OK / CONFIRMED_NG のみが表す(10_requirements.md §3.2.8.6-8)。他手段との二重収納の防止は受取人の責任であり、ZC の債務はこの信号の品質に限られる。

retriable_today は当日中の再挑戦余地の有無。ACCOUNT_NOT_FOUND 等は当日の入金では解決しないため、受取人が督促対象を絞れるようにする。督促そのものは ZC の外側で行われる。

POST /api/collections/:collection_id/additional-auth

追加認可(再許諾)。スコープ超過で AWAITING_ADDITIONAL_AUTH にある収納を、顧客の署名により通す。

Request: { "decision": "APPROVE" | "DECLINE", "mandate"?: { 単発認可の署名一式 }, "idempotency_key": "string" }

Response: { "result": "APPROVED"|"DECLINED", "collection_id": "COL-...", "state": "SCHEDULED"|"DECLINED_BY_PAYER", "extra_mandate_id": "MANDATE-..." }

  • 既定は単発認可(当該 charge_ref・当該金額・1 回限りのスコープ)。恒久的な引き上げを望む場合は PATCH /api/debit-mandates/:id/caps を用いる。
  • 単発認可は枠を迂回せず、枠に加算する(10_requirements.md §3.2.8.7-4)。
  • 無応答は拒否である。 振替日までに応答がなければ LAPSED となり、ラダー全体が終了する(後続段は発火しない)。この既定を反転させてはならない——沈黙を同意とみなすと本機構自体が過大請求の経路となる。

GET /api/directory/banks/:bankId/collection-profile

払出行の勘定系プロファイルの公開ビュー。公知情報(銀行の取扱時間は約款等で公表される運用事実)であり、受取人が「この行の顧客なら何時までに入金してもらえば間に合うか」を自ら算定できるようにする。

Response: { "bank_id": "002", "supported_modes": ["SCHEDULED","SCHEDULED_LONG"], "center_cut_schedule": ["00:00","12:00","20:00"], "realtime_name_check": true, "last_attempt_guidance": "20:00" }

収納ごとの状態ではなく静的な参照データである。LegacyProfile の window_open_hour / window_close_hour / realtime_name_check から導出する。


管理・設定

POST /api/pspr/register

PSPR登録

Request: { "pspr_ref": "...", "payee_bank_id": "001", "account_hash": "h:...", "expires_at": "RFC3339" }

POST /api/participants/register

参加行登録(初期投入用)

Request: { "bank_id": "001", "bank_name": "長岡銀行", "ingress_base_url": "/bank/001", "h_limit": 100000000 }

GET /api/banks

参加行一覧

POST /api/banks/add

参加行追加(シミュレーター用: 銀行+システム勘定を一括作成)

Request: { "bank_id": "003", "bank_name": "加賀銀行", "h_limit": 100000000, "participant_type": "GOVERNMENT" }

  • participant_type(省略可): "BANK"(既定)| "GOVERNMENT"。給付発起参加者
    の類型化。不正な値は 400 INVALID_INPUT。

DELETE /api/banks/:bankId

参加行削除

GET /api/banks/:bankId/accounts

参加行の口座一覧

GET /api/accounts/:accountId/name

口座名義照会


OpenAPI仕様書

GET /api/openapi/zc.yaml

ZC APIのOpenAPI仕様書(YAML)

GET /api/openapi/bank.yaml

Bank APIのOpenAPI仕様書(YAML)


PDFを作成

フォントは初回だけ読み込むため、1回目は時間がかかります。

用紙
組み方向
表紙
本文