JETCHECKER LINK HTTP API 仕様書(公開版 rev.7)

JETCHECKER LINK HTTP API 仕様書 公開版 rev.7

対応バージョン: rev.7 の全機能は JETCHECKER LINK v1.20.0 以降で利用できます。

サイト掲載版: この文書は、最新版の JETCHECKER LINK に同梱しているAPI 仕様書(公開版 rev.7)と同じ内容です(掲載 2026-09-16)。ソフトウェアの改訂に合わせて更新します。
正式な内容は、お使いのバージョンの JETCHECKER LINK に同梱されている文書です(業務画面のヘルプメニューから開けます)。本ページは最新版の写しのため、古いバージョンではまだ使えない機能の記述を含むことがあります。
導入前のご相談や連携についてのご質問は、JETCHECKER LINK 製品ページの「お問い合わせ」からお気軽にお寄せください。

対象: JETCHECKER LINK(計数結果の受信・保存ソフト)と自社システムを連携する開発者。 計数イベントの取り込み、両替レートの設定(店頭レートボードへの反映)、 店頭・バックヤードディスプレイへの表示(計数結果・見積もり・取引)、 伝票の印字(ESC/POS 透過)ができます。

この文書は外部提供用の仕様です。ここに書かれた内容は互換性を維持します (2章「互換性の約束」参照)。ここに書かれていない動作・ パラメータ・ファイル形式は内部実装であり、予告なく変わることがあります。

版表記について: 本書自体の改訂版は rev.x と表記します (API パスの /v1/・イベントスキーマ v2・雛形規約 v1 などの 機能側のバージョンとは別物です)。

本書の構成: API の仕様そのものは5章「エンドポイント」全13本をまとめて記載します。8章「レート設定ガイド」9章「ディスプレイ表示ガイド」10章「印刷ガイド」は 考え方と使い方の章で、必要に応じて5章を参照します。

1. 概要

  • JETCHECKER LINK は、計数機に接続された PC 上で常駐し、計数イベントを ローカルのデータベースへ追記専用で保存します。
  • 本 API は同じ PC の 127.0.0.1(localhost)のみで待ち受けます。 他の PC・LAN からは接続できません。読み取りに認証はありません (同一 PC 内であることが境界です)。書き込みのみトークン認証が 必要です(5章「書き込み系 API の共通規則」参照)。
  • クライアントは「未取込イベントの取得 → 処理 → カーソル更新(確定)」を 繰り返すことで、取りこぼしなく1件ずつイベントを消費できます。
  • 店頭ディスプレイへの表示(計数結果・見積もり・取引・レートボード)は 9章「ディスプレイ表示ガイド」を参照してください。

2. 互換性の約束

本仕様(rev.7)では以下を約束します:

  • エンドポイント13本(イベント取得・カーソル更新・状態取得・レート一覧・ レート変更・表示状態の設定・表示スナップショット・単一イベント取得・ レートボードデータ・レートボード設定の取得/変更・印刷・印刷ジョブ状態)の パス・メソッド・必須パラメータ
  • 印刷(エンドポイント1213)の バイト透過の動作ルール(無検査・バイト無追加)、発行元スタンプの存在(raw では先頭)、 ジョブ state の遷移、/v1/status printer 節の 各項目の意味。ジョブ失敗の reason の値は追加される ことがあります(未知の値も「失敗」として扱ってください)。 10章「印刷ガイド」の組版ガイドは参考情報 (プリンター実機の挙動は機種依存)で、約束の対象外です
  • NDJSON エンベロープの必須フィールド(seq / event_id / received_at / event または raw+ingest_error)と in_window の意味
  • イベントスキーマ v2 のフィールド(6章参照)。 フィールドの追加は互換変更として行うことがあります。 クライアントは未知のフィールドを無視してください(エンベロープ・レート応答・ 表示スナップショットも同様)
  • カーソルの動作ルール(連続消費・巻き戻し可・最新超えは拒否)
  • 表示状態の動作ルール(中身を解釈しないパススルー・後勝ち・非永続)と 雛形のファイル差し替え規則
  • 雛形規約 v1(表示雛形の data-jc 語彙)。 語彙の追加は互換変更として行います。規約 v2 が必要になった場合も、 移行期間中は v1 雛形の動作を維持します
  • ポート発見手順・書き込みトークンの取得手順

約束しないもの: 内蔵雛形(店頭表示・レートボード)のデザイン・版面は 対象外です(予告なく改善されます)。デザインを固定したい場合は 9.2「表示ページと雛形」の差し替え規則で自前の雛形を 置いてください(その規則自体は約束します)。

破壊的変更が必要になった場合は、パスを /v2/ として提供し、 /v1/ は移行期間中並行して維持します。

3. 接続手順(ポート発見)

ポートは固定ではありません。必ず設定ファイルから実ポートを読んでください:

  1. %LocalAppData%\JetChecker\connector.json を読む(JSON)
  2. http.last_port の値が、現在実際に待ち受けているポート
  3. http://127.0.0.1:<last_port>/v1/... へ接続する

既定ポートは 17434 ですが、使用中の場合は自動で別ポート(+1 ずつ)に ずれるため、決め打ちしないでください。JETCHECKER LINK の再起動でポートが 変わることがあるので、接続エラー時は connector.json を読み直す実装を 推奨します。

4. イベントと取り込み位置

  • イベント = 計数機での1回の計数結果。受信時に一意の event_id(ULID)と受信連番 seq(1 から単調増加)が 付与されます。イベントは追記専用で、一度保存されたものは変更・削除されません。
  • クライアントID は利用者が自由に決めます(例: pos-syncmy-app)。事前の登録手続きはなく、決めた ID を client パラメータで名乗って初めてアクセスした時点で、サーバー側にその ID の 取り込み位置(0 = 全件未取込)が作られます。
  • 予約 ID: webapp と、jc-(小文字)で始まる ID は 製品予約です。使用すると 400(code: "reserved_client")に なります。また excel / csv / diag-vba は 付属機能が使用中のため、エラーにはなりませんが使わないでください (付属機能とイベントを取り合います)。
  • 取り込み位置(API 上の名称は「カーソル」)はサーバー側が記憶します。 「その ID が seq いくつまで取り込んだか」を JETCHECKER LINK が覚えており、 GET /v1/events はその位置より後のイベントだけを返し、 POST .../cursor で位置を進めます。クライアント側で位置を保存する 必要はありません
  • ID はアプリケーションごとに固有のものを使ってください。 複数のクライアントが同じ ID を共有すると、取り込み位置を取り合ってイベントを 取りこぼします(別 ID 同士は完全に独立で、干渉しません)。
  • 位置を巻き戻せば(小さい値を POST)、過去のイベントを何度でも再取得できます。

5. エンドポイント(全13本)

表5-1: エンドポイント一覧
#メソッドとパス認証内容
1GET /v1/events不要未取込イベントの取得
2POST /v1/clients/{id}/cursor不要カーソル更新(取込確定)
3GET /v1/status不要状態取得(新着検知・接続状態)
4GET /v1/rates不要レート一覧の取得
5POST /v1/rates必要レート変更/削除
6POST /v1/display/state必要表示状態の設定/クリア(見積もり・取引・待機)
7GET /v1/display/snapshot不要表示スナップショット(表示ページのポーリング先)
8GET /v1/events/{event_id}不要単一イベントの取得
9GET /v1/display/rates不要レートボードデータの取得(ボードの描画データ)
10GET /v1/display/rateboard不要レートボード設定の取得
11POST /v1/display/rateboard必要レートボード設定の変更(ヘッダー・列数・表示順)
12POST /v1/print/raw必要印刷(完成済み ESC/POS バイト列の透過印字)
13GET /v1/print/jobs/{job_id}不要印刷ジョブ状態(印字の完了確認)

書き込み系 API の共通規則(トークン認証と実施者)

書き込み(エンドポイント5・6・11・12)にはトークン認証が必要です:

  1. %LocalAppData%\JetChecker\connector.jsonhttp.print_token の値を読む (名称は歴史的経緯によるもので、書き込み系 API 共通のトークンです)
  2. リクエストに Authorization: Bearer <トークン> を付ける
  3. Content-Type: application/json を必ず付ける(無いと 415)

トークン不一致・欠落は 401。ブラウザからの呼び出し(Origin ヘッダー付き)は loopback オリジン以外 403 です(通常のネイティブアプリ・サーバープロセスからの 呼び出しには関係ありません)。お客様に見せる金額・レートを偽装できる口のため、 書き込みはすべてこの規則で保護されています。

また、レート・表示系の書き込み(エンドポイント5・6・11)のボディには actor(実施者名)が必須です (64文字以内・制御文字不可)。誰の操作かが JETCHECKER LINK 側の操作記録(監査ログ)と レート履歴に残ります。JETCHECKER LINK の設定で実施者リストが登録されている場合は、 登録名との完全一致が要求されます(未登録名は 400)。 リスト未登録の店では自由入力です。 印刷(エンドポイント12)は actor の代わりに client(発行元の識別子)を必須とし、印刷履歴に記録されます。

エンドポイント1: 未取込イベントの取得

GET /v1/events?client=<id>[&limit=N]
  • client(必須): 利用者が決めたクライアントID。英数字と . _ -、64文字以内。初めて使う ID は 取り込み位置 0(全件が未取込)から始まります
  • limit(任意): 最大件数。省略時は全未取込

応答は NDJSON(1行 = 1イベントの JSON、seq 昇順)。各行はエンベロープ形式です:

{"seq":131,"event_id":"01KXQQ...","received_at":"2026-07-17T18:50:46+09:00","event":{"schema_version":2,"currency":"JPY","total_value":93000,"...":"..."}}
表5-2: NDJSON エンベロープのフィールド
フィールド内容
seq受信連番(単調増加)。カーソル更新の基準値
event_idULID。重複排除・突合のキーは必ずこれを使う(12章「実装ガイド」)
received_atPC がイベントを受信した日時(ISO 8601)。信頼できる時刻はこちら
eventペイロード(計数イベント本体。6章「イベントスキーマ」)。無加工
raw / ingest_errorペイロードが JSON として解釈できなかった場合のみ、event の代わりに原文と理由が入る。「異常でも保存し、捨てない」方針のため API でも返る
in_windowキーが無い = 正規受信false = 受信窓外の行(7章「受信窓」)。true は送られません(省略が正規)

エンドポイント2: カーソル更新(取込確定)

POST /v1/clients/{id}/cursor
Content-Type: application/json

{"cursor_seq": N}

seq = N まで取り込み済み」を記録します。以後の取得は seq > N のみ返ります。

  • N が現在の最新 seq を超える場合は 400(未受信イベントの黙殺防止)
  • N を小さくする巻き戻しは許可されています(再処理・復旧用)
  • 応答: {"client_id":"...","cursor_seq":N}

エンドポイント3: 状態取得

GET /v1/status
{
  "serial": {"port": "COM3", "connected": true},
  "events": {"count": 141, "max_seq": 141, "last_received_at": "2026-07-18T16:00:02+09:00"},
  "clients": [
    {"client_id": "my-app", "type": "http", "cursor_seq": 138, "pending": 3}
  ]
}
  • 新着の検知は events.max_seq のポーリングで行ってください (前回値より増えていたら取得を実行)。1秒間隔程度までを目安とします
  • serial.connected で計数機側ケーブルの接続状態を確認できます
  • clients = 登録済みクライアントIDごとの取り込み位置の一覧。 pending = そのクライアントの未取込件数

エンドポイント4: レート一覧の取得

GET /v1/rates

有効レート(通貨ごとの最新値)を表示順で返します。 値の読み方(WE SELL / WE BUY・unit)は 8章「レート設定ガイド」参照。

{"rates": [{"currency": "USD", "we_sell": "159.24", "we_buy": "157.15",
  "unit": 1, "base_fee": 200, "base_fee_pct": null, "inverted": false,
  "changed_by": "山田", "source": "manual", "changed_at": "2026-08-01T12:00:00+09:00"}]}
  • レート値は10進文字列です(float の丸め誤差を持ち込まないため。 設定時も文字列で送ってください)
  • inverted: true = WE SELL ≦ WE BUY の逆転(誤レートの疑い)
  • base_fee = 両替手数料(円)。base_fee_pct が null なら 固定額制、値あり(文字列%)なら定率制で base_fee は最低手数料(円) (8.1「手数料の適用のされ方」参照)

エンドポイント5: レート変更/削除

POST /v1/rates
Authorization: Bearer <トークン>
Content-Type: application/json

{"actor": "山田", "currency": "USD", "we_sell": "159.24", "we_buy": "157.15", "unit": 1}

1通貨分の設定(新規・上書きとも同じ形)です。

表5-3: レート変更/削除のフィールド
フィールド内容
actor(必須)実施者名(共通規則)
currency(必須)通貨コード(英字3文字。小文字は大文字に正規化)
we_sell / we_buyレート(正の10進文字列。整数部9桁・小数部6桁まで)。null = その向きは未設定(掲示されない)。値の意味は8章
unit1 または 100(「100 KRW あたり」等)。省略時 1
base_fee手数料(円・0以上の整数)。省略時 0。固定額制ではこの額がそのまま手数料。定率制(base_fee_pct あり)では最低手数料になる(8.1)
base_fee_pct定率手数料%(10進文字列。0 より大きく 100 未満)。省略・null = 固定額制
removetrue で削除({"actor","currency","remove":true})。履歴には残ります
  • 応答: {"currency": "USD", "removed": false}。WE SELL ≦ WE BUY のときは 警告つきで受理されます(warning フィールド。 掲示前に確認してください)
  • 不正な値は 400({"error":"...","code":"bad_request"})
  • 変更はすべて JETCHECKER LINK 側のレート履歴と監査ログに残ります

エンドポイント6: 表示状態の設定/クリア

POST /v1/display/state
Authorization: Bearer <トークン>
Content-Type: application/json

{
  "client": "my-app",
  "type": "exchange",
  "refs": {"transaction_id": "X20260728-0002",
           "events": [{"role": "in", "event_id": "01K..."}]},
  "fields": {"title": "両替計算書", "amount_jpy": "¥45,000"},
  "lines": ["両替計算書", "USD 300.00", "RATE 152.30", "¥45,000"]
}

店頭ディスプレイの表示内容を push します(表示の仕組み・使い方の流れは 9章「ディスプレイ表示ガイド」参照)。

表5-4: 表示状態設定のフィールド
フィールド内容
client(必須)呼び出し元の識別子(イベント取得のクライアントID と同じ文字規則・予約規則。表示専用に別 ID でもよい)
type(必須)表示の種別。JETCHECKER LINK は中身を解釈しません(表5-5 の推奨語彙参照)。予約語 idle = 表示クリア(このとき refs / fields / lines は無視)
refs(任意)関連付け。transaction_id(自社の取引番号等)と events({role, event_id} の配列)。event_id は実在検証されます(未知・重複は 400)— イベントを取得してから push してください
fields(任意)自由なキー→文字列値のマップ。中身は解釈されず、雛形が data-jc="tx.fields.<キー>" で拾います
lines(任意)文字列の配列(表示本文)。既定雛形はこれを表示します
  • title(fields.title)と lines を 必ず入れてください — 既定雛形が参照する標準キーはこの2つです (独自雛形なら任意)
  • 整形は push 側の責務です(¥記号・3桁区切り・桁揃え)。値の差し込みは textContent のみで、HTML として解釈されることはありません(タグを送っても 文字がそのまま表示されるだけ。安全側の仕様)
  • ボディ全体 16KB 以内。適用は上書きのみ(履歴なし)。 データベースには保存されません(JETCHECKER LINK 再起動で消えます)
  • 応答: {"type": "exchange", "posted_at": "..."}。エラーは code 付き(bad_request | unknown_event | unauthorized | forbidden)
表5-5: type の推奨語彙(既定雛形が特別扱いする値)
意味既定雛形の挙動
quote見積もり(確定前の提示。お客様との合意形成用)配色を変え「未確定」注記を表示。確定時刻を出さない(図9-2)
exchange両替取引(確定)通常表示(図9-3)
closingレジ締め等の社内向け情報店頭用雛形は表示しない(バックヤード用のみ)
idle(予約語)表示クリア待機画面へ(図9-4)

上記以外の自由な値も送れます(既定雛形は通常表示。独自雛形での出し分けは 9.4「独自 type と雛形の紐づけ」)。

エンドポイント7: 表示スナップショット

GET /v1/display/snapshot

表示ページのポーリング先です。独自の表示クライアント(サイネージ アプリ等)を作る場合はこれをポーリングしてください(1秒間隔で十分)。

{
  "state": {"client": "my-app", "type": "exchange", "posted_at": "...",
            "refs": {"...": "..."}, "fields": {"...": "..."}, "lines": ["..."]},
  "latest_event": {"seq": 164, "event_id": "01K...", "received_at": "...",
                   "event": {"schema_version": 2, "...": "..."}}
}
  • state = 最後に push された表示状態。未 push・再起動後は null。 クリア後は {"type": "idle", "posted_at": "..."}
  • latest_event = 最新イベントのエンベロープ(/v1/events と同形式)。 1件も無ければ null
  • 取り込み位置(カーソル)には一切影響しません — イベント消費とは独立の読み取り専用観測です

エンドポイント8: 単一イベントの取得

GET /v1/events/{event_id}

エンベロープ形式で1件返します(未知の event_id は 404)。 特定の計数結果の固定表示(/display?event_id=<ULID>)や突合の再確認に 使います。

エンドポイント9: レートボードデータの取得

GET /v1/display/rates

店頭レートボード(両替レートを1通貨=1カードで掲示するモニター表示。図8-2)の 描画データです(店頭掲示と同じ公開情報のみで、手数料・変更者・履歴は 含まれません)。

ボードのページ /display/rates.html は本エンドポイントを10秒ごとに 取得して描画しています。このためエンドポイント5 で レートを流し込めば、店頭掲示は常に自動で最新になります(掲示の貼り替え・ 更新漏れがなくなる — ボードを使うこと自体が利点です)。

版面を独自デザインにしたい場合も、この自動更新の仕組みごと引き継げます: 表示雛形と同じファイル差し替え(9.2)で %LocalAppData%\JetChecker\display\rates.html を自作ページ (本エンドポイントをポーリングして描画。10秒間隔で十分)に置き換えれば、 キオスク用ショートカットにも LAN 表示(タブレット等の別デバイス。 セットアップ手順書参照)にも自作の版面がそのまま配信されます。

自社システムから本エンドポイントを読むだけの使い方もできます(掲示内容の確認・ 転記等)。ただし別オリジンに置いたブラウザページからの fetch は失敗します (読み取り API は CORS 許可ヘッダーを返しません。許可すると、店の PC のブラウザで開いた任意の Web サイトがローカルの計数データ・レートを読めてしまうためです。ブラウザを介さない呼び出し=自社サーバー・ネイティブアプリには関係ありません)。 掲示ページとして作るものは上記のファイル差し替えで JETCHECKER LINK に 配信させてください。

{
  "as_of": "2026-08-11T12:00:00+09:00",
  "rates": [
    {"currency": "USD", "we_sell": "159.24", "we_buy": "157.15", "unit": 1,
     "name_ja": "アメリカドル", "flag": "flag-usd.svg", "symbol": "$",
     "changed_at": "2026-08-11T09:30:00+09:00"}
  ],
  "header_lines": ["JetChecker 両替店"],
  "max_columns": 4
}
  • rates は設定された表示順(エンドポイント11)。 we_sell / we_buy は10進文字列、未設定の側は null(ボードは「—」表示)
  • name_ja / flag / symbol は同梱の通貨情報 (未収載の通貨では空)。国旗画像は /display/<flag の値> で取得できます
  • header_lines(ヘッダー文言)・max_columns(1行の最大カード枚数)は 設定されている場合のみ現れます(設定はエンドポイント11)。 max_columns はボードの列数調整用の値(エンドポイント11)で、 差し替えた独自版面では無視して構いません

エンドポイント10: レートボード設定の取得

GET /v1/display/rateboard

店頭レートボードの表示設定です(店頭掲示と同じ公開情報)。 未設定の項目は空配列・0 で返ります。

{
  "header_lines": ["JetChecker 両替店", "手数料込みのレートです"],
  "max_columns": 4,
  "currency_order": ["EUR", "USD", "KRW"]
}

エンドポイント11: レートボード設定の変更

POST /v1/display/rateboard
Authorization: Bearer <トークン>
Content-Type: application/json

{"actor": "山田", "header_lines": ["JetChecker 両替店"], "max_columns": 4,
 "currency_order": ["EUR", "USD", "KRW"]}

部分更新: 送ったフィールドだけが変わり、省略したフィールドは 変わりません。JETCHECKER LINK の業務画面(「設定」→「店頭レートボード」)でも 同じ設定を編集でき、どちらで変えても同じ検証・同じ監査記録です。

表5-6: レートボード設定の変更フィールド
フィールド内容
actor(必須)実施者名(共通規則)
header_linesヘッダー文言の配列(最大3行・各40文字以内・制御文字不可)。1行目が大きく(店名の格で)表示され、2行目以降は小さめの添え書き(図8-2 の上部)。空配列 [] = ヘッダーなし(レートカードだけの表示)に戻す
max_columnsボードの列数調整用の値。ボードの版面は窓の大きさと通貨数から1行のカード枚数を自動で決めるが、この値を設定するとそれが上限になる(縦長画面で文字が窮屈なときの逃げ道。0〜8、0 = 完全自動=既定)。差し替えた独自版面で使うかは自由
currency_order通貨コード(英字3文字)の配列。重複不可・40通貨までエンドポイント4「レート一覧」の応答順・レートボード・業務画面のレート表の並びに適用され、未収載の通貨は末尾に続く。空配列 [] = 既定の並びに戻す
  • 応答: 現在の設定(エンドポイント10 と同形)+ changed(変更された項目数)
  • 不正な値は 400(1フィールドでも不正なら全体不受理)
  • 変更は JETCHECKER LINK 側の監査ログに残ります(業務画面からの設定変更と同じ記録)
  • ボードへは約10秒以内に反映されます

エンドポイント12: 印刷(ESC/POS 透過)

POST /v1/print/raw
Authorization: Bearer <トークン>
Content-Type: application/json

{
  "client": "my-app",
  "data": "G0BIZWxsbyEKCg==",
  "refs": {},
  "meta": {}
}

完成済みの ESC/POS バイト列を、接続ユニット経由でプリンターへそのまま送って 印字します。前提・制約・伝票の作り方は10章「印刷ガイド」を 先にお読みください(プリンターはシリアル接続モデルのみ対応です)。

表5-7: 印刷のフィールド
フィールド必須内容
client発行元の識別子(英数字・. _ -、64文字以内)。印刷履歴(ジャーナル)に記録されます
dataESC/POS バイト列の base64(RFC 4648・パディングあり)。デコード後 1〜262,144 バイト(256KB)
slip_type伝票種別の自由文字列。JETCHECKER LINK は解釈せず記録のみ
refs引用申告(下記)
meta自由形式 JSON。解釈せず記録のみ(シリアライズ後 16KB 以内)

動作ルール:

  • 無検査・バイト無追加: data の中身は検査しません(キャッシュドロワーの開閉、プリンターの NV メモリ書き込み等の制御コマンドもそのまま通ります — 何を送るかはクライアントアプリの責任です)。カットや紙送りも一切自動で足しません。カットして排出したい場合は、カット命令まで含めて data を組んでください(10章)。
  • 唯一の例外 — 発行元スタンプ: 接続ユニットが1行 [<機器ID> job=<ジョブ番号>] を無条件で印字します(無効化できません)。raw のジョブでは、クライアントが送ったバイト列の前(先頭)に必ずこの1行が付きます。これは「この紙は PC 発行である」ことを紙の上で判別するための仕組みで、ジョブ番号はエンドポイント13・印刷履歴と突き合わせるキーになります。なお JETCHECKER LINK 同梱の伝票(計数結果伝票・両替計算書)では同じスタンプが伝票の末尾に付きます(接続ユニットのソフトウェア 0.11.0 以降)。raw が先頭固定なのは意図的な違いです: raw はバイト透過で、クライアントが送るデータ内のカット命令の位置を JETCHECKER LINK が知り得ないため、末尾に付けるとカットの後=次の紙の先頭にスタンプが載ってしまいます。
  • 成功応答は受理のみの即応答です: {"job_id": 123, "state": "queued"}。実際の印字はこの後に進みます(完了確認はエンドポイント13)。

引用申告(refs)— 任意。「この紙はどの計数イベント/どの取引を根拠に刷ったか」を申告できます。申告すると印刷履歴に残り、後日「どのアプリがいつどのイベントを引用して刷ったか」を店舗側で確認(監査)できます:

"refs": {
  "transaction_id": "X20260831-0001",
  "events": [ {"role": "count", "event_id": "01KXQQ..."} ]
}
  • transaction_id: 自由文字列(64文字以内)。自社システムの取引 ID をそのまま使えます(JETCHECKER LINK は形式に関知しません)
  • events[].role: 自由文字列(推奨: in / out / count)
  • events[].event_id: 実在チェックがあります(未知の ID・同一リクエスト内の重複は 400)。エンドポイント1で得た event_id を渡してください

ジョブの動作ルール:

  • 同時1ジョブの直列実行です(同梱機能の印刷とも同じ待ち行列を共有します)。待ち行列の上限(目安8件)を超えると 503(queue_full)。
  • 計数機(接続ユニット)が未接続のときは受理せず 503(printer_unavailable)を即返します。接続復旧待ちの取り置きはしません(何分も後に突然紙が出る事故を防ぐため)。印刷ボタンの活性判定は /v1/statusserial.connected で行ってください。
  • キャンセル API はありません(1ジョブは最長でも数分、実用上は数十秒以内)。
  • リトライは新規 POST(新しいジョブ番号でジョブ丸ごと再送)です。途中再開はありません。二重印字の最終判断(再発行の表示など)はクライアントアプリの責任です。
  • 全ジョブ(成功・失敗とも)は JETCHECKER LINK の印刷履歴(追記専用)に記録されます。ペイロード全文+SHA-256 も保存され、「送ったバイト列と紙が違う」問い合わせの際に照合できます。

主なエラー(共通のエラー応答形式は11章): 400 bad_request(base64 不正・client の文字種違反・refs の不正)/ 400 too_large(デコード後 256KB 超過・meta 16KB 超過)/ 400 unknown_event(refs.events[].event_id が実在しない)/ 503 printer_unavailable / 503 queue_full

エンドポイント13: 印刷ジョブ状態

GET /v1/print/jobs/{job_id}
{
  "job_id": 123, "kind": "raw", "client": "my-app", "state": "sending",
  "total_bytes": 1832, "sent_bytes": 1024,
  "created_at": "2026-08-31T14:00:12+09:00", "started_at": "2026-08-31T14:00:12+09:00",
  "finished_at": null, "reason": null
}
  • statequeued(受理済み・送信待ち)→ sending(送信中)→ done または failed と遷移します。
  • sent_bytes は接続ユニットが物理的にプリンターへ送出済みのバイト数です。進捗表示は実際の印字の進行とほぼ一致します。
  • done は「接続ユニットがプリンターへ全バイトを書き切り、データ化けなしを確認した」という意味であり、紙が正しく出た保証ではありません(紙切れ・紙詰まりは検知しません。10章の注意事項も参照)。
  • 完了済みジョブも照会できます(JETCHECKER LINK の再起動をまたいでも可)。未知の job_id は 404。
  • ポーリングは1秒間隔で十分です。標準の送出速度は約 634 バイト/秒(通常の伝票 1〜2KB なら 2〜3 秒。店舗側の設定・機種によっては高速化されている場合があります)。

reason(failed のときのみ。値は追加されることがあります=未知の値も失敗として扱う):

表5-8: 印刷ジョブの失敗理由
意味
unauth / busy / bad_line / bad_seq / crc_mismatch / timeout接続ユニットが報告した失敗(接続直後・別ジョブ実行中・転送データの破損・応答なし 等)。新規 POST でジョブごと再送してください
too_largeジョブが上限(256KB)超過
serial_lost送信中に計数機(接続ユニット)との接続が切れた
baud_unsupported / foot_unsupported接続ユニットのソフトウェアが古く、店舗側のプリンター設定に対応していない(店舗側で接続ユニットの更新が必要)
internalJETCHECKER LINK 内部エラー

6. イベントスキーマ(v2)

event フィールドの中身(ペイロード)です。schema_version が 2 であることを 確認してから処理してください(未知のバージョンも配信はされます。その場合は保留するか raw 相当として扱ってください)。

{
  "schema_version": 2,
  "device_id": "JC-A4C2F0",
  "timestamp": "2026-03-26T14:26:38+09:00",
  "currency": "JPY",
  "mode": "value",
  "denominations": [
    {"denom": 2000, "qty": 2, "value": 4000},
    {"denom": 1000, "qty": 5, "value": 5000}
  ],
  "total_count": 7,
  "total_value": 9000,
  "verified": true
}
表6-1: 必須フィールド
フィールド内容
schema_versionスキーマ版。現行 2
device_id送信ユニットの個体ID(JC- + 英数字)
timestamp計数機本体の時計による計数日時(ISO 8601)。機体の時計状態によっては過去日付(2000-01-01 起点など)になることがあります。時刻の判断にはエンベロープの received_at を使ってください
currency通貨コード(英大文字3文字)。機体側で確定できない計数では空文字 ""
mode"value"(金額計数)/ "count"(枚数のみの計数)
denominations金種内訳の配列 {denom(額面), qty(枚数), value(小計)}count モードでは空配列
total_count合計枚数
total_value合計金額(count モードでは 0)
verified金種内訳と合計の照合結果。false の行は計数値に不整合があった可能性があります(それでも保存・配信されます。error に理由)
表6-2: オプションフィールド(存在する場合のみ)
フィールド内容
receipt_no計数機が採番する通し番号。機体の状態によりリセットされ得るため、一意キー・重複排除には使わないでください(event_id を使う)
user_id計数機に設定されている操作者ID
machine_sn計数機本体のシリアル番号(device_id とは別。複数台運用時の突合用)
mix_index / mix_count混合通貨(CUR MIX)モードの計数結果でのみ現れます(通常計数ではキー自体なし)。1回の Mix 計数は通貨ごとに分割されて複数イベントになり、mix_count = 含まれていた通貨の数(=分割された行数)、mix_index = 通貨別の行に振られる連番(1〜mix_count。伝票上の出力順)。下記「Mix 計数の受信例」参照
serials紙幣1枚ごとのシリアル番号の配列 {denom, serial}(計数時の並び順を保持)。読み取れなかった紙幣は serial: null
serials_verifiedserials の枚数が total_count と一致するか(verified とは独立)
error異常の内容(verified: false 時など)

Mix 計数(複数通貨一括)の受信例

計数機の混合通貨(CUR MIX)モードで3通貨を一括計数すると、 GET /v1/events には3行のイベントとして届きます (1イベント=1通貨の原則)。例:

{"seq":152,"event_id":"01KZG3W8A0EXAMPLE000000001","received_at":"2026-08-11T14:20:11+09:00","event":{"schema_version":2,"device_id":"JC-A4C2F0","receipt_no":"00123","machine_sn":"K7A012345","timestamp":"2026-08-11T14:20:05+09:00","currency":"JPY","mode":"value","denominations":[{"denom":10000,"qty":3,"value":30000},{"denom":1000,"qty":2,"value":2000}],"total_count":5,"total_value":32000,"verified":true,"mix_index":1,"mix_count":3}}
{"seq":153,"event_id":"01KZG3W8A1EXAMPLE000000002","received_at":"2026-08-11T14:20:11+09:00","event":{"schema_version":2,"device_id":"JC-A4C2F0","receipt_no":"00123","machine_sn":"K7A012345","timestamp":"2026-08-11T14:20:05+09:00","currency":"USD","mode":"value","denominations":[{"denom":100,"qty":2,"value":200},{"denom":20,"qty":1,"value":20}],"total_count":3,"total_value":220,"verified":true,"mix_index":2,"mix_count":3}}
{"seq":154,"event_id":"01KZG3W8A2EXAMPLE000000003","received_at":"2026-08-11T14:20:11+09:00","event":{"schema_version":2,"device_id":"JC-A4C2F0","receipt_no":"00123","machine_sn":"K7A012345","timestamp":"2026-08-11T14:20:05+09:00","currency":"CNY","mode":"value","denominations":[{"denom":100,"qty":6,"value":600}],"total_count":6,"total_value":600,"verified":true,"mix_index":3,"mix_count":3}}

読み方:

  • mix_count: 3 = この Mix 計数には3通貨が含まれていた。 mix_index 1〜3 が通貨別の行の連番(この例では JPY → USD → CNY の 伝票出力順)
  • receipt_no / machine_sn / timestamp は 3行すべてに同じ値が複製されます — 「同じ1回の計数」として束ねる手がかりは こちらです(mix_index はグループ内の何番目かを示すだけで、 グループの識別子ではありません)
  • event_idseq は行ごとに別 (全行が独立したイベントとして保存・配信されます)
  • denominations / total_count / total_value / serialsその行の通貨の分だけが入ります
  • 実装上は「連続して届く同じ receipt_notimestampmix_count 行」を1回の計数として扱ってください。Mix を使わない 運用ならこの2キーは無視して構いません(現れません)

7. 受信窓(in_window)

JETCHECKER LINK は接続ユニット(計数機側の送信機器)と定期的に相互認証を行い、 認証が成立している間だけ「受信窓」が開きます。窓が開いていない状態で届いた行 (認証に対応しない機器からの入力・PC 上の別ソフトによる注入など)にも保存・配信は 行われますが、エンベロープに in_window: false が付きます。

  • キーが無い行 = 正規受信です(通常はすべてこちら)
  • in_window: false の行は業務データとして採用しないことを 推奨します。JETCHECKER LINK 自身の業務機能(取引確定)も窓外の行の 取引化を拒否します
  • 窓外の行が継続的に発生する場合は、機器構成か PC 環境に異常があります (販売元へお問い合わせください)

8. レート設定ガイド

レート関連のエンドポイントは 4(一覧)5(変更/削除)9(ボードデータ)10/11(ボード設定)です。 この章は値の意味と店頭への反映を説明します。

レート値の意味: 値は「その通貨 unit(1 または 100)単位あたりの円」で、 店側が主語です。we_sell = 店が外貨を売るレート (お客様は外貨を買う)、we_buy = 店が外貨を買うレート。 正常な状態は WE SELL > WE BUY です。

業務画面のレート管理タブ(レート表)
図8-1 — 業務画面「レート管理」のレート表。 エンドポイント5 で設定した値はここに反映され、店舗スタッフの手動変更と同じ履歴・監査に残る。 行左端の「⠿」ドラッグで通貨の表示順を変えられる(エンドポイント11 の currency_order と同じ設定)

8.1 手数料の適用のされ方

JETCHECKER LINK の業務画面が両替計算に使う規則です: 固定額制は base_fee そのまま。定率制は「お預り額の円換算 × %」を 円未満切り捨てで計算し、base_fee を下回る場合は base_fee(=最低手数料)を適用します。 例: {"base_fee_pct": "2.0", "base_fee": 200} → 手数料はお預り額の2%、 ただし小額取引で計算額が 200 円に満たないときは 200 円。

8.2 店頭レートボードへの反映

設定したレートは、JETCHECKER LINK が配信する店頭レートボード (http://127.0.0.1:<last_port>/display/rates.html)に自動反映されます (約10秒以内)。レートボードは push 駆動の店頭表示(エンドポイント6) とは独立した別ページで、表示状態の push や計数イベントの影響を 受けません(変わるのはレート変更と、エンドポイント11 の ボード設定変更のときだけです)。ヘッダー文言(店名等)・カード枚数・通貨の表示順は エンドポイント11 で設定できます。版面のデザイン自体は 本仕様の互換性約束の対象外です(データが反映されることのみ約束)。 別デバイス(HDMI スティック・タブレット等)での表示はセットアップ手順書の 「LAN 表示」を参照してください。版面を独自デザインにする場合は エンドポイント9 の差し替え方式を参照してください。

店頭レートボードの表示例
図8-2 — 店頭レートボード(/display/rates.html)。 エンドポイント5 のレート POST が約10秒以内にここへ反映される。上部のヘッダー文言・ 表示順・列数はエンドポイント11 で設定(版面デザインは互換性約束の対象外)

9. ディスプレイ表示ガイド

お客様向けの店頭ディスプレイ(2枚目モニター等)やバックヤードのモニターに、 計数結果・見積もり・両替取引を表示する機能です。関連エンドポイントは 6(表示状態の push)7(スナップショット)8(単一イベント)です。

構成(登場者は2者):

  • 表示ページ — ブラウザで開いておく HTML(店頭用 http://127.0.0.1:<last_port>/display、バックヤード用 /display/live.html)。約1秒間隔でエンドポイント7を ポーリングして描画するだけの読み取り専用の観測者です。 何枚開いても構いません。開き方(キオスク用ショートカット等)は セットアップ手順書の「ディスプレイ表示」を参照
  • push 元 = 自社システムエンドポイント6で 表示内容を送ります。計数結果の表示だけは push 不要です (表示ページが最新イベントを自動表示)

9.1 表示の3状態と切り替わり規則

表示ページは常に次の3状態のいずれかです:

表9-1: 表示の3状態
状態表示内容きっかけ
count最新の計数結果計数イベントの受信(自動。API 発行不要)
transactionpush された内容(見積もり・取引など)POST /v1/display/state
idle待機画面type: "idle" の push、または自動待機

切り替わりは後勝ち(last-write-wins)です:

  • 「最新計数イベントの受信時刻」と「表示 push の時刻」の新しい方が 表示されます。同時刻の場合は push(transaction)優先
  • 最終更新から一定時間(既定180秒。雛形の設定で変更可)経過すると自動的に idle へ落ちます
  • したがって「取引表示中に次のお客様の計数が始まると、表示は自動的に新しい 計数結果へ切り替わる」— これは1台のディスプレイを接客順に使い回すための 意図した挙動です(9.6「注意事項」参照)
計数結果(count)の表示例
図9-1 — 計数結果(count)。計数イベント受信で自動表示 (API 発行不要)
見積もり(quote)の表示例
図9-2 — 見積もり(type: "quote")。 配色が変わり「未確定」注記が出る。確定時刻は表示されない
両替取引確定(exchange)の表示例
図9-3 — 両替取引の確定(type: "exchange")。 push した lines がそのまま表示される
待機(idle)の表示例
図9-4 — 待機(idle)。type: "idle" の push または自動待機(既定180秒)でこの画面に戻る。図中央のロゴは display\logo.png 設置時の表示例(未設置なら挨拶文のみ — 「自社ロゴの表示」参照)

9.2 表示ページと雛形(デザインの差し替え)

内蔵の表示ページ(雛形)は次の3枚です。内蔵雛形のデザインは互換性の約束の 対象外で、製品更新で予告なく改善されます:

  • /display — 店頭用(お客様向け。closing を表示しない)
  • /display/live.html — バックヤード用(取引 push を無視して計数のみ表示)
  • /display/rates.html — 店頭レートボード

これらのページは JETCHECKER LINK 本体に埋め込まれて配信されていますが、 配信時には先にデータ領域のフォルダーが確認されます。本書ではこの仕組みを ファイル差し替えと呼びます:

  • %LocalAppData%\JetChecker\display\ に同名ファイルを置くと、 内蔵版の代わりにそのファイルが配信されます(プログラム変更・ 再インストール不要。反映はブラウザのリロードだけ)
  • ファイルを消せば内蔵版に自動で戻ります(失敗しても復旧が簡単)
  • この規則自体は互換性の約束の対象です

内蔵雛形は参考実装として、そのままコピーして編集の起点にできます。

URL パラメータ: ?event_id=<ULID> = そのイベントの固定表示 (自動更新なし)/ ?mode=counter|live = 雛形の用途切り替え。

9.3 雛形規約 v1(独自雛形の書き方)

雛形は HTML/CSS だけで書けます。ポーリング・状態判定・エスケープは すべて同梱ランタイム /display/jetdisplay.js が担います(雛形から読み込むだけ)。 語彙は以下です(規約 v1。追加のみ行い、変更しません):

表9-2: 雛形規約 v1 の語彙
属性場所意味
data-jc="<パス>"任意要素スナップショット中の値を要素のテキストとして差し込み
data-jc-format="number"data-jc と併用数値なら3桁区切りに整形
data-jc-format="datetime"data-jc と併用ISO 8601 を 2026/07/30 14:26:38 表記に整形
data-jc-show="count transaction idle"任意要素列挙した状態のときだけ表示(スペース区切り)
data-jc-mode="counter|live"bodylive = 取引 push を無視(バックヤード用)
data-jc-ignore-types="closing"body列挙した type の push を無視(スペース区切り)
data-jc-idle-seconds="180"body最終更新から N 秒で待機表示(0=無効。既定180)

パスの名前空間:

  • count.seq / count.event_id / count.received_at / count.event.<ドットパス>(計数イベント。event 以下は イベントスキーマ v2 をそのまま辿る。配列は数字インデックス)
  • tx.type / tx.posted_at / tx.fields.<キー> / tx.lines(lines は改行結合。雛形側は white-space: pre-wrap で組むと桁揃えが崩れません)
  • 解決できないパス・未知の data-jc-* 属性は無視されます (前方互換)

ランタイムは <html data-jc-state="count|transaction|idle"> を常時更新する ため、状態別の見た目は CSS だけで書けます (例: [data-jc-state="idle"] .slip { display: none })。

宣言的語彙で足りない高度な雛形向けに、更新のたびに document へ CustomEvent jc:update(detail = {state, snapshot})が 発火します。これを購読して自前 JS で描画しても構いません(その場合も表示ページから 書き込み API は叩けません — Origin 拒否)。

既定雛形で図9-1〜図9-4 と同じ画面を出す push 例

既定(店頭用)雛形は push された内容を次の規則で描画します(現行の参考実装の 挙動です。内蔵雛形のデザインは約束対象外=将来調整され得ます):

  • fields.title → 左パネルの見出し。「 / 」区切りで 日本語行+英語行の2行になる(区切りが無ければ1行)
  • lines → 右パネルに上から並ぶ。空文字列の行は詰められる
  • 半角スペース2つ以上を含む行は「項目名+値」の2列になり、 値が大きく強調される(例: "お渡し金額 ¥46,945" — 間のスペースが2つ)。含まない行はそのまま1行表示

図9-2(見積もり)を出す push:

{
  "client": "my-app",
  "type": "quote",
  "fields": {"title": "両替 お見積もり"},
  "lines": ["両替 お見積もり", "", "USD 300.00", "RATE 157.15",
            "手数料 ¥200", "", "お渡し金額  ¥46,945"]
}

図9-3(両替取引の確定)を出す push(type を変え、 refs で計数イベントに紐付ける。refs は画面には出ません — 記録・突合用):

{
  "client": "my-app",
  "type": "exchange",
  "refs": {"transaction_id": "X20260811-0001",
           "events": [{"role": "in", "event_id": "01K..."}]},
  "fields": {"title": "両替計算書"},
  "lines": ["両替計算書", "", "USD 300.00", "RATE 157.15",
            "手数料 ¥200", "", "お渡し金額  ¥46,945"]
}

図9-4(待機)に戻す push:

{"client": "my-app", "type": "idle"}

図9-1(計数結果)だけは push ではなくイベント受信で出ます — 計数機で数えた時点で自動表示され、API 発行は不要です。次のイベント(エンベロープ)を 受信したとき、既定雛形は図9-1 の画面を描画します:

{"seq":21,"event_id":"01KZ7VPDKM2YNCMFBDNDNCKW45","received_at":"2026-08-05T11:23:00+09:00",
 "event":{"schema_version":2,"device_id":"JC-54155C","receipt_no":"SIM104","user_id":"User1",
   "machine_sn":"SIM","timestamp":"2000-01-02T10:16:00+09:00","currency":"JPY","mode":"value",
   "denominations":[{"denom":10000,"qty":2,"value":20000},{"denom":5000,"qty":1,"value":5000},
                    {"denom":1000,"qty":4,"value":4000}],
   "total_count":7,"total_value":29000,"verified":true}}

画面との対応: currency → 「日本円 / JPY」(通貨名は雛形側の対応表)、 total_value → 合計金額、total_count → 合計枚数、 denominations → 金種別内訳、seqreceived_at → 左下の「No.21 ・ 2026/08/05 11:23:00」。timestamp(機体時計。この例は 電池切れリセットで 2000年)は表示に使われません — エンベロープの received_at が使われる実例です。画面確認には固定表示 URL /display?event_id=<ULID> が使えます(エンドポイント8)。

既定雛形は /display/logo.png を参照します(待機画面の中央=図9-4 の位置と、 計数/取引画面の右下のマーク)。ロゴ画像は製品に同梱されません — 未設置の間、その場所には何も表示されません(待機画面は挨拶文のみ)。 自社ロゴを表示するには:

  • %LocalAppData%\JetChecker\display\logo.png に画像を置く (雛形のファイル差し替えと同じ規則。消せば非表示に戻ります。 反映はブラウザのリロードのみ)
  • 推奨: 横長のロゴタイプ・背景透過 PNG・幅 600〜1000px 程度。 表示は画面サイズに応じて自動縮小されるため、 解像度よりも横長の縦横比が収まりを決めます
  • 既定雛形はライト基調の面に置くため、白背景で視認できる濃色のロゴを 推奨します

9.4 独自 type と雛形の紐づけ(type 別の出し分け)

雛形が type ごとに選ばれる仕組みはありません。開いている1枚の雛形に すべての push が流れ込み、雛形側が type を見て出し分けます。 手段は3つ:

  1. 除外: data-jc-ignore-types="closing maintenance" — 列挙した type の push をそのページでは無視する(宣言的)
  2. type 値の表示: data-jc="tx.type" — type 文字列そのものを差し込む
  3. type 別のレイアウト・配色切り替え: jc:update を購読して CSS クラスをトグルする(数行の JS)。同梱の店頭用雛形の quote 出し分け (配色変更+「未確定」注記。図9-2)もこの方式です:
<section id="tx" data-jc-show="transaction">...</section>
<script src="/display/jetdisplay.js"></script>
<script>
  document.addEventListener("jc:update", function (e) {
    var st = e.detail.snapshot.state; // 直近の push(未 push 時は null)
    var isQuote = !!st && st.type === "quote";
    document.getElementById("tx").classList.toggle("tx--quote", isQuote);
  });
</script>
.tx--quote .heading { color: #a06000; }   /* 見積もり時だけ配色を変える */
.tx--quote .note    { display: block; }   /* 「未確定」注記を出す */

補足: クラスの付け外しでできることは配色変更に限りません。既定雛形の quote は 「版面の構成は共通のまま、配色・注記・一部要素の表示だけ切り替える」という 軽い使い方の例ですが、type 専用の版面ブロック(<section> 等)を 複数用意してクラスで display を切り替えれば、type ごとに 全く別のレイアウトにもできます。jc:update の中は自由な JS なので、 type ごとに描画処理を分岐しても構いません。

独自 type(例: depositreceipt-check)を送る場合も 同じパターンで、st.type の比較値を変えるだけです。判定に使える情報は state の全フィールド(type / fields / lines / refs)なので、type 以外(例: fields の 特定キーの有無)で出し分けることもできます。

実例: 全画面お知らせ画面(type: "notice")を追加する

「type ごとに全く別のレイアウト」の実例として、店頭表示に1ペイン全画面・ オレンジ系グラデーション背景・白抜き中央文字のお知らせ画面(お客様への案内の掲示。 図9-5)を追加する手順です。

修正するファイル: %LocalAppData%\JetChecker\display\counter.html (内蔵の店頭用雛形をコピーして配置=ファイル差し替え。内蔵版の入手は、 ブラウザで /display を開いて「ページのソースを保存」)。 追加は次の3点です。

① HTML — </main> の直後 (<script src="/display/jetdisplay.js"> の前)に お知らせ用のセクションを追加:

<section class="notice">
  <div class="notice-lines" data-jc="tx.lines"></div>
</section>

② CSS — </style> の直前に追加(全画面固定・中央寄せ・ グラデーション。notice 表示中は既存の2ペイン(main)と ヘッダーを隠す):

.notice { display: none; position: fixed; inset: 0;
          align-items: center; justify-content: center; text-align: center;
          background: linear-gradient(135deg, #f7a021, #e2571b); color: #fff; }
.notice-lines { font-size: 6vmin; font-weight: 700; line-height: 2;
                white-space: pre-wrap; }
body.is-notice .notice { display: flex; }
body.is-notice main, body.is-notice header { display: none; }

③ JS — 雛形末尾の <script> 内に追加(type が notice のとき is-notice クラスを付ける):

document.addEventListener("jc:update", function (e) {
  var st = e.detail.snapshot.state;
  document.body.classList.toggle("is-notice", !!st && st.type === "notice");
});

push 例(図9-5 になります。data-jc="tx.lines" は 改行結合なので lines の1要素=画面の1行):

{
  "client": "my-app",
  "type": "notice",
  "lines": ["MONEY EXCHANGE JETCHECKER", "受け取った現金はその場でご確認ください。"]
}
全画面お知らせ画面(type: notice)の表示例
図9-5 — 独自 type notice の全画面お知らせ。 上記の雛形3点追加+push 例の実行結果(実画面)

この実装の持続性: 上記の JS は表示の状態機械 (count/transaction/idle)を見ずに「最後の push の typenotice か」だけで判定しているため、計数イベントが入っても 自動待機時間が過ぎても消えません。消えるのは別の type (idle = 通常運用へ、exchange 等)を push したときと、 JETCHECKER LINK の再起動時(表示状態は非永続=9.6)だけです。

「特定のディスプレイに常時掲示」の用途には push を使わない: 表示状態は全ディスプレイ共通の1スロットなので、quote/exchange を push する営業運用と並行して notice を保持することはできません(exchange を push した時点で上書きされます)。例えば「4枚目のモニターには営業案内を 出しっぱなしにしたい」場合は、push で運ぶのではなく内容を直接書いた 静的ページ%LocalAppData%\JetChecker\display\notice.html 等の別名で置き、そのディスプレイでは /display/notice.html を 開いてください(表示ページは別 URL を何枚でも開けます。内容の変更= ファイル編集+リロード)。内容を API で更新し続けたい場合は、雛形側で notice push を localStorage に記憶して出し続ける(以後の push に 影響されない)カスタム JS パターンも組めます。

9.5 API 発行シナリオ(表示内容が出るまでの流れ)

表示系はどれも「事前に何かを送っておく → 表示ページが追従する」構造です。 push から表示反映までは約1秒(表示ページのポーリング間隔)です。

準備(全シナリオ共通): ディスプレイに表示ページを開いておきます (セットアップ手順書参照)。何も送っていない初期状態は待機画面(図9-4)です。

シナリオ1: 計数結果を表示する — API 発行不要

  1. お客様の紙幣を計数機で数える
  2. イベントが JETCHECKER LINK に届いた時点で、表示ページが自動的に 計数結果(count。図9-1)を表示します

自社システムがイベントを取り込んでいなくても表示されます(表示と取り込みは独立)。

シナリオ2: 見積もり → 両替取引の確定(フルフロー)

  1. /v1/status をポーリングし、events.max_seq の増加で計数を検知
  2. GET /v1/events?client=<自社ID> で計数イベントを取得 (event_id を控える)
  3. 自社側でレート・手数料から見積もりを計算
  4. POST /v1/display/statetype: "quote"fields.titlelines に整形済みの見積もり内容 → 店頭に「未確定」注記付きで表示される(図9-2)
  5. お客様と合意 → 自社側で取引を確定
  6. POST /v1/display/statetype: "exchange"refs.events に手順2の event_idlines に 確定内容 → 店頭が確定表示に変わる(図9-3)
  7. POST /v1/clients/<自社ID>/cursor で取込を確定
  8. 接客終了。type: "idle" を push して消すか、自動待機(既定180秒)に任せる

見積もり(手順4)は任意の中間段階です。確定表示だけでよければ手順4〜5 を 飛ばして exchange を直接 push できますし、逆に提示のみで不成立に終わった 場合は idle を push して(または自動待機で)終わります。

シナリオ3: 表示を明示的にクリアする

POST /v1/display/state{"client": "...", "type": "idle"} を送ります。 次の計数または push まで待機画面になります。

シナリオ4: レートボード

エンドポイント5 でレートを POST するだけです(表示専用の API 発行は 不要)。ボードには約10秒以内に反映されます。ヘッダー・表示順は エンドポイント11 で設定できます(版面の独自化は エンドポイント9 の差し替え方式)。

9.6 注意事項(表示系)

  • 取引表示中に新しい計数が届くと、表示は計数結果に切り替わります(後勝ち)。 1台のディスプレイを接客順に使い回すための意図した挙動です。見積もり提示中に 数え直しが発生した場合は、再計算して quote を push し直してください
  • 表示状態は保存されません。JETCHECKER LINK の再起動後は最新計数または 待機に戻ります。取引表示を復元したい場合は再 push してください
  • 自動待機のタイムアウトは既定180秒です。長い接客で見積もりを 出し続けたい場合は、同じ内容を再 push するか、独自雛形で data-jc-idle-seconds を調整してください
  • 表示系はイベントの取り込み位置(カーソル)に影響しません。 表示のために /v1/events を消費する必要はなく、逆に表示だけ使って イベント取込をしない構成も可能です
  • 複数の書き込み元が併存する場合も後勝ちです。JETCHECKER LINK 同梱の 業務画面(取引確定時に表示 push をする)と自社システムを併用する場合は、 表示の主導権をどちらに置くか運用で決めてください(API 連携が主なら 同梱業務画面を接客に使わない、が推奨構成です)
  • 表示ページは構造的に読み取り専用です(書き込み API はブラウザの Origin 検査で拒否)。お客様の目に触れる端末で開いても、そこから データを変更される経路はありません

10. 印刷ガイド(ESC/POS 透過)

エンドポイント12(印刷)13(ジョブ状態)の 前提と、自前の伝票の作り方です。

10.1 できること・前提(はじめに必ずお読みください)

  • POST /v1/print/raw完成済みの ESC/POS バイト列を渡すと、JETCHECKER LINK が接続ユニット経由でプリンターへそのまま送って印字します。
  • プリンターはシリアル接続モデルのみ対応です。印字データは PC からではなく接続ユニットからプリンターへ送られます。PC に繋いだ USB プリンター・ネットワークプリンター・Windows のプリンタードライバー経由の印刷はできません。
  • 対象プリンターは ESC/POS 準拠のシリアル接続サーマルプリンターです。プリンター本体は店舗様の既存資産を使う前提のため、機種ごとのコマンド対応差・印字結果の妥当性はクライアント(アプリ)側の責任になります。ESC/POS コマンドの正はお使いのプリンターのマニュアルです(本章に、当社が実機で確認済みの範囲を参考として載せています)。
  • JETSCAN 系計数機の計数結果を紙にする用途に限らず、任意の内容を印字できます。計数イベントとの紐づけ(引用申告)も任意でできます(エンドポイント12refs)。
  • JETCHECKER LINK 内部には、同梱の業務画面などが使う「text 経路」(テキスト行を渡すと JETCHECKER LINK が ESC/POS へ変換する)もありますが、外部開発者向けに公開するのは raw 経路のみです(text 経路は内部扱い=互換性の約束の対象外)。raw は「バイトをそのまま送る」ことだけを約束する経路です。

10.2 プリンター情報の読み方 — /v1/statusprinter

店舗に設置されたプリンターの情報は、店舗側が設置時のテスト印字で確定し、 GET /v1/status(認証不要)の printer 節で読めます:

"printer": { "kanji": true, "cutter": true, "cut": "part", "columns": 32, "baud": 9600,
             "verified_at": "2026-08-31T10:12:00+09:00" }
表10-1: printer 節の項目
項目意味raw で刷るときの使い方
kanji漢字(Shift-JIS)フォント搭載か(null=未確定 / true / false)false の機体に日本語を送らない(化け文字が連続印字されます)。false なら英語のみで組む
cutterオートカッター搭載かtrue ならジョブ末尾にカット命令、false なら紙送り(LF×5 程度)で代替(10.3)
cut店舗が選んだカット方式(part=ハーフカット / full=全カット。cutter: true のときのみ意味)店舗の好みに合わせるなら partGS V 66 0 / fullGS V 65 0 を出し分ける(合わせなくても動作はします)
columns1行の半角桁数(58mm→32 / 80mm→42 が目安)組版の桁数 W に使う(10.3)。未設定ならクライアント側の設定を使う
baudプリンターとの通信速度クライアントは意識不要(接続ユニットが吸収します)

raw 経路は透過のため、これらの吸収(カット代替・言語切替)は自動では行われませんprinter 節を読んでクライアントアプリ側で出し分けてください。 上記以外の printer 項目は内部扱いです(未知の項目は無視してください)。

10.3 組版ガイド(印字幅・最小構成・コマンド早見)

サーマルプリンターの紙幅は 58mm と 80mm が主流です。1行に入る文字数の目安:

表10-2: 印字幅と文字数
紙幅印字ドット幅半角(英数字)全角(漢字・かな)
58mm384 ドット32桁16文字
80mm576 ドット42桁(機種により 48桁 等)21文字(同 24文字)
  • 全角文字は半角2桁ぶんとして数えます(半角カナは1桁)。
  • 80mm はドット数・フォント設定の機種差が大きいため、導入時に試し刷りで実桁数を確定してください。店舗側で確定済みなら /v1/statusprinter.columns で読めます。
  • 桁数の計算は「Shift-JIS エンコード後のバイト数=表示幅」と覚えると簡単です(半角=1バイト=1桁、全角=2バイト=2桁。同梱伝票もこの方式です)。
  • 1行の桁数を超えた分は勝手に折り返されて読みにくくなります。すべての行を桁数 W 以内に収めるのが原則です。

最小構成のバイト列(同梱伝票と同じ構成。この順で data を組んでください):

(1) 先頭(日本語を使う場合の必須プロローグ)
    FS &      1C 26        漢字モード ON
    FS C 1    1C 43 01     漢字コード = Shift-JIS
    ESC R 8   1B 52 08     国際文字セット = 日本(0x5C が ¥ で印字される)

(2) 本文
    各行 = Shift-JIS エンコードしたテキスト + LF(0A)

(3) 末尾
    LF LF                  余白(2行)
    GS V 66 0  1D 56 42 00 紙送り付きパーシャルカット
    ※ カッター無し機(printer.cutter=false)ではカットの代わりに LF×5
  • (1) のプロローグは省略しないでください。多くの機種は漢字コードの既定が JIS のため、FS C 1 を送り忘れると全文が化けます(当社実測: ハイフンの連続が「⑬」の連打になる等)。プリンターの既定設定に依存せず、毎ジョブ自分で指定するのが raw の鉄則です。
  • 初期化(ESC @)は不要です(raw では先頭の発行元スタンプ印字時に接続ユニットが送出済み)。
  • 円記号は文字列上は半角バックスラッシュ(0x5C)で書きます(\1,000)。(1) の ESC R 8 により紙の上では ¥ になります。Unicode の ¥(U+00A5)は Shift-JIS に無いので使わないでください。
  • 絵文字など Shift-JIS に無い文字は印字できません(エンコード時に除外してください)。

ESC/POS コマンド早見(当社実機確認分。透過経路のため使えるコマンドはプリンター次第です):

表10-3: ESC/POS コマンド早見
コマンドバイト列用途注意
漢字モード ON1C 26プロローグ
漢字コード Shift-JIS1C 43 01プロローグ忘れると全文化け
国際文字セット 日本1B 52 080x5C → ¥
白黒反転 ON/OFF1D 42 01 / 1D 42 00タイトル帯非対応機あり。対応機でも漢字には効かない個体あり(ASCII・スペースのみ反転)。反転に意味を持たせない装飾に留める
中央揃え ON/OFF1B 61 01 / 1B 61 00ロゴ等テキストはスペース詰めのセンタリングが確実
紙送り(n ドット)1B 4A nn余白
紙送り付きパーシャルカット1D 56 42 00ジョブ末尾カッター無し機は無視した実測あり(ただし printer.cutter を読んで出し分けるのが確実)

組版のコツ(同梱伝票の流儀):

表10-4: 組版のコツ
要素ルール
罫線-= を W 個並べる
店名・タイトルスペース詰めでセンタリング
ラベル+金額ラベル左寄せ、金額を W 桁目に右詰め
金種内訳額面 x 枚数 小計 の3列。列位置は W から逆算して右詰め
数値3桁カンマ区切り。小数桁は通貨の最小単位(JPY=0桁、USD=2桁 等)
日時YYYY/MM/DD HH:MM。PC 時刻を使う

印字イメージ(32桁。1行目は接続ユニットが自動で付ける発行元スタンプです。 raw では先頭固定 — 同梱伝票では末尾に付くのと位置が異なります。 理由はエンドポイント12「発行元スタンプ」参照):

[JC-54155C job=1070]
        ○○両替ショップ
--------------------------------
受信 2026/08/31 14:26:40
店舗 S01          機器 JC-54155C
--------------------------------
━━━━  計 数 結 果  ━━━━
--------------------------------
通貨 JPY
金種           枚数         小計
    10,000 x      2       20,000
     1,000 x      3        3,000
--------------------------------
合計        5 枚         \23,000
--------------------------------

10.4 実装サンプル(Python)

import base64, json, time, urllib.request

# connector.json(%LocalAppData%\JetChecker\connector.json)から読む
PORT  = 17434        # http.last_port
TOKEN = "xxxxxxxx"   # http.print_token

ESC, FS, GS = b"\x1b", b"\x1c", b"\x1d"
W = 32                                   # 印字桁数(58mm。printer.columns 推奨)

def sj(s):                               # Shift-JIS 化(表示幅=バイト数)
    return s.encode("shift_jis")

def line(s=""):
    return sj(s) + b"\n"

def lr(label, value):                    # ラベル左寄せ+右詰め
    pad = W - len(sj(label)) - len(sj(value))
    return line(label + " " * max(pad, 1) + value)

body  = FS + b"&" + FS + b"C\x01" + ESC + b"R\x08"   # プロローグ(10.3)
body += line("        サンプル伝票")
body += line("-" * W)
body += lr("合計", "\\23,000")                        # \ は紙の上で ¥
body += line("-" * W)
body += b"\n\n" + GS + b"V\x42\x00"                  # 余白+カット

def call(path, payload=None):
    req = urllib.request.Request(
        f"http://127.0.0.1:{PORT}{path}",
        data=None if payload is None else json.dumps(payload).encode(),
        headers={"Content-Type": "application/json",
                 "Authorization": f"Bearer {TOKEN}"})
    with urllib.request.urlopen(req) as r:
        return json.load(r)

job = call("/v1/print/raw", {
    "client": "my-app",
    "slip_type": "sample",
    "data": base64.b64encode(body).decode(),
})
while True:                              # 1秒ポーリングで十分
    st = call(f"/v1/print/jobs/{job['job_id']}")
    if st["state"] in ("done", "failed"):
        break
    time.sleep(1)
print(st["state"], st.get("reason"))

10.5 注意事項(印刷系)

  • done は印刷完了の保証ではありません(エンドポイント13)。紙切れ・紙詰まり・プリンター側のボーレート不一致では、done でも紙が出ない/化けることがあります。導入時は必ず実機で試し刷りしてください。
  • プリンターの処理能力は保証外です。コマンド処理の重いバイト列(例: 初期化 ESC @ の連打)は、プリンター内部バッファが溢れて文字が欠落することがあります(当社実測あり。通常のテキスト主体の伝票では起きません)。
  • 反転(GS B)はベストエフォートです(10.3)。反転が伝票の意味を担わない組版にしてください。
  • 発行元スタンプ(エンドポイント12)の1行は消せません。raw では先頭に付くので、スタンプの直後から自分の版面が始まる前提でデザインしてください(同梱伝票の末尾スタンプとは位置が異なります=意図的な違い。エンドポイント12参照)。
  • 店舗のプリンターは任意機種です。特定機種の拡張コマンド(バーコード・画像等)を使う場合は、対象店舗のプリンターでの動作確認をクライアント側で行ってください。

11. エラー応答

すべて {"error": "メッセージ"} の JSON です(書き込み系は機械可読の code 付き)。400 = パラメータ・ボディ不正、401 = トークン不一致、 403 = オリジン拒否、404 = 対象なし(単一イベント取得・印刷ジョブ状態)、 405 = メソッド不正、415 = Content-Type 不正、500 = 内部エラー、 503 = 印刷不可(接続ユニット未接続 printer_unavailable・ 待ち行列満杯 queue_fullエンドポイント12参照)。

12. 実装ガイド

推奨する取り込みループ:

  1. /v1/status をポーリングし、events.max_seq が 前回より増えたら次へ
  2. GET /v1/events?client=<自社ID> で未取込を取得
  3. 各行を処理する。処理は event_id で冪等に (同じ event_id を二度受け取っても二重登録しない)。 in_window: false の行は業務データとして採用しない
  4. 処理が完了した最大の seqPOST .../cursor を呼ぶ

注意点:

  • 手順 3 と 4 の間で障害が起きると、次回同じイベントを再受信します。 これは仕様(at-least-once)であり、3 の冪等処理が前提です
  • verified: falseraw 付きの行も配信されます。 除外するかどうかは受信側の業務判断です(データとしては保存されています)
  • 接続に失敗した場合は JETCHECKER LINK が停止しているか、ポートが 変わっています。connector.json の http.last_port を読み直してください
  • 巻き戻し(cursor を小さくする)で任意の時点からの再処理が可能です
  • レート設定は自社システム側のレート確定タイミングで都度 POST してください (差分計算は不要。同じ通貨への POST は常に上書きです)
  • ディスプレイ表示の組み込みは9.5「API 発行シナリオ」を 参照してください(表示は取り込みループとは独立に動きます)

13. 動作確認例

curl http://127.0.0.1:17434/v1/status
curl "http://127.0.0.1:17434/v1/events?client=my-app"
curl -X POST -H "Content-Type: application/json" -d "{\"cursor_seq\":141}" http://127.0.0.1:17434/v1/clients/my-app/cursor
curl http://127.0.0.1:17434/v1/rates
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer <トークン>" ^
  -d "{\"actor\":\"担当者\",\"currency\":\"USD\",\"we_sell\":\"159.24\",\"we_buy\":\"157.15\"}" ^
  http://127.0.0.1:17434/v1/rates
curl http://127.0.0.1:17434/v1/display/snapshot
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer <トークン>" ^
  -d "{\"client\":\"my-app\",\"type\":\"quote\",\"fields\":{\"title\":\"お見積もり\"},\"lines\":[\"USD 300.00\",\"RATE 152.30\",\"¥45,000\"]}" ^
  http://127.0.0.1:17434/v1/display/state
curl http://127.0.0.1:17434/v1/display/rates
curl http://127.0.0.1:17434/v1/display/rateboard
curl -X POST -H "Content-Type: application/json" -H "Authorization: Bearer <トークン>" ^
  -d "{\"actor\":\"担当者\",\"header_lines\":[\"JetChecker 両替店\"],\"max_columns\":4}" ^
  http://127.0.0.1:17434/v1/display/rateboard

PowerShell:

$cfg  = Get-Content "$env:LocalAppData\JetChecker\connector.json" | ConvertFrom-Json
$port = $cfg.http.last_port
Invoke-RestMethod "http://127.0.0.1:$port/v1/status"
Invoke-RestMethod "http://127.0.0.1:$port/v1/events?client=my-app"
Invoke-RestMethod -Method Post -ContentType 'application/json' -Body '{"cursor_seq":141}' "http://127.0.0.1:$port/v1/clients/my-app/cursor"
Invoke-RestMethod "http://127.0.0.1:$port/v1/rates"
Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Headers @{Authorization = "Bearer $($cfg.http.print_token)"} `
  -Body '{"actor":"担当者","currency":"USD","we_sell":"159.24","we_buy":"157.15"}' `
  "http://127.0.0.1:$port/v1/rates"
Invoke-RestMethod "http://127.0.0.1:$port/v1/display/snapshot"
Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Headers @{Authorization = "Bearer $($cfg.http.print_token)"} `
  -Body '{"client":"my-app","type":"quote","fields":{"title":"お見積もり"},"lines":["USD 300.00","RATE 152.30","¥45,000"]}' `
  "http://127.0.0.1:$port/v1/display/state"
Invoke-RestMethod "http://127.0.0.1:$port/v1/display/rates"
Invoke-RestMethod "http://127.0.0.1:$port/v1/display/rateboard"
Invoke-RestMethod -Method Post -ContentType 'application/json' `
  -Headers @{Authorization = "Bearer $($cfg.http.print_token)"} `
  -Body '{"actor":"担当者","header_lines":["JetChecker 両替店"],"max_columns":4}' `
  "http://127.0.0.1:$port/v1/display/rateboard"

14. 内部扱いの機能(依存しないでください)

以下は本製品の内部で使用しているもので、互換性の約束の対象外です:

  • GET /v1/eventsformat パラメータ(CSV 表現。 付属 Excel テンプレート専用)
  • クライアントID excel / csv / diag-vba (付属機能が使用中のため使わないこと。webappjc-* は 400 で拒否されます — 4章参照)
  • connector.json の http.last_porthttp.print_token 以外の項目
  • 印刷 API のうち /v1/print/raw/v1/print/jobs/{job_id} 以外(テキスト変換経路 /v1/print/text・テスト印字等。 raw とジョブ状態はエンドポイント1213)・ 業務画面(/app)・ /v1/webapp/* 全体(レート API の公開パスは /v1/rates です。/v1/webapp/rates は同梱業務画面用の 内部パスで、同じ動作をしますが互換性の約束は /v1/rates のみ)
  • 内蔵雛形(店頭表示・バックヤード・レートボード)の HTML/CSS の中身・ デザイン・版面(9.2参照。差し替え規則と 雛形規約 v1 は約束、内蔵雛形そのものは参考実装)