メトリクス (Metrics/Alerts)
ダッシュボード指標・アラート。
GET/api/metrics/overview公開live 200
Headline KPI cards computed from this workspace’s own records. A figure that cannot be computed is returned with computed:false, a reason and display:"—" — never a substituted or sample number; deltaPct is null when there is no prior month to compare against.
レスポンス例
{"ok":true,"month":"2026-08","revenueForecast":{"display":"¥0.00","unit":"M","total":0,"oneTime":0,"recurring":0,"deltaPct":null,"computed":true},"mrr":{"display":"¥0","unit":"K","amount":0,"invoiceRecurring":0,"projected":null,"computed":true},"pipeline":{"display":"—","unit":"M","amount":null,"deals":0,"computed":false,"reason":"商談金額のレコードがありません"}}
GET/api/revenue/mix公開live 200
12-month revenue-mix series (months labels, recurring, onetime 万円 arrays) for #revChart; optional ?months=N trims to last N points.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
months | query | integer | | Return only the last N points. Must be 1..120 or a 400 is returned. Default = full 12-point series. |
レスポンス例
{"ok":true,"months":["25/8","25/9","25/10","25/11","25/12","26/1","26/2","26/3","26/4","26/5","26/6","26/7"],"recurring":[38,40,42,46,46,48,50,50,93,93,140,140],"onetime":[22,65,18,120,34,52,88,93,318,282,60,72]}
GET/api/ops/today公開live 200
Today's operation rows (#todayList): id/time/chip/chipLabel/title/detail/status/statusColor, clipped to length limits.
レスポンス例
{"ok":true,"ops":[{"id":"op-001","time":"09:30","chip":"c-cyan","chipLabel":"作業","title":"北斗商事 交換12台 再キッティング(Intune再登録)","detail":"担当:一条 | Zone A | チェックリスト 8/12 完了","status":"進行中","statusColor":"var(--cyan)"}]}
GET/api/alerts公開live 200
Alert list (#alertList) with per-alert action (label/danger/toast) plus lateCount = number of unacked late alerts.
レスポンス例
{"ok":true,"alerts":[{"id":"alert-001","label":"出荷漏れ","late":true,"ack":false,"chip":"c-red","chipLabel":"重大","what":"北斗商事 交換2台 — 予定 07/10 を超過","detail":"...","action":{"label":"対応","danger":true,"toast":"出荷漏れアラートを対応済みとして記録しました"}}],"lateCount":1}
POST/api/alert/:id/ack公開
Mark one alert acknowledged (sets ack=true, acked=timestamp) and returns the updated alert.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Alert id from metrics-alerts seed, e.g. 'alert-001'. 404 {ok:false,error:'alert not found'} if unknown. |
レスポンス例
{"ok":true,"alert":{"id":"alert-001","label":"出荷漏れ","ack":true,"acked":1710000000000,...}} 端末台帳 (Devices)
IMEI単位の端末台帳 CRUD・検索・一括操作。
GET/api/devices公開live 200
List all devices in the ledger collection (seeded with 178 mulberry(42)-generated records keyed by imei).
レスポンス例
{"ok":true,"devices":[{"imei":"356247446539346","serial":"F2LXD73EB4","asset":"HK-26-1001","model":"iPhone 15","maker":"Apple","cust":"北斗商事","mdm":"Intune","status":"保管中","chip":"c-blue",...}],"count":178}
POST/api/device公開
Create one device from partial input; generates a fresh unique imei if absent, fills defaults via makeDevice.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
imei | body | string | | Device IMEI (max 40 chars). If omitted, a fresh 35+13-digit imei is generated. |
model | body | string | | Device model (max 60), default 'iPhone 16'. iPad-* switches OS defaults. |
cust | body | string | | Customer (max 60), default '社内検証機'. |
status | body | string | | Status (max 20), default '入荷待ち'; sets chip color via chipFor. |
mdm | body | string | | MDM platform (max 40), default 'Intune'. |
serial | body | string | | Serial (max 40); defaulted from model prefix + padded index. |
asset | body | string | | Asset tag (max 40), default 'HK-26-<n>'. |
bat | body | number | | Battery %, default 100 (passed through, not clipped). |
note | body | string | | Free note (max 300). |
リクエスト
{"imei?":"str","model?":"str","cust?":"str","status?":"str","mdm?":"str","serial?":"str","asset?":"str","bat?":100,"...other device fields":"str"}レスポンス例
{"ok":true,"device":{"imei":"35...","model":"iPhone 16","cust":"社内検証機","status":"入荷待ち","chip":"c-gray",...}}
POST/api/devices/bulk公開
Create N devices from {model,cust,mdm,status,qty}; returns the created[] array.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
qty | body | number | ○ | Count to create; clamped to 1..1000. Missing/0 -> 400. |
model | body | string | | Model applied to all created devices. |
cust | body | string | | Customer applied to all created devices. |
mdm | body | string | | MDM applied to all created devices. |
status | body | string | | Status applied to all created devices. |
リクエスト
{"qty":5,"model?":"iPhone 16","cust?":"北斗商事","mdm?":"Intune","status?":"保管中"}レスポンス例
{"ok":true,"created":[{"imei":"35...","model":"iPhone 16",...}],"count":5}
POST/api/device/:imei公開
Update fields of an existing device by imei; imei key is immutable, bat passed raw, other fields clipped, chip recomputed when status changes.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
imei | path | string | ○ | IMEI of the device to update (e.g. seed value 356247446539346). |
status | body | string | | New status; triggers chip color recompute via chipFor. |
bat | body | number | | Battery %, assigned raw (not clipped). |
note | body | string | | Note (clipped to 300); all other string fields clipped to 120. |
リクエスト
{"status?":"作業中","loc?":"B-05","user?":"藤宮 健","bat?":88,"note?":"str"}レスポンス例
{"ok":true,"device":{"imei":"356247446539346","status":"作業中","chip":"c-cyan",...}}
POST/api/device/:imei/delete公開
Remove one device by imei from the collection.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
imei | path | string | ○ | IMEI of the device to delete (e.g. seed value 356247446539346). |
レスポンス例
{"ok":true,"imei":"356247446539346"} 案件管理 (Jobs)
8段階の案件カード(キッティング工程)。
GET/api/jobs公開live 200
List all job (kanban) cards from the in-memory jobs collection.
レスポンス例
{"ok":true,"jobs":[{"id":"job-001","c":"東成建設","t":"PoC 20台(iPhone 16e・Intune新規)","s":0,"due":"07/22","ch":"c-blue","tag":"新規"}, ...9 cards]}
POST/api/job公開
Create a job card; server assigns id (job-…) and defaults stage to 0.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
c | body | string | | Customer/company name (clipped to 120 chars) |
t | body | string | | Task/title text (clipped to 300 chars) |
s | body | number | | Stage index, clamped 0-7; defaults to 0 if not finite |
due | body | string | | Due date label e.g. '07/22' (clipped to 40) |
ch | body | string | | Color/chip class; defaults 'c-blue' (clipped to 40) |
tag | body | string | | Tag label (clipped to 40) |
リクエスト
{"c":"string","t":"string","s":0,"due":"string","ch":"c-blue","tag":"string"}レスポンス例
{"ok":true,"job":{"id":"job-<generated>","c":"...","t":"...","s":0,"due":"...","ch":"c-blue","tag":"..."}}
POST/api/job/:id公開
Update stage/fields of an existing job card; only provided (non-null) fields are changed.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Job id e.g. 'job-001' |
c | body | string | | Customer name (clip 120); updated only if non-null |
t | body | string | | Title (clip 300); updated only if non-null |
s | body | number | | Stage, clamped 0-7; updated only if non-null |
due | body | string | | Due label (clip 40); updated only if non-null |
ch | body | string | | Color class (clip 40); updated only if non-null |
tag | body | string | | Tag (clip 40); updated only if non-null |
リクエスト
{"c":"string","t":"string","s":3,"due":"string","ch":"c-cyan","tag":"string"} (all optional)レスポンス例
{"ok":true,"job":{"id":"job-001","c":"...","t":"...","s":3,"due":"...","ch":"...","tag":"..."}}
POST/api/job/:id/delete公開
Delete a job card by id.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Job id to delete e.g. 'job-005' |
レスポンス例
{"ok":true,"id":"job-005"}
GET/api/inquiries公開
List every helpdesk inquiry in this workspace, oldest 問合せID first.
レスポンス例
{"ok":true,"inquiries":[]}
POST/api/inquiry公開
Create an inquiry; the server assigns the next sequential 問合せID and uses it as the record id.
リクエスト
{"cust":"北斗商事","title":"MDM設定"}レスポンス例
{"ok":true,"inquiry":{"status":"オープン","type":"故障","inquirerName":"","department":"","summary":"","faultyDeviceId":"","sentDeviceId":"","remarks":"","inquiryDate":"","returnDeadline":"","shipDate":"","arrivalDate":"","returnDate":"","returnReceivedDate":"","inquiryId":"00001","id":"00001"}}
POST/api/inquiry/:id公開
Update fields/status on one inquiry; inquiryId and id are preserved.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | 問合せID, e.g. '00001'. |
リクエスト
{"status":"クローズ"}レスポンス例
{"ok":true,"inquiry":{"status":"クローズ","type":"故障","inquirerName":"","department":"","summary":"","faultyDeviceId":"","sentDeviceId":"","remarks":"","inquiryDate":"","returnDeadline":"","shipDate":"","arrivalDate":"","returnDate":"","returnReceivedDate":"","inquiryId":"00001","id":"00001"}}
POST/api/inquiry/:id/delete公開
Delete one inquiry.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | 問合せID, e.g. '00001'. |
レスポンス例
{"ok":true,"id":"00001"} ヘルプデスク (Tickets)
サポートチケットと対応履歴。
GET/api/tickets公開live 200
List all support tickets from the tickets collection (seeded with 6 demo tickets TK-0667..TK-0672).
レスポンス例
{"ok":true,"tickets":[{"id":"TK-0672","cust":"北斗商事","kind":"紛失","subject":"営業車内で紛失 → 位置特定&リモートワイプ要請","owner":"一条","sla":"1h 12m","status":"対応中"}]}
POST/api/ticket公開
Create a new ticket; auto-assigns id TK-0<next num> (starts at 673) and created timestamp, clips input fields.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
cust | body | string | | Customer name (alias: customer); default '—', clipped to 120. |
kind | body | string | | Ticket type; default '問合せ', clipped to 60. |
subject | body | string | | Subject text; default '', clipped to 300. |
owner | body | string | | Assigned owner; default '—', clipped to 60. |
sla | body | string | | SLA label; default '4h 00m', clipped to 60. |
status | body | string | | Status; default '受付', clipped to 40. |
priority | body | string | | Priority; default '', clipped to 40. |
リクエスト
{"cust":"string","kind":"string","subject":"string","owner":"string","sla":"string","status":"string","priority":"string"}レスポンス例
{"ok":true,"ticket":{"id":"TK-0673","cust":"—","kind":"問合せ","subject":"","owner":"—","sla":"4h 00m","status":"受付","priority":"","created":1700000000000}}
POST/api/ticket/:id公開
Update an existing ticket's status/owner/sla/subject/cust/kind/priority (only provided non-null fields).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Ticket id, e.g. TK-0672. |
status | body | string | | New status, clipped to 40. |
owner | body | string | | New owner, clipped to 60. |
sla | body | string | | New SLA label, clipped to 60. |
subject | body | string | | New subject, clipped to 300. |
cust | body | string | | New customer, clipped to 120. |
kind | body | string | | New kind, clipped to 60. |
priority | body | string | | New priority, clipped to 40. |
リクエスト
{"status":"string","owner":"string","sla":"string","subject":"string","cust":"string","kind":"string","priority":"string"}レスポンス例
{"ok":true,"ticket":{"id":"TK-0672","status":"解決",...}}
POST/api/ticket/:id/delete公開
Delete a ticket and its associated messages.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Ticket id to delete, e.g. TK-0672. |
レスポンス例
{"ok":true}
GET/api/ticket/:id/messages公開live 200
List all messages for a given ticket (empty array if none recorded).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Ticket id, e.g. TK-0672. |
レスポンス例
{"ok":true,"messages":[]}
POST/api/ticket/:id/message公開
Append a message {from,text} to a ticket's message list; assigns msg id and timestamp.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Ticket id, e.g. TK-0672. |
from | body | string | | Sender label; default '—', clipped to 60. |
text | body | string | ○ | Message body, clipped to 2000; required (400 if empty). |
リクエスト
{"from":"string","text":"string (required)"}レスポンス例
{"ok":true,"message":{"id":"msg-xxxx","from":"綾瀬","text":"...","ts":1700000000000}} 顧客管理 (Customers)
顧客アカウント・更新・商談。
GET/api/customers公開live 200
List all customer accounts (id, name, industry, mdm, monthly, devices, since, status, note, renew).
レスポンス例
{"ok":true,"customers":[{"id":"cust-mrv8frxlg","key":"hokuto","name":"北斗商事","industry":"大手商社 | 500台(iPhone)","mdm":"Intune(Clomo→移行済)","monthly":"¥380,000","devices":"500 台","since":"2025/04","status":"運用中","statusChip":"c-green","note":"豊島倉庫 専用エリア ¥45K/月","renew":{"date":"2027-03-31",...}}]}
POST/api/customer公開
Create a new customer account; requires name; returns generated id and full record.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
name | body | string | ○ | Account name (required; 400 if missing) |
industry | body | string | | Industry / device summary line |
mdm | body | string | | MDM platform |
monthly | body | string | | Monthly fee (default '—') |
devices | body | string | | Device count (default '—') |
since | body | string | | Start month (default '—') |
status | body | string | | Status label |
statusChip | body | string | | Status chip class (default 'c-gray') |
note | body | string | | Free-text note |
key | body | string | | Short account key |
renew | body | object | | Renewal/deal object {date,dateLabel,year,kind,kindChip,what,detail,confidence,confColor} or null |
リクエスト
{"name":"...","industry":"...","mdm":"...","monthly":"...","devices":"...","since":"...","status":"...","statusChip":"...","note":"...","key":"...","renew":{...}|null}レスポンス例
{"ok":true,"id":"cust-xxxx","customer":{"id":"cust-xxxx","name":"...","industry":"","mdm":"","monthly":"—","devices":"—","since":"—","status":"","statusChip":"c-gray","note":"","renew":null}}
POST/api/customer/:id公開
Update fields of an existing customer by id; 404 if id not found.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Customer id (e.g. cust-mrv8frxlg) |
name | body | string | | Any of the shape() string fields to update |
renew | body | object | | Renewal object or null to overwrite renew |
リクエスト
{"name":"...","industry":"...","note":"...","renew":{...}|null, ...any shape fields}レスポンス例
{"ok":true,"customer":{"id":"cust-mrv8frxlg","name":"...",...}}
POST/api/customer/:id/delete公開
Delete a customer by id; 404 if not present.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Customer id to delete |
レスポンス例
{"ok":true,"id":"cust-mrv8frxlg"}
GET/api/renewals公開live 200
Renewal/deal calendar derived from customers with a renew.date; sorted ascending by date.
レスポンス例
{"ok":true,"renewals":[{"id":"ren-cust-mrv8frxli","customerId":"cust-mrv8frxli","customer":"天城製薬","date":"2026-08-31","dateLabel":"08/31","year":"2026","kind":"商談","kindChip":"c-orange","what":"最終見積回答期限","detail":"...","confidence":"確度 80%","confColor":"orange"}]} 見積 (Quote)
見積の作成・改訂・PDF元データ。
GET/api/quote/catalog公開live 200
Returns the constant INITIAL/RUNNING line-item catalog master (mirrors app.html).
レスポンス例
{"ok":true,"catalog":{"initial":[{"n":"プロジェクト管理費","q":1,"p":300000,"d":"..."}, ...],"running":[{"n":"サービス管理・月次報告","q":1,"p":125000,"d":"..."}, ...]}}
GET/api/quote/new公開
Builds an UNSAVED quote for ONE named customer from the catalog master. The device quantity comes from that customer's own record and the volume discount is RECOMPUTED from it — so it is simply absent below the 1,000台 tier. 404 customer_not_found for an unknown custId; 400 customer_required when neither custId nor cust is given.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
custId | query | string | | Customer id owned by this workspace (e.g. 'cust-mtg57z057'). Its device count drives 数量 and the volume discount. 404 when unknown. |
cust | query | string | | Customer NAME, when the quote is for a party not yet in the roster. Ignored if custId is given; one of the two is required. |
devices | query | integer | | Override the device quantity (floored at 0). Default: the customer record's own count. |
pattern | query | string | | Running-cost pattern; anything other than 'B' is read as 'A'. |
レスポンス例
{"ok":true,"quote":{"id":null,"custId":"cust-mtg57z057","cust":"三峰精機 御中","pattern":"A","costRate":45,"ini":[{"n":"プロジェクト管理費","q":1,"p":300000,"d":"..."},...,{"n":"iPhone初期キッティング(台)","q":50,"p":1500,"d":"..."}],"run":[...],"totals":{"ini":2425000,"iniTax":242500,"iniTotal":2667500,"run":470000,"runTax":47000,"runTotal":517000,"arr":5640000,"y1":8065000,"costRate":45,"gp":4435750,"gpRate":55,"deviceQty":50,"perDevice":1500,"volumeApplied":false,"volumeQty":0,"volumeMinQty":1000,"taxRate":0.1}}}エラー
400 — {"ok":false,"error":"customer_required","message":"対象顧客を指定してください。"} — 対象顧客が指定されていません
404 — {"ok":false,"error":"customer_not_found"} — custId に該当する顧客がありません
GET/api/quotes公開live 200
Lists all saved quotes, each augmented with a computed totals snapshot.
レスポンス例
{"ok":true,"quotes":[{"id":"Q-AMAGI-001","cust":"天城製薬 御中","pattern":"A","costRate":45,"ini":[...],"run":[...],"signId":null,"totals":{"ini":...,"iniTax":...,"iniTotal":...,"run":...,"arr":...,"y1":...}}]}
GET/api/quote/draft公開
Returns the in-progress quote-builder draft for this workspace with computed totals, or draft:null when nothing is in progress. One draft per workspace (key 'current'); it survives navigation.
レスポンス例
{"ok":true,"draft":{"cust":"北斗商事","custId":null,"pattern":"A","costRate":45,"ini":[{"n":"キッティング","q":100,"p":1500,"d":""}],"run":[],"updatedAt":1788114480640,"totals":{"ini":150000,"iniTax":15000,"iniTotal":165000,"run":0,"runTax":0,"runTotal":0,"arr":0,"y1":150000,"costRate":45,"gp":82500,"gpRate":55,"deviceQty":0,"perDevice":null,"volumeApplied":false,"volumeQty":0,"volumeMinQty":1000,"taxRate":0.1}}} — 未作成なら {"ok":true,"draft":null}
POST/api/quote/draft公開
Saves the in-progress quote-builder draft (overwrites the single 'current' draft). Line items are normalized and the volume discount is recomputed. If the caller also sends its own `totals`, they are checked against the recompute and a disagreeing figure is REFUSED by name with 400 quote_inconsistent — nothing is stored.
リクエスト
{"cust":"北斗商事","custId":null,"pattern":"A","costRate":45,"ini":[{"n":"キッティング","q":100,"p":1500,"d":""}],"run":[],"totals":{"ini":150000,...}} — totals は任意。付ければ再計算と照合されますレスポンス例
{"ok":true,"draft":{"cust":"北斗商事","custId":null,"pattern":"A","costRate":45,"ini":[{"n":"キッティング","q":100,"p":1500,"d":""}],"run":[],"updatedAt":1788114480640,"totals":{"ini":150000,"iniTax":15000,"iniTotal":165000,"run":0,"runTax":0,"runTotal":0,"arr":0,"y1":150000,"costRate":45,"gp":82500,"gpRate":55,"deviceQty":0,"perDevice":null,"volumeApplied":false,"volumeQty":0,"volumeMinQty":1000,"taxRate":0.1}}}エラー
400 — {"ok":false,"error":"quote_inconsistent","message":"イニシャル小計が一致しません:画面 999 / 明細からの再計算 150,000。","errors":[{"code":"figure_mismatch","field":"ini","message":"イニシャル小計が一致しません:画面 999 / 明細からの再計算 150,000。"}]} — 画面の数字と再計算が食い違う場合。保存されません
POST/api/quote/draft/clear公開
Discards the in-progress quote-builder draft for this workspace. Idempotent — answers {ok:true} whether or not a draft existed. Saved quotes are untouched.
レスポンス例
{"ok":true}
POST/api/quote/preview公開
PRE-ISSUE VALIDATION of an UNSAVED quote. Nothing is stored. On success it returns the quote with computed totals PLUS the customer-facing CSV text and the PDF source document, so screen / CSV / PDF are literally the same computation. It REFUSES with 400 quote_inconsistent — naming the offending figure — when the caller's own totals disagree with the recompute, or when the quote names a custId that does not exist in this workspace.
リクエスト
{"id":null,"cust":"北斗商事","custId":"cust-…","pattern":"A","costRate":45,"ini":[{"n":"キッティング","q":100,"p":1500,"d":""}],"run":[{"n":"管理","q":1,"p":125000,"d":""}],"totals":{...}} — totals は任意(付ければ照合されます)レスポンス例
{"ok":true,"quote":{"cust":"北斗商事","custId":null,"pattern":"A","costRate":45,"ini":[{"n":"キッティング","q":100,"p":1500,"d":""}],"run":[{"n":"管理","q":1,"p":125000,"d":""}],"id":null,"createdAt":1788114480661,"totals":{"ini":150000,"iniTax":15000,"iniTotal":165000,"run":125000,"runTax":12500,"runTotal":137500,"arr":1500000,"y1":1650000,"costRate":45,"gp":907500,"gpRate":55,"deviceQty":0,"perDevice":null,"volumeApplied":false,"volumeQty":0,"volumeMinQty":1000,"taxRate":0.1}},"csv":"\"見積番号\",\"(未保存)\"\r\n\"顧客名\",\"北斗商事\"\r\n...","document":{"meta":{...},"sections":[...],"summary":[...],"totals":{...}}}エラー
400 — {"ok":false,"error":"quote_inconsistent","message":"イニシャル小計が一致しません:画面 1 / 明細からの再計算 150,000。","errors":[{"code":"figure_mismatch","field":"ini",...}]} — 数字の不一致。顧客不明なら {"code":"customer_unknown","field":"custId","message":"見積の顧客ID(nope)に該当する顧客が存在しません。"}
GET/api/quote/:id公開live 200
Returns one saved quote by id with computed totals, or 404 if not found.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Quote id, e.g. 'Q-AMAGI-001' |
レスポンス例
{"ok":true,"quote":{"id":"Q-AMAGI-001","cust":"天城製薬 御中","pattern":"A","costRate":45,"ini":[...],"run":[...],"signId":null,"totals":{...}}}
GET/api/quote/:id/csv公開
Customer-facing CSV for one saved quote (text/csv; charset=utf-8, BOM-prefixed, Content-Disposition attachment). Header block 見積番号・顧客名・発行日・パターン・数量・ボリューム値引き, then the line items, then the summary block — the same figures as the PDF. VALIDATED FIRST: a quote whose custId no longer resolves, or whose figures disagree, is REFUSED with 400 quote_inconsistent (JSON) instead of being exported.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Saved quote id, e.g. 'Q-AMAGI-001' |
レスポンス例
"見積番号","Q-mtg57yza6"
"顧客名","北斗商事 御中"
"発行日","2026-08-31"
"パターン","A"
"数量(台)","100"
"ボリューム値引き","非適用(1,000台未満)"
"区分","項目","数量","単価","金額"
"イニシャル","iPhone初期キッティング(台)","100","1500","150000"
...
"区分","サマリー","金額"
"イニシャル","小計(税抜)","250000"
..."合計","粗利率(%)","55"
エラー
400 — {"ok":false,"error":"quote_inconsistent","message":"見積の顧客ID(cust-…)に該当する顧客が存在しません。","errors":[{"code":"customer_unknown","field":"custId",...}]}
404 — {"ok":false,"error":"not_found"}
GET/api/quote/:id/document公開
The PDF's SOURCE DATA for one saved quote — meta (見積番号/顧客名/発行日/パターン/発行済みフラグ), the イニシャル and ランニング sections with per-row amounts and subtotal lines, the summary and the totals snapshot. The client renders it; it never recomputes it. VALIDATED FIRST, exactly like the CSV: an inconsistent quote is REFUSED with 400 quote_inconsistent naming the offending figure.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Saved quote id, e.g. 'Q-AMAGI-001' |
レスポンス例
{"ok":true,"document":{"meta":{"quoteNo":"Q-mtg57yza6","cust":"北斗商事 御中","custId":"cust-mtg57yz50","issueDate":"2026-08-31","pattern":"A","issued":true},"sections":[{"key":"ini","title":"イニシャル(構築・初期キッティング)","rows":[{"n":"iPhone初期キッティング(台)","q":100,"p":1500,"amt":150000},...],"lines":[{"label":"イニシャル小計(税抜)","amount":250000},{"label":"消費税(10%)","amount":25000},{"label":"イニシャル合計(税込)","amount":275000}]},{"key":"run","title":"ランニング(月額 LCM運用)","rows":[...],"lines":[...,{"label":"年間継続収益(ARR)","amount":1500000}]}],"summary":[{"label":"初年度合計(税抜)","amount":1750000},{"label":"想定粗利","amount":962500}],"totals":{...}}}エラー
400 — {"ok":false,"error":"quote_inconsistent","message":"見積の顧客ID(cust-…)に該当する顧客が存在しません。","errors":[{"code":"customer_unknown","field":"custId",...}]}
404 — {"ok":false,"error":"not_found"}
POST/api/quote公開
Saves a new quote with a generated id (Q-...), normalizing line items and totals.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
cust | body | string | | Customer name (clipped 200 chars, defaults '御中') |
pattern | body | string | | 'A' or 'B' (defaults 'A') |
costRate | body | number | | Cost rate percent (defaults 45) |
ini | body | array | | Initial line items [{n,q,p,d}] |
run | body | array | | Running line items [{n,q,p,d}] |
signId | body | string | | Optional linked sign id (clipped 120 chars) |
リクエスト
{"cust":"...","pattern":"A","costRate":45,"ini":[{"n":"...","q":1,"p":100,"d":"..."}],"run":[...],"signId":null}レスポンス例
{"ok":true,"quote":{"id":"Q-...","cust":"...",...,"totals":{...}}}
POST/api/quote/:id公開
Updates an existing quote in place (patch semantics), or 404 if not found.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Quote id to update |
cust | body | string | | Updated customer name |
pattern | body | string | | 'A' or 'B' |
costRate | body | number | | Cost rate; falls back to current if 0/invalid |
ini | body | array | | Initial line items to replace |
run | body | array | | Running line items to replace |
signId | body | string | | Sign id to link (or null to clear) |
リクエスト
{"signId":"S-123"} (any subset of: cust,pattern,costRate,ini,run,signId)レスポンス例
{"ok":true,"quote":{"id":"Q-AMAGI-001",...,"updatedAt":...,"totals":{...}}} 請求 (Invoices/Billing)
請求書・売上サマリ。
POST/api/quote/:id/issue公開
THE PRE-ISSUE GATE. Nothing reaches a customer until 数量・単価・値引き・税・合計 agree AND the quote names a customer that exists in this workspace (a customer is REQUIRED here, unlike CSV/document). On success 発行日 is stamped ONCE (issuedAt; a re-issue keeps the first stamp) so CSV and PDF can never print different dates, and the vetted quote, document and CSV are returned together. Otherwise 400 quote_inconsistent naming the offending figure — and nothing is stamped.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Saved quote id to issue, e.g. 'Q-AMAGI-001' |
レスポンス例
{"ok":true,"quote":{"id":"Q-mtg57yza6","cust":"北斗商事 御中","custId":"cust-mtg57yz50","pattern":"A","costRate":45,"ini":[...],"run":[...],"signId":null,"issuedAt":1788114526682,"createdAt":1788114526678,"updatedAt":1788114526682,"totals":{"ini":250000,"iniTax":25000,"iniTotal":275000,"run":125000,"runTax":12500,"runTotal":137500,"arr":1500000,"y1":1750000,"costRate":45,"gp":962500,"gpRate":55,"deviceQty":100,"perDevice":1500,"volumeApplied":false,"volumeQty":0,"volumeMinQty":1000,"taxRate":0.1}},"document":{"meta":{...,"issued":true},"sections":[...],"summary":[...],"totals":{...}},"csv":"\"見積番号\",\"Q-mtg57yza6\"\r\n..."}エラー
400 — 発行拒否。顧客未選択: {"ok":false,"error":"quote_inconsistent","message":"対象顧客が選択されていません。顧客を選択してから発行してください。","errors":[{"code":"customer_required","field":"custId",...}]} | 顧客不明: {"code":"customer_unknown","field":"custId"} | 数字の不一致: {"code":"figure_mismatch","field":"ini",...}
404 — {"ok":false,"error":"not_found"}
GET/api/invoices公開live 200
List all invoices sorted newest-id first (descending localeCompare on id).
レスポンス例
{"ok":true,"invoices":[{"id":"INV-2607-004","cust":"三峰精機","month":"2607","desc":"新入社員向けキッティング 8台+送料","amount":19800,"due":"08/15","status":"draft","recurring":false}],"count":5}
POST/api/invoice公開
Create an invoice; auto-generates sequential id (INV-YYMM-NNN) unless a fresh INV- id is supplied.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
cust | body | string | ○ | Customer name, trimmed, clipped to 120 chars; 400 if empty. |
amount | body | number | ○ | Invoice amount; must be finite and >= 0, rounded; 400 if invalid. |
status | body | string | | One of draft|issued|paid|overdue; defaults to draft if omitted/invalid. |
month | body | string | | Billing month, YYMM exactly (e.g. 2607). Rejected with 400 if malformed — it used to be clipped to 8 chars and stored as-is, producing an invoice that belonged to no month and silently never matched the month-scoped MRR figure. Omitted = current YYMM. |
desc | body | string | | Line-item description, clip 300. |
due | body | string | | Due date string, clip 20. |
id | body | string | | Optional custom id; used only if matches /^INV-/ and not already present, else auto-assigned. |
recurring | body | boolean | | Recurring flag (coerced to bool). |
リクエスト
{"cust":"string(req)","amount":number(req),"status":"draft|issued|paid|overdue","month":"YYMM","desc":"string","due":"string","id":"INV-...","recurring":bool}レスポンス例
{"ok":true,"invoice":{"id":"INV-2607-005","cust":"北斗商事","month":"2607","desc":"","amount":10000,"due":"","status":"draft","recurring":false}}
POST/api/invoice/:id公開
Update status and/or fields on one existing invoice; validates status enum and amount.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Invoice id to update, e.g. INV-2607-004; 404 if not found. |
status | body | string | | New status; must be in draft|issued|paid|overdue else 400. |
cust | body | string | | Customer name, clip 120. |
desc | body | string | | Description, clip 300. |
month | body | string | | Billing month, YYMM exactly. Rejected with 400 if malformed; omitted leaves the invoice’s existing month unchanged. |
due | body | string | | Due date, clip 20. |
amount | body | number | | Amount; must be finite and >= 0 else 400. |
recurring | body | boolean | | Recurring flag (coerced to bool). |
リクエスト
{"status":"...","cust":"string","desc":"string","month":"YYMM","due":"string","amount":number,"recurring":bool} (all optional)レスポンス例
{"ok":true,"invoice":{"id":"INV-2607-004","cust":"三峰精機","month":"2607","desc":"新入社員向けキッティング 8台+送料","amount":19800,"due":"08/15","status":"issued","recurring":false}}
POST/api/invoices/issue公開
Bulk-issue: flips all draft invoices in a given (or current) month to issued, returns count.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
month | body | string | | Target YYMM month, clip 8; defaults to current month (e.g. 2607). |
リクエスト
{"month":"YYMM"} (optional; defaults to current month)レスポンス例
{"ok":true,"count":1}
GET/api/billing/summary公開live 200
Compute billing summary: MRR/ARR, billed/paid/unpaid/overdue/draft totals, recurring:oneshot ratio and paid ratio.
レスポンス例
{"ok":true,"summary":{"mrr":550000,"arr":6600000,"billed":632500,"paid":550000,"unpaid":82500,"overdue":49500,"draft":19800,"recurring":550000,"oneshot":82500,"ratio":"87:13","recurringPct":87,"paidRatio":87,"month":"2607","count":5}} シフト (Shift)
現場スタッフのシフト計画。
GET/api/shifts公開live 200
Returns the weekly roster grid and today's timeclock state.
レスポンス例
{"ok":true,"grid":{"s1":["day","day","day","day","day","off","off"],...},"clock":{"s1":{"in":"09:00","out":null,"status":"in","units":76},...}}
GET/api/staff公開live 200
Returns the roster staff list (own + agency staff).
レスポンス例
{"ok":true,"staff":[{"id":"s1","name":"一条","type":"own"},{"id":"a1","name":"派遣A","type":"agency"}]}
POST/api/shifts公開
Saves the roster grid, and optionally the timeclock, to the store.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
grid | body | object | ○ | Roster grid keyed by staffId → array of 7 day-status strings (e.g. 'day','late','off','disp'). |
clock | body | object | | Optional timeclock object keyed by staffId → {in,out,status,units}. |
リクエスト
{"grid":{"s1":["day",...]},"clock":{"s1":{"in":"09:00",...}}}レスポンス例
{"ok":true,"grid":{...},"clock":{...}}
POST/api/shifts/draft公開
Stages an UNCONFIRMED shift proposal (typically the AI suggestion) in a per-workspace in-memory slot. IT IS NOT PERSISTED and the roster is NOT changed: GET /api/shifts keeps returning the confirmed grid and reports the proposal separately as `draft`, so a reload never lets a suggestion overwrite the roster. One draft per workspace (a new one replaces the old); it expires by itself after 12 hours. Only POST /api/shifts/draft/apply writes.
リクエスト
{"grid":{"s1":["off","day","day","day","day","off","off"],"s2":[...]}} — 値は day|late|night|disp|off などの勤務区分。未知の値は off に丸められますレスポンス例
{"ok":true,"draft":{"grid":{"s1":["off","day","day","day","day","off","off"]},"diff":[{"staffId":"s1","day":0,"from":"day","to":"off"},{"staffId":"s2","day":0,"from":"late","to":"off"},...],"ts":1788114480682,"status":"unconfirmed"}}エラー
400 — {"ok":false,"error":"grid required"} — grid が無い、またはどの行も 7 日分の配列として読めない
POST/api/shifts/draft/apply公開
CONFIRMS the staged proposal — the only call in this group that writes. The roster BEFORE the change is pushed onto the restore history first (reason 適用前のシフト, id returned as applied.restoreId), then the draft grid becomes the live grid and the draft is cleared. 404 no draft when nothing is staged (or the 12-hour draft TTL has passed).
レスポンス例
{"ok":true,"grid":{"s1":["off","day","day","day","day","off","off"]},"applied":{"ts":1788114480690,"diff":[{"staffId":"s1","day":0,"from":"day","to":"off"},...],"restoreId":"SHmtg56zhu0"},"history":[{"id":"SHmtg56zhu0","ts":1788114480690,"reason":"適用前のシフト","grid":{...}}]}エラー
404 — {"ok":false,"error":"no draft"} — 未確定提案がありません
POST/api/shifts/draft/discard公開
Throws the staged proposal away. Since a draft was never persisted, nothing is rolled back: `discarded` simply says whether a draft was actually present, and `grid` is the unchanged confirmed roster. Idempotent.
レスポンス例
{"ok":true,"discarded":true,"grid":{"s1":["day","day","day","day","day","off","off"],"s2":["late","late","late","late","late","off","off"],...}} — 未確定提案が無ければ discarded:false
GET/api/shifts/history公開
Lists the pre-change roster snapshots taken by apply and restore (newest first, capped at 20) — each { id, ts, reason, grid }. These ids are the restore targets. Empty until the first 確定 operation.
レスポンス例
{"ok":true,"history":[{"id":"SHmtg56zhu0","ts":1788114480690,"reason":"適用前のシフト","grid":{"s1":["day","day","day","day","day","off","off"],"s2":["late","late","late","late","late","off","off"],"s3":["off","day","day","day","off","off","off"],"a1":["off","off","disp","disp","off","off","off"],"a2":["off","off","off","off","off","off","off"]}}]} — 未確定操作がなければ history:[]
POST/api/shifts/restore/:hid公開
Restores the roster from a history snapshot. A restore is itself a 確定 operation, so the CURRENT roster is pushed onto the history first (reason 復元前のシフト) and any staged draft is invalidated. 404 unknown snapshot for an id not in the history.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
hid | path | string | ○ | History snapshot id from GET /api/shifts/history, e.g. 'SHmtg56zhu0' |
レスポンス例
{"ok":true,"grid":{"s1":["day","day","day","day","day","off","off"],...},"restored":"SHmtg57z0n8","history":[{"id":"SHmtg57z0q9","ts":1788114526730,"reason":"復元前のシフト","grid":{...}},{"id":"SHmtg57z0n8","ts":1788114526727,"reason":"適用前のシフト","grid":{...}}]}エラー
404 — {"ok":false,"error":"unknown snapshot"}
POST/api/timeclock/:staffId/punch公開
Punches a staff member in or out, updating and returning their timeclock row.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
staffId | path | string | ○ | Staff id (clipped to 40 chars); must exist in staff roster (e.g. 's1','a1'). |
dir | body | string | | 'in' or 'out' (clipped to 8 chars); if omitted/invalid, toggles based on current status. |
リクエスト
{"dir":"in"}レスポンス例
{"ok":true,"staffId":"s1","row":{"in":"09:00","out":null,"status":"in","units":76}}
POST/api/staff公開
Create a staff member (type 'own' or 'agency') and return the updated staff list.
リクエスト
{"name":"山田 太郎","type":"own"}レスポンス例
{"ok":true,"staff":{"id":"smtc01egx1","name":"山田 太郎","type":"own"},"staffList":[{"id":"smtc01egx1","name":"山田 太郎","type":"own"}]}
POST/api/staff/:id公開
Update one staff member’s name and/or type.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Staff id, e.g. 'smtc03hjxd'. |
リクエスト
{"name":"山田 花子","type":"agency"}レスポンス例
{"ok":true,"staff":{"id":"smtc03hjxd","name":"山田 花子","type":"agency"}}
POST/api/staff/:id/delete公開
Delete one staff member and drop that person’s roster and timeclock rows.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Staff id, e.g. 'smtc03hjxd'. |
レスポンス例
{"ok":true,"id":"smtc03hjxd","staffList":[]} ネットワーク (Network)
拠点回線・機器監視。
GET/api/network/deals公開live 200
Return the capacity-network deal board (provide/accept/request deals), newest-first.
レスポンス例
{"ok":true,"deals":[{"id":"dmrv8fry22c","dir":"provide","partner":"ネットワーク公開","kind":"キッティング枠","units":40,"period":"7/15–7/22","rate":1200,"status":"募集中"}]}
POST/api/network/deal公開
Add a deal to the board (list surplus capacity or request capacity); prepends to deals array.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
units | body | integer | ○ | Number of units/slots; parsed as int, must be > 0 else 400. |
dir | body | string | | Direction: provide|accept|request; anything else coerced to 'provide' (clipped 16). |
partner | body | string | | Partner/counterparty name (clipped 120); defaults 'ネットワーク公開'. |
kind | body | string | | Capacity kind (clipped 40); defaults 'キッティング枠'. |
period | body | string | | Date range label (clipped 40); defaults '—'. |
rate | body | integer | | Unit rate; parsed int, defaults 0. |
status | body | string | | Status label (clipped 24); defaults by dir (承認待ち/対応中/募集中). |
リクエスト
{"dir":"provide","partner":"ネットワーク公開","kind":"キッティング枠","units":40,"period":"7/15–7/22","rate":1200,"status":"募集中"}レスポンス例
{"ok":true,"deal":{"id":"d...","dir":"provide","partner":"...","kind":"キッティング枠","units":40,"period":"—","rate":0,"status":"募集中"},"deals":[...]}
GET/api/network/centers公開live 200
Return the certified kitting-center directory / marketplace (5 seeded centers).
レスポンス例
{"ok":true,"centers":[{"id":"c-tsm","name":"豊島 近接センター","region":"東京","mdm":["Intune","Workspace ONE"],"slots":80,"shelf":40,"rate":1100,"certs":["ISMS","Pマーク"]}]}
GET/api/network/oem公開live 200
Return the white-label / OEM tenant config (brand, logo, domain, plan).
レスポンス例
{"ok":true,"oem":{"brand":"","logo":"","domain":"","plan":"seat"}}
POST/api/network/oem公開
Save the OEM tenant config; overwrites the single 'oem' doc.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
brand | body | string | | Brand name (clipped 24). |
logo | body | string | | Logo (clipped 2 chars — likely an emoji/initial). |
domain | body | string | | Custom domain (clipped 48). |
plan | body | string | | Billing plan: seat|rev; anything else coerced to 'seat' (clipped 8). |
リクエスト
{"brand":"Acme","logo":"A","domain":"acme.example","plan":"seat"}レスポンス例
{"ok":true,"oem":{"brand":"Acme","logo":"A","domain":"acme.example","plan":"seat"}}
POST/api/network/center公開
Create or update a certified partner center (update when a known id is supplied) and return the full centers list.
リクエスト
{"name":"板橋センター"}レスポンス例
{"ok":true,"center":{"id":"c-mtc01ei95","name":"板橋センター","region":"—","mdm":[],"slots":0,"shelf":0,"rate":0,"certs":[]},"centers":[{"id":"c-mtc01ei95","name":"板橋センター","region":"—","mdm":[],"slots":0,"shelf":0,"rate":0,"certs":[]}]}
POST/api/network/center/:id/delete公開
Delete one certified center and return the remaining list.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Center id, e.g. 'c-mtc01ei95'. |
レスポンス例
{"ok":true,"id":"c-mtc01ei95","centers":[]} オンボーディング (Onboarding)
導入チェックリスト。
GET/api/onboarding/projects公開live 200
Returns the current onboarding projects state {sel,tab,projects} (7-step MDM/ABM flow per customer), self-seeding demo data if missing.
レスポンス例
{"ok":true,"state":{"sel":"北斗商事","tab":"cust","projects":{"北斗商事":{"mdm":"Intune","steps":{"kickoff":"done","abm":"done","apns":"done","design":"done","pilot":"done","rollout":"done","handover":"wip"},"start":"2026-06-27"},"三峰精機":{...},"天城製薬":{...}}}}
PUT/api/onboarding/projects公開
Replaces the projects state; requires a projects object, sanitizes sel (clip 120) and tab (emp|cust).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
sel | body | string | | selected customer key, clipped to 120 chars |
tab | body | string | | active tab; coerced to 'emp' if === 'emp' else 'cust' |
projects | body | object | ○ | map of customer -> {mdm,steps,start}; missing/non-object -> 400 |
リクエスト
{"sel":"北斗商事","tab":"cust","projects":{"北斗商事":{"mdm":"Intune","steps":{...},"start":"2026-06-27"}}}レスポンス例
{"ok":true,"state":{"sel":"...","tab":"cust","projects":{...}}}
GET/api/onboarding/requests公開live 200
Lists all employee device requests, sorted ascending by ts. Seed is empty (mirrors client).
レスポンス例
{"ok":true,"requests":[]}
POST/api/onboarding/request公開
Creates a pending device request; honors a client-provided id so ids stay in sync. Requires name and dept.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | body | string | | optional client id, clipped 60; defaults to generated id('REQ') |
name | body | string | ○ | employee name, clip 120; empty -> 400 |
dept | body | string | ○ | department, clip 120; empty -> 400 |
model | body | string | | device model, clip 120 |
mdm | body | string | | MDM platform, clip 60, default 'Intune' |
use | body | string | | usage/purpose, clip 60 |
ticket | body | string | | ticket ref, clip 60 |
ts | body | number | | timestamp; defaults to Date.now() if not a number |
リクエスト
{"name":"桐生太郎","dept":"営業部","model":"iPhone 15","mdm":"Intune","use":"field","ticket":"T-100"}レスポンス例
{"ok":true,"request":{"id":"REQ-...","name":"桐生太郎","dept":"営業部","model":"iPhone 15","mdm":"Intune","use":"field","ticket":"T-100","status":"pending","ts":1700000000000}}
POST/api/onboarding/request/:id公開
Approve/reject/reset an existing request by id via {status}; on 'approved' sets a lead-time (optional lead days else random 0.8-2.4).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | request id; unknown -> 404 {error:'request not found'} |
status | body | string | ○ | one of approved|rejected|pending; else 400 {error:'invalid status'} |
lead | body | number | | lead days when approving; if not a number, random 0.8-2.4 rounded to 1dp |
リクエスト
{"status":"approved","lead":1.5}レスポンス例
{"ok":true,"request":{"id":"REQ-...","status":"approved","lead":1.5,...}} チーム (Team)
メンバー・権限・稼働。
GET/api/team/members公開live 200
Return the full list of team members from the team-members collection.
レスポンス例
{"ok":true,"members":[{"id":"t1","name":"藤宮 美咲","ini":"MS","role":"owner","dept":"経営","email":"misaki.fujimiya@ai-sns.io"}, ...]}
POST/api/team/member公開
Create/invite a new member; server issues the id, requires a name.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
name | body | string | ○ | Display name; 400 if missing/blank; clipped to 80 chars. |
ini | body | string | | Initials, first 2 chars uppercased; defaults 'NA'. |
role | body | string | | One of owner|admin|exec|member|viewer; defaults 'member'. |
dept | body | string | | Department, clipped to 60; defaults '—'. |
email | body | string | | Email, clipped to 120. |
scopes | body | object | | Per-area perms {mgmt,ops,sales,support,settings} each none|view|edit. |
リクエスト
{"name":"string","ini":"string?","role":"owner|admin|exec|member|viewer?","dept":"string?","email":"string?","scopes":{...}?}レスポンス例
{"ok":true,"member":{"id":"t<generated>","name":"...","ini":"NA","role":"member","dept":"—","email":""}}
POST/api/team/member/:id公開
Update an existing member's role/scopes/fields; blocks demoting the last owner.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Member id to update (e.g. 't5'); clipped to 40 chars. |
role | body | string | | New role; blocked with 409 if demoting the last owner. |
name | body | string | | Updated name. |
ini | body | string | | Updated initials. |
dept | body | string | | Updated department. |
email | body | string | | Updated email. |
scopes | body | object | | Updated per-area scopes. |
リクエスト
{"role":"...?","name":"...?","ini":"...?","dept":"...?","email":"...?","scopes":{...}?}レスポンス例
{"ok":true,"member":{"id":"t5","name":"御堂 由美","role":"member", ...}}
POST/api/team/member/:id/delete公開
Delete a member by id; refuses to remove the last remaining owner.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Member id to delete (e.g. 't6'); clipped to 40 chars. |
レスポンス例
{"ok":true,"id":"t6"}
GET/api/team/plan要サインインlive 200
Return the current billing plan key (starter|pro|ent), defaulting to 'pro'.
レスポンス例
{"ok":true,"planKey":"pro"}
POST/api/team/plan要サインイン
Set the current billing plan; validates against starter|pro|ent.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
planKey | body | string | ○ | Target plan; must be one of starter|pro|ent, else 400; clipped to 20 chars. |
リクエスト
{"planKey":"starter|pro|ent"}レスポンス例
{"ok":true,"planKey":"pro"}
POST/api/team/invite要サインイン
Invite a teammate into THIS workspace (owner/admin only). Issues a signed, expiring invite token, stores a pending "invited" member row and returns the invite URL. A pending invite consumes a seat against the plan member limit.
リクエスト
{"email":"teammate@example.com","role":"member","name":"佐藤 花子"}レスポンス例
{"ok":true,"inviteUrl":"https://kit-agent.ai-sns.io/signup?invite=<token>","token":"<body>.<sig>","member":{"id":"tmtc03zwxf","name":"佐藤 花子","ini":"NA","role":"member","dept":"—","email":"teammate@example.com","status":"invited"}}
POST/api/team/accept公開
Redeem an invite token: creates an account bound to the invited address that JOINS the invite’s workspace (not the caller’s anonymous one), sets the signed kit_tid cookie and returns a session token.
リクエスト
{"inviteToken":"<invite token>","email":"teammate@example.com","password":"Str0ng-Passw0rd-2026","name":"佐藤 花子"}レスポンス例
{"ok":true,"token":"<body>.<sig>","account":{"email":"invitee2@example.com","company":"","name":"佐藤 花子","tenantId":"t-…","created":"2026-08-27T20:54:48.597Z","lastLogin":null},"joinedTenant":"t-…"} 設定 (Settings)
テナント設定・機能フラグ。
GET/api/settings/company公開live 200
Return the issuer/company profile stamped onto quote/invoice/certificate PDFs.
レスポンス例
{"ok":true,"company":{"name":"Freeborn Inc.","brand":"Ai-Sns.io Kitting","addr":"東京都板橋区・板橋キッティングセンター","tel":"","email":"","regno":"","rep":"","web":"","bank":"","logo":""}}
PUT/api/settings/company公開
Merge-update the issuer/company profile (text fields + base64 logo dataURL).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
name | body | string | | Company legal name (clipped to 300 chars) |
brand | body | string | | Brand/product name (clip 300) |
addr | body | string | | Address (clip 300) |
tel | body | string | | Phone (clip 300) |
email | body | string | | Contact email (clip 300) |
regno | body | string | | Registration/invoice number (clip 300) |
rep | body | string | | Representative name (clip 300) |
web | body | string | | Website URL (clip 300) |
bank | body | string | | Bank details (clip 300) |
logo | body | string | | Logo base64 dataURL (clipped to 6MB) |
リクエスト
{"name":"...","brand":"...","addr":"...","tel":"...","email":"...","regno":"...","rep":"...","web":"...","bank":"...","logo":"data:image/png;base64,..."}レスポンス例
{"ok":true,"company":{...merged record...}}
GET/api/settings/prefs公開live 200
Return notification preferences (emailNotify / slaAlert toggles).
レスポンス例
{"ok":true,"prefs":{"emailNotify":true,"slaAlert":true}}
PUT/api/settings/prefs公開
Merge-update notification toggles (emailNotify / slaAlert).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
emailNotify | body | boolean | | Email notification toggle; coerced with !! (default true) |
slaAlert | body | boolean | | SLA breach alert toggle; coerced with !! (default true) |
リクエスト
{"emailNotify":true,"slaAlert":false}レスポンス例
{"ok":true,"prefs":{"emailNotify":true,"slaAlert":false}}
GET/api/config公開
Public, read-only view of the admin-controlled site config (no secrets) — announcement banner, maintenance state and front-end feature toggles. Sent with Cache-Control: no-store. The demo workspace additionally gets demoEnabled:true and demoTenant:true.
レスポンス例
{"ok":true,"config":{"maintenanceMode":false,"maintenanceMessage":"ただいまメンテナンス中です。しばらくしてから再度お試しください。","registrationOpen":true,"announcementEnabled":false,"announcementText":"","announcementLevel":"info","demoEnabled":true,"signinEnabled":true,"chatBubbleEnabled":true,"primaryCtaLabel":""}}
GET/api/settings/ui公開
Return this workspace’s server-side UI state blob (opaque string map), so a view/filter choice survives a cache-clear or a new device.
レスポンス例
{"ok":true,"state":{}}
PUT/api/settings/ui公開
Merge-update the UI state blob: only string values are stored, an explicit null deletes a key, and the whole document is capped at 512KB.
リクエスト
{"state":{"theme":"dark"}}レスポンス例
{"ok":true,"keys":1,"size":16} コンプライアンス (Compliance)
ISMS/Pマーク/SOC2/APPI・監査ログ・CAPA。
GET/api/compliance公開live 200
Returns control checklists per standard (isms/pmark/soc2/appi) plus the full CAPA corrective-action list.
レスポンス例
{"ok":true,"controls":{"isms":[true,true,true,true,false,true],"pmark":[...],"soc2":[...],"appi":[...]},"capa":[{"id":"CAPA-2026-014","src":"SOC 2","finding":"変更管理の承認記録に一部欠落","due":"2026-07-25","owner":"一条 玲","state":"対応中"}]}
POST/api/compliance/capa/:id公開
Updates the state of a single corrective action (CAPA) identified by id.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | CAPA id, e.g. CAPA-2026-014 |
state | body | string | ○ | New state string, clipped to 20 chars; must be non-empty after trim |
リクエスト
{"state":"是正待ち"}レスポンス例
{"ok":true,"capa":{"id":"CAPA-2026-014","src":"SOC 2","finding":"...","due":"2026-07-25","owner":"一条 玲","state":"是正待ち"}}
PUT/api/compliance/controls/:key公開
Replaces a standard's control checklist with a new boolean array.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
key | path | string | ○ | Control set key: isms | pmark | soc2 | appi |
controls | body | boolean[] | ○ | Array of booleans (coerced via !!), first 100 kept |
リクエスト
{"controls":[true,true,false,true,false,true]}レスポンス例
{"ok":true,"key":"isms","controls":[true,true,false,true,false,true]}
GET/api/audit-log公開live 200
Returns the tamper-evident audit trail as newest-first rows, each [ts,user,action,target,result,cat].
レスポンス例
{"ok":true,"logs":[["2026-07-11 09:02","一条 玲","ログイン(多要素認証)","管制コンソール","成功","login"],["2026-07-11 09:14","綾瀬 涼","端末台帳 更新(ステータス変更)","IMEI 356821…4471","成功","ledger"]]}
POST/api/audit-log公開
Appends a new audit entry; auto-fills ts/user/target/result/cat defaults when omitted.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
action | body | string | ○ | Action description, clipped 200, must be non-empty after trim |
ts | body | string | | Timestamp, clipped 25; defaults to server now() 'YYYY-MM-DD HH:mm' |
user | body | string | | Actor, clipped 60; defaults to 'システム' |
target | body | string | | Target, clipped 200; defaults to '—' |
result | body | string | | Result, clipped 20; defaults to '成功' |
cat | body | string | | Category, clipped 20; defaults to 'audit' |
リクエスト
{"action":"消去実行","user":"早瀬 佳奈","target":"北斗商事 45台","result":"成功","cat":"erase"}レスポンス例
{"ok":true,"entry":["2026-07-22 10:15","早瀬 佳奈","消去実行","北斗商事 45台","成功","erase"]} ワークデスク (Workdesk)
社内ナレッジ・依頼。
POST/api/compliance/report公開
PRE-ISSUE CHECK for the internal audit report. The month, the record count, the CAPA list and the issuer block are RECOMPUTED from this workspace's own audit log and settings. Returns 422 with the offending figure named — never a printed document — when the month is malformed or in the future, a log timestamp is not a real datetime, a CAPA due date is invalid, the issuer/Pマーク registration is missing or malformed, or a caller-supplied claim.count disagrees with the records for that month. With no month the current month is used, falling back to the newest month that actually has records so the figure and its label describe the same period.
リクエスト
{"month":"2026-07","claim":{"count":15}} — どちらも任意。claim.count を付けると記録件数と照合されますレスポンス例
{"ok":true,"doc":{"month":"2026-07","count":15,"byCat":{"login":3,"ledger":2,"erase":3,"ship":2,"perm":2,"mdm":2,"audit":1},"capa":[{"id":"CAPA-2026-014","src":"SOC 2","finding":"...","due":"2026-07-25","owner":"一条 玲","state":"対応中","dueValid":true,"overdue":true,"daysLeft":-37},...],"overdue":3,"org":{"name":"Freeborn Inc.","brand":"Ai-Sns.io Kitting","addr":"...","email":"contact@ai-sns.io","officerTitle":"情報セキュリティ管理責任者","officer":"一条 玲","center":"板橋キッティングセンター","pmark":"第20002676号","isms":"ISO/IEC 27001:2022"},"issuedAt":"2026-08-31 03:28"}}エラー
422 — 整合しないので発行しない。{"ok":false,"issues":["監査ログ件数 99件 が 2026-07 の記録 15件 と一致しません"],"month":"2026-07"} / {"ok":false,"issues":["監査対象月「2026-13」が YYYY-MM 形式ではありません"],"month":"2026-07"}
POST/api/compliance/custody公開
PRE-ISSUE CHECK for a 授受記録 (chain of custody). A record that says who held a device and when may not be issued with an unsigned or unfinished step, an impossible timestamp, steps out of chronological order, or no issuer. Returns 422 with the offending step named, splitting the faults: `invalid` = hard faults (the record cannot be printed at all), `incomplete` = the handover simply has not finished yet. `draftable` is true exactly when invalid is empty — that record may be printed only as a clearly-marked 進行中 draft, never as the trail. Up to 50 steps.
リクエスト
{"imei":"356938035643809","model":"iPhone 16","cust":"北斗商事","steps":[{"label":"受領","role":"センター","who":"一条","dt":"2026-08-01 09:00","state":"完了","sign":"一条"},{"label":"出荷","who":"綾瀬","dt":"2026-08-02 10:00","state":"完了","sign":"綾瀬"}]} — state は 完了 で確定。sign が空/—/(未署名)は未署名扱いレスポンス例
{"ok":true,"record":{"imei":"356938035643809","model":"iPhone 16","cust":"北斗商事","steps":[{"label":"受領","role":"","who":"一条","dt":"2026-08-01 09:00","state":"完了","sign":"一条"},{"label":"出荷","role":"","who":"綾瀬","dt":"2026-08-02 10:00","state":"完了","sign":"綾瀬"}],"org":{"name":"Freeborn Inc.","brand":"Ai-Sns.io Kitting","addr":"...","email":"contact@ai-sns.io","officerTitle":"情報セキュリティ管理責任者","officer":"一条 玲","center":"板橋キッティングセンター","pmark":"第20002676号","isms":"ISO/IEC 27001:2022"},"issuedAt":"2026-08-31 03:28"}}エラー
422 — 発行不可。ハード欠陥: {"ok":false,"issues":["対象端末(IMEI/シリアル)が指定されていません","授受工程が1件もありません"],"invalid":[同左],"incomplete":[],"draftable":false} / 進行中のみ: {"ok":false,"issues":["工程「出荷」が未完了です(状態:予定)","工程「出荷」が未署名です","工程「出荷」の担当者が未確定です"],"invalid":[],"incomplete":[同左],"draftable":true}
GET/api/workdesk/docs公開live 200
List source-library documents, newest user-added first then seed docs in original order.
レスポンス例
{"ok":true,"docs":[{"id":"sop","name":"キッティング手順書","type":"SOP","size":184320,"status":"索引済","date":"2026/07/21","created":0,"seedIdx":0}]}
POST/api/workdesk/doc公開
Create/upsert a document; all body fields optional, defaults applied.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | body | string | | Doc id (clip 60); auto-generated 'doc-...' if omitted |
name | body | string | | Doc name (clip 200); defaults '資料-<timestamp>' |
type | body | string | | Doc type (clip 40); defaults 'FILE' |
size | body | number | | Byte size; defaults 0 |
status | body | string | | Index status (clip 40); defaults '索引済' |
date | body | string | | Display date (clip 40); defaults today() JA date |
リクエスト
{"name":"...","type":"...","size":123,"status":"...","date":"...","id":"..."}レスポンス例
{"ok":true,"doc":{"id":"doc-...","name":"...","type":"FILE","size":0,"status":"索引済","date":"2026/07/22","created":1700000000000}}
POST/api/workdesk/doc/:id/delete公開
Delete a document by id; 404 if not found.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Doc id to delete (e.g. 'sop') |
レスポンス例
{"ok":true}
GET/api/workdesk/threads公開live 200
List AI chat threads, newest first (created desc); seed is empty.
レスポンス例
{"ok":true,"threads":[]}
POST/api/workdesk/thread公開
Create/upsert a full chat thread; body optional, sanitizes msgs.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | body | string | | Thread id (clip 60); auto-generated 'thr-...' if omitted |
title | body | string | | Title (clip 120); defaults '新しいチャット' |
ts | body | string | | Timestamp label (clip 40) |
msgs | body | array | | Messages [{role,text?,html?,srcs?,typing?}], each sanitized via cleanMsg |
リクエスト
{"id":"...","title":"...","ts":"...","msgs":[{"role":"user","text":"..."}]}レスポンス例
{"ok":true,"thread":{"id":"thr-...","title":"新しいチャット","ts":"","msgs":[],"created":1700000000000}}
POST/api/workdesk/thread/:id公開
Append/replace msgs or rename a thread; creates thread on-append if missing.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Thread id |
title | body | string | | Rename thread (clip 120) |
ts | body | string | | Timestamp label (clip 40) |
msgs | body | array | | Replace whole msgs list |
append | body | array | | Append msgs to existing list |
リクエスト
{"title":"...","ts":"...","msgs":[...],"append":[{"role":"ai","text":"..."}]}レスポンス例
{"ok":true,"thread":{"id":"...","title":"...","ts":"...","msgs":[...],"created":1700000000000}}
POST/api/workdesk/conn公開
Persist connector on/off map {id:bool,...}, coerced to booleans.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
map | body | object | | {id:bool} map; body itself used as map if no .map key. Keys clip 40, values coerced !! |
リクエスト
{"slack":true,"teams":false} OR {"map":{"slack":true}}レスポンス例
{"ok":true,"conn":{"slack":true,"teams":false}}
GET/api/workdesk/conn公開live 200
Read persisted connector on/off map (hydration convenience).
レスポンス例
{"ok":true,"conn":{}}
POST/api/workdesk/search公開
Real retrieval over THIS workspace's indexed documents. No match returns no hits — never a plausible source for an unsupported answer.
リクエスト
{"q":"キッティング"}レスポンス例
{"ok":true,"q":"キッティング","hits":[],"indexed":0} 顧客ポータル (Portal)
顧客共有ビュー。
POST/api/esg/report公開
PRE-ISSUE CHECK for the ESG report. Every figure is recomputed from this workspace's reuse records and the saved CO2e/e-waste factors. Returns 422 with the offending figure named when 顧客別合計 disagrees with 月次実績合計, a customer's 再利用 exceeds its 返却, the CO2e factor is not a positive number, or a caller-supplied claim (units / actualUnits / forecastUnits / co2e / factor) disagrees with the computation. 実績 and 予測 are reported as two separate, labelled figures and are never summed together. `warnings` (e.g. a factor outside the published source range) do NOT block issuance and are returned on the 200 too.
リクエスト
{"scope":"center","claim":{"units":1240,"actualUnits":351,"forecastUnits":889,"co2e":68.2,"factor":55}} — すべて任意。scope は 'center'/'センター全体'(既定)または集計対象の顧客名。claim を付けた項目だけが照合されますレスポンス例
{"ok":true,"warnings":[],"doc":{"scope":"センター全体","fy":"2026年度","actualThrough":"2026-07","forecastFrom":"2026-08","units":1240,"ret":1420,"co2e":68.2,"ewasteKg":520.8,"circularity":87.3,"actualUnits":351,"forecastUnits":889,"actualCo2e":19.3,"forecastCo2e":48.9,"settings":{"co2e":55,"ewaste":0.42},"basis":{"co2e":{"unit":"kg-CO2e/台","lo":55,"hi":66,"perModel":false,"method":"...","source":"...","scope":"..."},"ewaste":{"unit":"kg/台","lo":0.19,"hi":1.5,...}}}}エラー
422 — 整合しないので発行しない。{"ok":false,"issues":["対象台数 1台 が集計値 1240台 と一致しません"],"warnings":[]} / {"ok":false,"issues":["CO2e 削減量 1 t が 1240台 × 55 kg-CO2e/台 ÷ 1000 = 68.2 t と一致しません"],"warnings":[]}
GET/api/portals公開live 200
Returns the full customer portal state: per-customer {published,suffix,last,name,slug} plus global counters _tickets (MTD) and _dls (avg monthly downloads).
レスポンス例
{"ok":true,"state":{"hokuto":{"published":true,"suffix":"blk3","last":"3分前","name":"北斗商事","slug":"hokuto"},"tosei":{"published":false,"suffix":"1jip","last":"—",...},"_tickets":4,"_dls":63}}
GET/api/portal/viewtoken
The entire read-only customer view for ONE client, addressed by a PORTAL TOKEN — not by a session. The workspace and the client both come from the signed token, so a visitor never sees the operator's customer roster. An unpublished portal returns only { client, published:false, last } and nothing else. When published it adds the client's own device figures with the IMEI masked to its last 6 (max 200 rows) and their 20 most recent tickets. Cache-Control: no-store.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
t | query | string | ○ | Portal token from POST /api/portal/issue. Binds the client AND the workspace, expires and can be revoked. Required in production; outside production a legacy ?c=<顧客名> form is still accepted so the demo Space keeps working. |
c | query | string | | LEGACY, non-production only: the customer name, resolved against the CALLER's workspace. Refused outright when the app runs in production — use ?t= instead. |
レスポンス例
{"ok":true,"client":{"name":"北斗商事","slug":"hokuto"},"published":true,"last":"たった今","devices":{"total":124,"active":124,"computed":true,"list":[{"imei":"****539346","model":"iPhone 15","status":"保管中","chip":"c-blue","loc":"A-10"},...],"shown":124},"tickets":[{"id":"TK-PG56ZJ90F05","kind":"故障","subject":"端末が起動しません","status":"受付","created":1788114480741,"viaPortal":true}]} — 未公開なら {"ok":true,"client":{...},"published":false,"last":"たった今"} のみエラー
401 — {"ok":false,"code":"portal_link_invalid","error":"このリンクは無効または期限切れです"} — トークンが無い・改竄・期限切れ・失効
404 — {"ok":false,"code":"portal_not_found","error":"ポータルが見つかりません"}
500 — {"ok":false,"error":"ポータルを読み込めませんでした"}
POST/api/portal/inquirytoken
A portal client's enquiry becomes a REAL helpdesk ticket in the workspace the link belongs to — the same `tickets` collection GET /api/tickets reads — and answers with the 受付番号 (TK-P…, unique by construction, marking it as portal-raised) and the epoch-ms time it was accepted. The 「ポータル経由の問い合わせ(今月)」 counter is bumped by the same action, once. Authenticated by the PORTAL TOKEN, not a session. A failure is reported as a failure (502) rather than claimed as sent, and an unknown or unpublished portal never creates a ticket. Cache-Control: no-store.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
t | query | string | | Portal token — may be sent as ?t= or as body.t. Required (in production) either way. |
リクエスト
{"t":"<portal token>","kind":"故障","message":"端末が起動しません","name":"山田","email":"y@example.com"} — kind は 問合せ|故障|交換|紛失|返却|その他(既定 問合せ)。message は必須・2000字までレスポンス例
{"ok":true,"ref":"TK-PG56ZJ90F05","ticket":{"id":"TK-PG56ZJ90F05","status":"受付","kind":"故障","cust":"北斗商事"},"created":1788114480741}エラー
400 — {"ok":false,"code":"message_required","error":"ご用件を入力してください。"}
401 — {"ok":false,"code":"portal_link_invalid","error":"このリンクは無効または期限切れです"}
404 — {"ok":false,"code":"portal_not_found","error":"ポータルが見つかりません"}
409 — {"ok":false,"code":"portal_unpublished","error":"このポータルは現在ご利用いただけません。"} — 未公開ポータルにはチケットを作りません
502 — {"ok":false,"code":"ticket_not_created","error":"送信できませんでした。お手数ですが時間をおいて再度お試しください。"} — 送信できなかったときは、できなかったと答えます
POST/api/portal/:custId公開
Mutates one customer portal. body.action: 'toggle' (default; flip or set published, sets last='たった今' when publishing) | 'ticket' (bump global tickets, set last) | 'dl' (bump global downloads) | 'touch' (set last from body.last). Returns updated portal + counters.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
custId | path | string | ○ | Customer id: one of hokuto|mitsumine|amagi|tosei|mizuki. 404 if unknown. |
action | body | string | | One of toggle|ticket|dl|touch (default toggle, clipped to 20 chars). Unknown value -> 400. |
published | body | boolean | | For action=toggle: explicit publish state; if omitted, flips current value. |
last | body | string | | For action=touch: last-access label, clipped to 40 chars (default 'たった今'). |
リクエスト
{"action":"toggle","published":true,"last":"たった今"}レスポンス例
{"ok":true,"portal":{"id":"tosei","name":"東成建設","slug":"tosei","published":true,"suffix":"1jip","last":"たった今"},"tickets":4,"dls":63}
POST/api/portal/issue公開
Issues (publishes) a portal for a customer identified by body.custId/id, sets published=true and last='たった今', and returns the public portal URL '/portal.html?t=<name>'.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
custId | body | string | ○ | Customer id (alias 'id' also accepted), clipped to 40 chars; must map to a known customer else 400. |
リクエスト
{"custId":"tosei"}レスポンス例
{"ok":true,"url":"/portal.html?t=%E6%9D%B1%E6%88%90%E5%BB%BA%E8%A8%AD","portal":{"id":"tosei","name":"東成建設","slug":"tosei","published":true,"suffix":"1jip","last":"たった今"}} アカウント・サインイン (Account/Auth)
メール/パスワード・Google・電話番号でのサインアップとサインイン。
POST/api/account/signup公開
Create an account that ADOPTS the anonymous workspace this browser is already using; sets the signed kit_tid cookie and returns a session token. Refused when registrationOpen is off; rate/velocity capped per IP.
リクエスト
{"email":"demo.user@example.com","password":"Str0ng-Passw0rd-2026","company":"北斗商事","name":"山田 太郎"}レスポンス例
{"ok":true,"token":"<body>.<sig>","account":{"email":"demo.user@example.com","company":"北斗商事","name":"山田 太郎","tenantId":"t-…","created":"2026-08-27T20:52:56.379Z","lastLogin":null},"adoptedTenant":"t-…"}
POST/api/account/login公開
Password sign-in. Returns the session token and sets the signed kit_tid cookie for the account’s workspace. When 2FA (sec2FA) is on it instead returns mfaRequired:true plus a challenge id to complete at /api/account/login/verify.
リクエスト
{"email":"demo.user@example.com","password":"Str0ng-Passw0rd-2026"}レスポンス例
{"ok":true,"token":"<body>.<sig>","account":{"email":"demo.user@example.com","company":"北斗商事","name":"山田 太郎","tenantId":"t-…","created":"2026-08-27T20:52:56.379Z","lastLogin":"2026-08-27T20:52:56.416Z"}}
POST/api/account/login/verify公開
Complete a 2FA login started at /api/account/login by submitting the emailed one-time code with its challenge id.
リクエスト
{"email":"demo.user@example.com","challenge":"<challenge id>","code":"123456"}
POST/api/account/logout公開
Clear the kit_tid cookie so a shared machine does not leak the workspace dataset.
レスポンス例
{"ok":true}
GET/api/account/me公開
Report whether the caller is signed in. Without a session token it answers signedIn:false plus the anonymous tenantId; with a valid Bearer token it answers signedIn:true and the account profile.
レスポンス例
{"ok":true,"signedIn":false,"tenantId":"t-…"} | {"ok":true,"signedIn":true,"account":{"email":"owner.demo@example.com","company":"北斗商事","name":"山田 太郎","tenantId":"t-…","created":"2026-08-27T20:54:38.468Z","lastLogin":null}}
POST/api/account/password要サインイン
Change the signed-in account’s password after verifying the current one; every previously issued session token is invalidated and a fresh one is returned.
リクエスト
{"current":"<current password>","next":"<new password>"}レスポンス例
{"ok":true,"token":"<body>.<sig>"}
GET/api/auth/methods公開
Which sign-in methods this deployment has configured: password is always true, google and phone reflect whether their provider credentials are set.
レスポンス例
{"ok":true,"password":true,"google":false,"phone":false}
GET/api/auth/google/start公開
Begin Google OAuth: 302-redirects to Google’s consent screen with a signed, 10-minute state that carries the current tenant. 503 when Google login is not configured.
GET/api/auth/google/callback公開
Google OAuth callback: verifies state, exchanges the code, finds or creates the account, sets the kit_tid cookie and 302-redirects to /login.html with the session token in the URL fragment (#gt=…). Failures redirect to /login.html?err=…
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
code | query | string | — | Authorization code returned by Google. |
state | query | string | — | Signed state issued by /api/auth/google/start. An invalid state redirects to /login.html?err=state. |
POST/api/auth/phone/start公開
Send a one-time SMS code to the given phone number. 503 when phone login is not configured.
リクエスト
{"phone":"+819012345678"}レスポンス例
{"ok":true}
POST/api/auth/phone/verify公開
Verify the SMS code, find or create the phone-keyed account (tel:+81…), set the kit_tid cookie and return a session token.
リクエスト
{"phone":"+819012345678","code":"123456"} キャパシティ (Capacity)
キッティング能力・保管枠と計画負荷。
GET/api/capacity公開
Full capacity state for this workspace: the saved config, every planned-load entry sorted by date, the derived week view and a summary.
レスポンス例
{"ok":true,"config":{"kittingDaily":192,"storageMax":240,"storageUsed":178,"palletMax":8,"palletUsed":5,"warehouseMax":240,"warehouseUsed":116,"staffCount":2,"perStaff":80,"updated":1787863913818},"load":[{"id":"CAP-0001","date":"2026-08-28","created":1787863913818,"cust":"北斗商事","qty":62,"kind":"kitting","note":"LCM 交換・再キッティング"},…],"week":[…],"summary":{…}}
POST/api/capacity/config公開
Save/patch this workspace’s capacity config; every numeric field falls back to its current value when omitted, and updated is stamped server-side.
リクエスト
{"kittingDaily":200,"staffCount":3}レスポンス例
{"ok":true,"config":{"kittingDaily":200,"storageMax":0,"storageUsed":0,"palletMax":0,"palletUsed":0,"warehouseMax":0,"warehouseUsed":0,"staffCount":3,"perStaff":80,"updated":1787863957335}}
POST/api/capacity/load公開
Add a planned-load entry; the server assigns the next CAP-#### id.
リクエスト
{"date":"2026-09-10","cust":"北斗商事","qty":40,"kind":"kitting","note":"新規"}レスポンス例
{"ok":true,"entry":{"id":"CAP-0001","date":"2026-09-10","cust":"北斗商事","qty":40,"kind":"kitting","note":"新規","created":1787863957344}}
POST/api/capacity/load/:id公開
Update date / cust / qty / kind / note on one load entry; omitted fields are left alone.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Load entry id, e.g. 'CAP-0001'. 404 {ok:false,error:'load entry not found'} if unknown. |
リクエスト
{"qty":55}レスポンス例
{"ok":true,"entry":{"id":"CAP-0001","date":"2026-09-10","cust":"北斗商事","qty":55,"kind":"kitting","note":"新規","created":1787863957344}}
POST/api/capacity/load/:id/delete公開
Remove one planned-load entry.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Load entry id, e.g. 'CAP-0001'. |
レスポンス例
{"ok":true,"id":"CAP-0001"} DaaS (Device as a Service)
DaaS 見積ポートフォリオ・シミュレータ・提案送付履歴。
GET/api/daas公開
DaaS state for this workspace: the quote/contract portfolio, saved simulator preferences (null when never saved) and the proposal send history.
レスポンス例
{"ok":true,"quotes":[{"id":"DQ-DEMO1","cust":"天城製薬","model":"iPhone 16","qty":1800,"term":36,"res":20,"lcm":620,"war":"full","monthly":5120,"mrr":9216000,"ts":1},…],"sim":null,"sends":[]}
POST/api/daas/quote公開
Create or update a DaaS quote/contract. The portfolio is capped and the oldest entries are dropped first.
リクエスト
{"cust":"北斗商事","model":"iPhone 16","qty":100,"term":36,"res":20,"lcm":600,"war":"full"}レスポンス例
{"ok":true,"quote":{"id":"DQmtc01egb0","cust":"北斗商事","model":"iPhone 16","qty":100,"term":36,"res":20,"lcm":600,"war":"full","monthly":0,"mrr":0,"ts":1787863957355}}
POST/api/daas/quote/:id/status公開
Moves one DaaS quote along 下書き(draft) → 提案中(proposed) → 承認(approved) → 契約中(contracted). FORWARD MOVES ARE ONE STAGE AT A TIME — skipping is refused with 400 stage skipped, which names the only stage that is allowed next. 承認 and 契約中 additionally REQUIRE a customer and an unexpired 有効期限: a quote missing either is refused with 400 and the missing fields listed, and an expiry earlier than today is refused with 400 quote expired. Rolling BACK to any earlier stage is always allowed and clears 契約日. Only 契約中 quotes count towards totals.mrr / totals.units; earlier stages sit in pipeline*.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | DaaS quote id, e.g. 'DQ-DEMO1'. |
リクエスト
{"status":"approved"} — draft|proposed|approved|contracted のいずれかレスポンス例
{"ok":true,"quote":{"id":"DQmtg56zikh","cust":"北斗商事","model":"iPhone 16","qty":100,"term":36,"res":20,"lcm":600,"war":"full","monthly":5000,"mrr":500000,"expires":"2027-03-31","ts":1788114480716,"status":"approved","statusTs":1788114480721,"statusLabel":"承認"},"from":"proposed","totals":{"units":3950,"mrr":18013000,"arr":216156000,"contracts":4,"pipelineUnits":100,"pipelineMrr":500000,"byStatus":{"draft":0,"proposed":0,"approved":1,"contracted":4}}}エラー
400 — 段跳ばし: {"ok":false,"error":"stage skipped","from":"proposed","to":"contracted","next":"approved"} | 未知の状態: {"ok":false,"error":"unknown status","statuses":["draft","proposed","approved","contracted"]} | 条件未充足: {"ok":false,"error":"customer and expiry required","missing":["cust","expires"],"needs":["顧客","有効期限"]} | 期限切れ: {"ok":false,"error":"quote expired","expires":"2026-03-31"}
404 — {"ok":false,"error":"quote not found"}
POST/api/daas/quote/:id/delete公開
Remove one quote from the portfolio.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Quote id, e.g. 'DQ-DEMO1'. |
レスポンス例
{"ok":true,"id":"DQmtc03hi5b"}
POST/api/daas/sim公開
Save the simulator preferences (model, qty, term, residual, LCM, warranty, customer) for this workspace.
リクエスト
{"qty":100,"term":36}レスポンス例
{"ok":true,"sim":{"model":"","qty":100,"term":36,"res":0,"lcm":0,"war":"none","cust":""}}
POST/api/daas/send公開
Append one proposal send-history entry (newest first, capped at 50).
リクエスト
{"cust":"北斗商事","doc":"提案書"}レスポンス例
{"ok":true,"send":{"ts":1787863957369,"cust":"北斗商事","channel":"","to":"","model":"","qty":0,"mrr":0}} 付帯品受注 (Store)
付帯品カタログと受注。
GET/api/store/catalog公開
Accessory catalog: the built-in reference master concatenated with this workspace’s custom products, plus the category list.
レスポンス例
{"ok":true,"catalog":[{"id":"p-rug","name":"耐衝撃ケース(現場用)","cat":"ケース","ic":"🛡","price":2800,"cost":1180,"note":"現場向け"},{"id":"p-case","name":"TPUクリアケース","cat":"ケース","ic":"📱","price":1200,"cost":420},…],"cats":[…]}
GET/api/store/orders公開
This workspace’s accessory order history with its count.
レスポンス例
{"ok":true,"orders":[{"id":"AO-202608-8220","date":"2026-08-27T20:53:36.583Z","dateStr":"2026/8/28","cust":"北斗商事","job":"KIT-2026-311","dest":"本社 情シス部","count":40,"total":48000,"gp":34400,"lines":[{"id":"p-glass","name":"保護ガラス(9H)","price":1200,"cost":340,"qty":40}],"status":"確定"}],"count":1}
POST/api/store/order公開
Create an accessory order. count, total and gp are computed server-side from the line items.
リクエスト
{"cust":"北斗商事","job":"KIT-2026-311","dest":"本社 情シス部","lines":[{"id":"p-glass","name":"保護ガラス(9H)","price":1200,"cost":340,"qty":40}]}レスポンス例
{"ok":true,"order":{"id":"AO-202608-8220","date":"2026-08-27T20:53:36.583Z","dateStr":"2026/8/28","cust":"北斗商事","job":"KIT-2026-311","dest":"本社 情シス部","count":40,"total":48000,"gp":34400,"lines":[{"id":"p-glass","name":"保護ガラス(9H)","price":1200,"cost":340,"qty":40}],"status":"確定"}}
POST/api/store/order/:id公開
Update status / cust / job / dest on one order. Line items and the computed totals are not changed here.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Order id, e.g. 'AO-202608-8220'. |
リクエスト
{"status":"出荷済"}レスポンス例
{"ok":true,"order":{"id":"AO-202608-8220","date":"2026-08-27T20:53:36.583Z","dateStr":"2026/8/28","cust":"北斗商事","job":"KIT-2026-311","dest":"本社 情シス部","count":40,"total":48000,"gp":34400,"lines":[{"id":"p-glass","name":"保護ガラス(9H)","price":1200,"cost":340,"qty":40}],"status":"出荷済"}}
POST/api/store/order/:id/delete公開
Delete one accessory order.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Order id, e.g. 'AO-202608-8220'. |
レスポンス例
{"ok":true,"id":"AO-202608-8220"}
POST/api/store/product公開
Add a workspace-custom catalog product (custom:true), which then appears in GET /api/store/catalog after the reference master.
リクエスト
{"name":"USB-Cハブ","cat":"電源","price":3800,"cost":1500}レスポンス例
{"ok":true,"product":{"id":"p-mtc01eh52","name":"USB-Cハブ","cat":"電源","ic":"📦","price":3800,"cost":1500,"custom":true}} 買取・リファービッシュ (Trade-in)
買取査定・リファービッシュ在庫・買取証書。
GET/api/tradein公開
Trade-in state for this workspace: the buyback ledger, refurbished-stock inventory and buyback receipts.
レスポンス例
{"ok":true,"buybacks":[{"id":"BB-DEMO01","date":"2026-07-04","model":"iPhone 15","cust":"北斗商事","grade":"A","bat":92,"offer":56400,"resell":76100,"margin":17200,"residual":0.5},…],"inventory":[…],"receipts":[…]}
POST/api/tradein/buyback公開
Create or update a buyback record (margin and residual are computed server-side).
リクエスト
{"model":"iPhone 15","cust":"北斗商事","grade":"A","bat":92,"offer":56400,"resell":76100}レスポンス例
{"ok":true,"buyback":{"id":"BB-mtc01ehu4","date":"2026-08-27","model":"iPhone 15","cust":"北斗商事","grade":"A","bat":92,"offer":56400,"resell":76100,"margin":0,"residual":0}}
POST/api/tradein/buyback/:id/delete公開
Delete one buyback record.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Buyback id, e.g. 'BB-DEMO01'. |
レスポンス例
{"ok":true,"id":"BB-mtc03hed8"}
POST/api/tradein/inventory公開
Create or update one refurbished stock item.
リクエスト
{"model":"iPhone 14","grade":"B"}レスポンス例
{"ok":true,"stock":{"id":"RF-mtc02o5r7","model":"iPhone 14","grade":"B","cost":0,"ask":0,"margin":0}}
POST/api/tradein/inventory/:id/delete公開
Delete one refurbished stock item.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Stock id, e.g. 'RF-mtc02o5r7'. |
レスポンス例
{"ok":true,"id":"RF-mtc03hf99"}
POST/api/tradein/receipt公開
Create or update a buyback receipt, keyed by its receipt number (no).
リクエスト
{"cust":"北斗商事"}レスポンス例
{"ok":true,"receipt":{"no":"KIT-BUY-2026-0001","date":"2026-08-27","dateJa":"2026/8/28","cust":"北斗商事","person":"","items":[],"total":0,"ts":1787864016602}}
POST/api/tradein/receipt/:id/delete公開
Delete one buyback receipt by its receipt number.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Receipt number, e.g. 'KIT-BUY-2026-0001'. |
レスポンス例
{"ok":true,"id":"KIT-BUY-2026-0001"} 月次レポート (Report)
顧客別・月次のレポート生成と保存。
GET/api/reports公開
List saved reports for this workspace, newest first, with the count.
レスポンス例
{"ok":true,"reports":[],"count":0}
GET/api/report/generate公開
Compute a report preview from this workspace’s own records WITHOUT persisting it. hasData is false when nothing in the period could be computed.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
cust | query | string | — | Customer name to scope the report to. Omit for 全顧客. |
month | query | string | — | Target month as YYMM, e.g. '2608'. Defaults to the current month. |
レスポンス例
{"ok":true,"report":{"id":"RPT-2608-all","cust":"","custLabel":"全顧客","month":"2608","monthLabel":"2026年8月度","hasData":false,"figures":{"dev":178,"kit":0,"swap":0,"tk":0,"slaPct":null,"sla":"—","lost":0,"wipe":0,"inb":0,"out":0,"store":178,"revenue":0,"wipeCerts":27},"scope":{"kit":"month","swap":"month","tk":"month","slaPct":"month","sla":"month","lost":"month","wipe":"month","inb":"month","out":"month","revenue":"month","dev":"current","store":"current","wipeCerts":"current"},"asOf":1787863913867,"undated":{"jobs":0,"tickets":0,"shipments":0},"generatedAt":1787863913867},"hasData":false}
POST/api/report/generate公開
Compute AND persist a report; the saved record is keyed RPT-<YYMM>-<cust|all> so regenerating the same period overwrites it.
リクエスト
{"cust":"北斗商事","month":"2608"}レスポンス例
{"ok":true,"report":{"id":"RPT-2608-all","cust":"","custLabel":"全顧客","month":"2608","monthLabel":"2026年8月度","hasData":false,"figures":{…},"scope":{…},"asOf":1787863942144,"undated":{"jobs":0,"tickets":0,"shipments":0},"generatedAt":1787863942144},"hasData":false}
GET/api/report/:id公開
Fetch one saved report by id.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Report id, e.g. 'RPT-2608-all'. 404 {ok:false,error:'report not found'} if unknown. |
レスポンス例
{"ok":true,"report":{"id":"RPT-2608-all","cust":"","custLabel":"全顧客","month":"2608","monthLabel":"2026年8月度","hasData":false,"figures":{…},"scope":{…},"asOf":1787863942144,"undated":{"jobs":0,"tickets":0,"shipments":0},"generatedAt":1787863942144}} 管理者 (Admin)
主管理者認証・テナント管理・サブ管理者CRUD。要adminトークン
POST/api/admin/login公開
Master-admin login; returns an 8h HMAC-signed bearer token when the password matches ADMIN_PASSWORD.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
password | body | string | ○ | Master admin password (env ADMIN_PASSWORD, default 'kitting-admin') |
リクエスト
{"password":"kitting-admin"}レスポンス例
{"ok":true,"token":"<body>.<sig>"} | 401 {"ok":false,"error":"パスワードが違います"}
GET/api/admin/tenantsadminlive 401
List all tenants plus aggregate stats (totals, active/trial/suspended counts, MRR, devices, seats, sites, clients).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | | Admin token (alternative to Authorization: Bearer header) |
レスポンス例
{"ok":true,"tenants":[{"id":"t-itabashi","name":"板橋キッティングセンター","plan":"Business","seats":180,"used":120,"status":"active","mrr":582000,"devices":1204,"clients":[...],"features":{...}}],"stats":{"total":5,"active":3,"trial":1,"suspended":1,"mrr":2487000,...},"origin":"https://..."}
GET/api/admin/tenant/:idadminlive 401
Get one tenant with derived client dashboard URLs and published-site URLs.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Tenant id, e.g. t-itabashi |
token | query | string | | Admin token (alt to Bearer header) |
レスポンス例
{"ok":true,"tenant":{...},"dashboards":[{"client":"北斗商事","url":".../portal.html?t=..."}],"published":[{"slug":"itabashi-kitting","url":".../site/itabashi-kitting","live":true}],"origin":"https://..."}
POST/api/admin/tenant/:idadmin
Update an existing tenant's editable fields (name, plan, seats, used, status, email, devices, mrr, features, clients).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Tenant id to update |
name | body | string | | Tenant display name |
plan | body | string | | Starter|Business|Enterprise |
seats | body | number | | Licensed seats |
used | body | number | | Seats used |
status | body | string | | active|trial|suspended |
email | body | string | | Contact email |
devices | body | number | | Managed device count |
mrr | body | number | | Monthly recurring revenue |
features | body | object | | Feature-flag object merged into existing |
clients | body | array | | Client name array (capped at 200) |
リクエスト
{"plan":"Enterprise","seats":300,"status":"active","features":{"daas":true}}レスポンス例
{"ok":true,"tenant":{...updated...}}
POST/api/admin/tenantadmin
Create a new tenant (auto-generated id 't-<slug>-<hex>'), defaults to Starter/trial with base feature flags.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
name | body | string | | Tenant name (default '新規テナント', 80-char cap) |
plan | body | string | | Plan (default Starter) |
seats | body | number | | Seats (default 3) |
email | body | string | | Contact email |
リクエスト
{"name":"新規センター","plan":"Business","seats":50,"email":"x@example.jp"}レスポンス例
{"ok":true,"tenant":{"id":"t-...-a1b2","status":"trial","used":0,"mrr":0,"features":{...}}}
POST/api/admin/tenant/:id/deleteadmin
Delete a tenant from the store and persist.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Tenant id to delete |
レスポンス例
{"ok":true}
POST/api/admin/tenant/:id/clientadmin
Add a client name to a tenant and return that client's portal dashboard URL.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Tenant id |
client | body | string | ○ | Client name (80-char cap; 400 if empty) |
リクエスト
{"client":"新規クライアント"}レスポンス例
{"ok":true,"url":".../portal.html?t=%E6%96%B0%E8%A6%8F..."}
POST/api/admin/tenant/:id/client/revokeadmin
顧客ポータルのアクセストークンを失効させる。以後、その顧客に発行済みの /portal.html?t=… は全て無効になり、再発行が必要。
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | テナントID |
リクエスト
{"client":"北斗商事"}レスポンス例
{"ok":true,"client":"北斗商事","revocations":1}エラー
400 — {"ok":false,"error":"client name required"} — client が未指定
401 — {"ok":false,"error":"unauthorized"} — 管理者トークンがありません
404 — {"ok":false,"error":"not found"} — テナントが存在しない
POST/api/admin/impersonate/:idadmin
Return an assist-mode ('admin_as') app URL for the given tenant (app shows a disclaimer banner).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Tenant id to impersonate |
レスポンス例
{"ok":true,"url":".../app?admin_as=%E6%9D%BF%E6%A9%8B..."}
GET/api/admin/subadminsadminlive 401
List all sub-admins (public-safe fields, no passwords) plus a compact {id,name} tenant list for assignment.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | | Admin token (alt to Bearer header) |
レスポンス例
{"ok":true,"subadmins":[{"id":"sa-demo","name":"サポート担当 桐生","email":"kiryu@ai-sns.io","tenants":["t-itabashi","t-nagoya"],"perms":{"canDelete":true},"active":true,"created":"2026-06-01"}],"tenants":[{"id":"t-itabashi","name":"板橋キッティングセンター"}]}
POST/api/admin/subadminadmin
Create a sub-admin; validates required name/email and unique email, auto-generates a password if none supplied.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
name | body | string | ○ | Sub-admin name (80-char cap) |
email | body | string | ○ | Unique email |
password | body | string | | Login password (random hex if omitted) |
tenants | body | array | | Assigned tenant ids (cap 200) |
canDelete | body | boolean | | Grant delete permission |
リクエスト
{"name":"新担当","email":"new@ai-sns.io","tenants":["t-osaka"],"canDelete":false}レスポンス例
{"ok":true,"subadmin":{...},"password":"<pass>"}
POST/api/admin/subadmin/:idadmin
Update a sub-admin's name, email, password, assigned tenants, canDelete perm, or active flag.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Sub-admin id |
name | body | string | | New name |
email | body | string | | New email |
password | body | string | | New password |
tenants | body | array | | Reassign tenant ids |
canDelete | body | boolean | | Toggle delete perm |
active | body | boolean | | Enable/disable account |
リクエスト
{"active":false,"canDelete":true}レスポンス例
{"ok":true,"subadmin":{...updated...}}
POST/api/admin/subadmin/:id/deleteadmin
Delete a sub-admin and persist.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Sub-admin id to delete |
レスポンス例
{"ok":true}
GET/api/admin/backupsadminlive 401
Return the global backup log (up to 120 newest snapshots across all tenants) plus daily-backup status.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | | Admin token (alt to Bearer header) |
レスポンス例
{"ok":true,"log":[{"tenantId":"t-itabashi","tenantName":"板橋キッティングセンター","id":"bk-...","ts":1699999999999,"by":"system","reason":"日次自動バックアップ","sites":1}],"daily":{"last":1699999999999,"tenants":5}}
GET/api/admin/activityadminlive 401
Return the audit/activity log (newest first, up to 200 entries).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | | Admin token (alt to Bearer header) |
レスポンス例
{"ok":true,"activity":[{"ts":1699999999999,"by":"サポート担当 桐生","role":"subadmin","action":"パスワードを変更","tenantId":"","tenantName":"","detail":""}]}
GET/api/admin/workspacesadmin
The real customer list: every signed-up account with the plan actually enforced (planKey/planStatus/planSource + limits), the plan catalog, and the OPERATOR_TENANT configuration state.
レスポンス例
{"ok":true,"workspaces":[{"email":"demo.user@example.com","company":"北斗商事","name":"山田 太郎","tenantId":"t-…","created":"2026-08-27T20:52:56.379Z","lastLogin":"2026-08-27T20:53:22.052Z","planKey":"starter","planStatus":"none","planSource":"default","limits":{"devices":100,"members":3,"customers":10}}],"plans":[{"key":"starter","name":"Starter","priceJpy":128000,"limits":{"devices":100,"members":3,"customers":10}},{"key":"business","name":"Business","priceJpy":388000,"limits":{"devices":300,"members":10,"customers":30}},{"key":"enterprise","name":"Enterprise","priceJpy":908000,"limits":{"devices":null,"members":null,"customers":null}}],"operatorTenant":{"configured":false,"value":null,"resolves":false,"affects":["/d/<slug>-<suffix>/<last6> の端末ステータスページ","公開フォームからのリード/予約の帰属先"],"note":"OPERATOR_TENANT が未設定です。…"}}
POST/api/admin/workspace/:tid/planadmin
Grant/change the plan a workspace actually gets — the override for customers billed by invoice rather than Stripe self-serve. Writes an activity-log entry and returns the before/after plan.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tid | path | string | ○ | Workspace (tenant) id from GET /api/admin/workspaces. 400 for a malformed id, 404 {ok:false,error:'no such workspace'} when no account owns it. |
リクエスト
{"planKey":"business","status":"active","reason":"invoice-billed"}レスポンス例
{"ok":true,"tenantId":"t-…","planKey":"business","status":"active","previous":"starter","limits":{"devices":300,"members":10,"customers":30}}
POST/api/admin/workspace/:tid/impersonateadmin
Assist mode: mint a SHORT-LIVED session for the account that owns the workspace and return a /app?admin_as=… link. It grants exactly the customer’s own permissions (never more, never another tenant) and ends as soon as that customer changes their password. Logged as assist-start.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tid | path | string | ○ | Workspace (tenant) id from GET /api/admin/workspaces. |
レスポンス例
{"ok":true,"url":"https://kit-agent.ai-sns.io/app?admin_as=%E5%8C%97%E6%96%97%E5%95%86%E4%BA%8B&assist=<token>","expiresAt":1787865802201}
GET/api/admin/accountsadmin
List every end-user account (email, company, name, tenantId, created, lastLogin). No password material is returned.
レスポンス例
{"ok":true,"accounts":[]}
POST/api/admin/account/:email/deleteadmin
Delete one end-user account. The workspace data blob itself is left intact (recoverable); the tenant stops being gated only when no other account still owns it.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
email | path | string | ○ | URL-encoded account email, e.g. 'demo.user%40example.com'. |
レスポンス例(404 case captured; the 200 body is {ok:true,removed:{email,tenantId}})
{"ok":false,"error":"account not found"}
GET/api/admin/configadmin
Full global site config including the security toggles, plus meta (updatedAt/updatedBy) and the shipped defaults.
レスポンス例
{"ok":true,"config":{"maintenanceMode":false,"maintenanceMessage":"ただいまメンテナンス中です。しばらくしてから再度お試しください。","registrationOpen":true,"announcementEnabled":false,"announcementText":"","announcementLevel":"info","demoEnabled":true,"signinEnabled":true,"chatBubbleEnabled":true,"primaryCtaLabel":"","secEnabled":true,"secIpGuard":true,"secIpBlocklist":"","secLoginThrottle":true,"secMaxAttempts":8,"secGenericErrors":true,"secValidityCap":30,"secRequirePII":false,"sec2FA":false,"secLoginNotify":false,"secDeviceCheck":false,"secBehavioral":false,"sec3DS":false,"secNoLogin":false},"meta":{"updatedAt":null,"updatedBy":null},"defaults":{…}}
PUT/api/admin/configadmin
Patch the global site config. Accepts either {config:{…}} or a bare patch object; only known keys are applied and meta records the change as 'admin'.
リクエスト
{"config":{"announcementEnabled":true,"announcementText":"9/1 メンテナンス"}}レスポンス例
{"ok":true,"config":{"maintenanceMode":false,"registrationOpen":true,"announcementEnabled":true,"announcementText":"9/1 メンテナンス","announcementLevel":"info","demoEnabled":true,"signinEnabled":true,"chatBubbleEnabled":true,"primaryCtaLabel":"","secEnabled":true,…},"meta":{"updatedAt":null,"updatedBy":null}}
GET/api/admin/delete-requestsadmin
Pending sub-admin delete approvals with their access codes — the out-of-band channel that makes a destructive delete need a second human. Expired requests are dropped on read.
レスポンス例
{"ok":true,"requests":[]}
GET/api/admin/security/eventsadmin
The 200 most recent security countermeasure events (sign-up, auth failures, blocks, MFA challenges).
レスポンス例
{"ok":true,"events":[]} サブ管理者 (Sub-Admin)
担当クライアント限定の運用(メモ・ファイル・削除)。要subadminトークン
POST/api/subadmin/login公開
Sub-admin login; matches email+password against the subadmins map and issues an 8h HMAC token.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
email | body | string | ○ | Sub-admin email, lowercased/trimmed before match (seed: kiryu@ai-sns.io) |
password | body | string | ○ | Plaintext password; seed default 'kitting-sub' (or SUBADMIN_PASSWORD env) |
リクエスト
{"email":"kiryu@ai-sns.io","password":"kitting-sub"}レスポンス例
{"ok":true,"token":"<b64>.<sig>","sub":{"id":"sa-demo","name":"サポート担当 桐生","email":"kiryu@ai-sns.io","tenants":["t-itabashi","t-nagoya"],"perms":{"canDelete":true},"active":true,"created":"2026-06-01"}}
GET/api/subadmin/mesubadminlive 401
Returns the authenticated sub-admin profile, their in-scope tenants, and aggregate stats.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | | Alternative to Authorization: Bearer header |
レスポンス例
{"ok":true,"sub":{...},"tenants":[{"id":"t-itabashi",...}],"stats":{"tenants":2,"devices":1414,"clients":5,"sites":1},"origin":"https://kit-agent.ai-sns.io"}
GET/api/subadmin/tenant/:idsubadminlive 401
Scoped read of one tenant plus derived dashboards, published sites, backups, and the sub-admin's perms.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Tenant id, must be in sub-admin scope (seed: t-itabashi, t-nagoya) |
token | query | string | | Alternative to Bearer header |
レスポンス例
{"ok":true,"tenant":{"id":"t-itabashi","clients":["北斗商事",...],"sites":["itabashi-kitting"]},"dashboards":[{"client":"北斗商事","url":".../portal.html?t=..."}],"published":[{"slug":"itabashi-kitting","url":".../site/itabashi-kitting","live":true}],"backups":[],"perms":{"canDelete":true},"origin":"..."}
POST/api/subadmin/tenant/:idsubadmin
Scoped non-core tenant edit; only the contact email can be changed, main settings are blocked.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Tenant id, must be in scope |
email | body | string | | New contact email (max 120 chars) |
plan/seats/mrr/status/features | body | any | | If any present -> 403; sub-admins may not change core settings |
リクエスト
{"email":"new@example.co.jp"}レスポンス例
{"ok":true,"tenant":{"id":"t-itabashi","email":"new@example.co.jp",...}}
POST/api/subadmin/tenant/:id/clientsubadmin
Adds a client name to a scoped tenant and returns its portal URL.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Tenant id, must be in scope |
client | body | string | ○ | Client name (max 80 chars); 400 if empty |
リクエスト
{"client":"新規クライアント"}レスポンス例
{"ok":true,"url":"https://kit-agent.ai-sns.io/portal.html?t=%E6%96%B0%E8%A6%8F..."}
POST/api/subadmin/impersonate/:idsubadmin
Generates an assisted-login URL into the client app for a scoped tenant (support impersonation).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Tenant id, must be in scope |
レスポンス例
{"ok":true,"url":"https://kit-agent.ai-sns.io/app?admin_as=%E6%9D%BF%E6%A9%8B...&by=subadmin"}
POST/api/subadmin/delete-requestsubadmin
Requests deletion of a client/site/tenant; generates a 6-digit access code (10-min expiry) gated by canDelete perm.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | body | string | ○ | Tenant id, must be in scope |
targetType | body | string | | 'client'|'site'|'tenant', defaults to 'client' |
targetId | body | string | | Id/name of the thing to delete (max 120) |
リクエスト
{"tenantId":"t-itabashi","targetType":"client","targetId":"北斗商事"}レスポンス例
{"ok":true,"requestId":"dr-<hex>","sentTo":["itabashi@example.co.jp","マスター管理者"],"expiresInMin":10,"demoCode":"123456"}
POST/api/subadmin/delete-confirmsubadmin
Confirms a delete request with the emailed access code and performs the scoped delete after auto-backup.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
requestId | body | string | ○ | Id from delete-request; must belong to this sub-admin |
code | body | string | ○ | 6-digit access code |
リクエスト
{"requestId":"dr-<hex>","code":"123456"}レスポンス例
{"ok":true,"targetType":"client"}
GET/api/subadmin/activitysubadminlive 401
Returns up to 150 recent activity-log entries filtered to the sub-admin's own actions or in-scope tenants.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | | Alternative to Bearer header |
レスポンス例
{"ok":true,"activity":[{"ts":1700000000000,"by":"サポート担当 桐生","role":"subadmin","action":"サポートメモを追加","tenantId":"t-itabashi","tenantName":"板橋キッティングセンター","detail":"..."}]}
GET/api/subadmin/backups-allsubadminlive 401
Aggregated backup log across all of the sub-admin's tenants, newest first, capped at 120 rows.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | | Alternative to Bearer header |
レスポンス例
{"ok":true,"log":[{"tenantId":"t-itabashi","tenantName":"板橋キッティングセンター","id":"bk-...","ts":1700000000000,"reason":"...","by":"..."}]}
POST/api/subadmin/passwordsubadmin
Self-service password change for the authenticated sub-admin.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
current | body | string | ○ | Current password; 401 if wrong (plaintext compare) |
next | body | string | ○ | New password, min 6 chars else 400 |
リクエスト
{"current":"kitting-sub","next":"newpass123"}レスポンス例
{"ok":true}
GET/api/notes/:tenantIdsubadminlive 401
Lists support notes for a tenant (admin any tenant, sub-admin scoped).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | path | string | ○ | Tenant id; sub-admin must have it in scope (seed: t-itabashi) |
token | query | string | | Alternative to Bearer header |
レスポンス例
{"ok":true,"notes":[{"id":"nt-...","ts":1700000000000,"by":"サポート担当 桐生","text":"...","done":false}]}
POST/api/notes/:tenantIdsubadmin
Adds a support note (max 600 chars) to a tenant's note list.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | path | string | ○ | Tenant id, scoped for sub-admin |
text | body | string | ○ | Note text, trimmed, max 600; 400 if empty |
リクエスト
{"text":"クライアントへ連絡済み"}レスポンス例
{"ok":true,"note":{"id":"nt-...","ts":1700000000000,"by":"サポート担当 桐生","text":"...","done":false}}
POST/api/notes/:tenantId/:noteIdsubadmin
Updates (text/done) or deletes a specific support note.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | path | string | ○ | Tenant id, scoped |
noteId | path | string | ○ | Note id (nt-...) |
del | body | boolean | | If truthy, delete the note and return {ok:true} |
done | body | boolean | | Toggle done flag |
text | body | string | | New text (max 600) |
リクエスト
{"done":true}レスポンス例
{"ok":true,"note":{"id":"nt-...","done":true,"text":"..."}}
GET/api/files/:tenantIdsubadminlive 401
Lists uploaded file metadata (no data blob) for a tenant.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | path | string | ○ | Tenant id, scoped (seed: t-itabashi) |
token | query | string | | Alternative to Bearer header |
レスポンス例
{"ok":true,"files":[{"id":"fl-...","ts":1700000000000,"by":"サポート担当 桐生","name":"手順書.pdf","type":"application/pdf","size":10240}]}
POST/api/files/:tenantIdsubadmin
Uploads a base64 file/document (up to ~2MB) attached to a tenant.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | path | string | ○ | Tenant id, scoped |
name | body | string | | File name (max 160), defaults 'file' |
type | body | string | | MIME type (max 80) |
data | body | string | ○ | Base64/data payload; 400 if empty, 413 if >2,800,000 chars |
size | body | number | | Reported size; defaults to data.length |
リクエスト
{"name":"doc.pdf","type":"application/pdf","data":"<base64>"}レスポンス例
{"ok":true,"file":{"id":"fl-...","ts":1700000000000,"by":"...","name":"doc.pdf","type":"application/pdf","size":12345}}
GET/api/files/:tenantId/:fileId/downloadsubadminlive 401
Returns a single file's full base64 data for download.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | path | string | ○ | Tenant id, scoped |
fileId | path | string | ○ | File id (fl-...); 404 if not found |
token | query | string | | Alternative to Bearer header |
レスポンス例
{"ok":true,"name":"doc.pdf","type":"application/pdf","data":"<base64>"}
POST/api/files/:tenantId/:fileId/deletesubadmin
Deletes an uploaded file from a tenant's file list.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | path | string | ○ | Tenant id, scoped |
fileId | path | string | ○ | File id to remove |
レスポンス例
{"ok":true}
POST/api/subadmin/workspace/:tid/impersonatesubadmin
Assist mode for a sub-admin, restricted to the tenants assigned to them. It grants the customer’s own permissions only — plan/seats/mrr/status/features stay out of reach. Logged as assist-start.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tid | path | string | ○ | Workspace (tenant) id that must be in this sub-admin’s assigned scope. |
バックアップ (Backup/Export)
日次バックアップ・状態エクスポート。要BACKUP_TOKEN
GET/api/admin/backupsadminlive 401
Master-admin global backup log (last 120 snapshot rows across all tenants) plus daily auto-backup status.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | | Admin JWT; alternatively sent as Authorization: Bearer <token>. Required. |
レスポンス例
{"ok":true,"log":[{"tenantId":"t-itabashi","tenantName":"板橋キッティングセンター","id":"bk-...","ts":1784600000000,"by":"system","reason":"日次自動バックアップ","sites":1}],"daily":{"last":1784600000000,"tenants":5}}
GET/api/backups/:tenantIdadminlive 401
List backup snapshot metadata for one tenant (newest first), for admin or the scoped sub-admin.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | path | string | ○ | Tenant id, e.g. t-itabashi (seed tenants: t-itabashi, t-osaka, t-nagoya, t-fukuoka, t-sapporo). |
token | query | string | | Admin or sub-admin JWT; also accepted as Authorization: Bearer. Required. |
レスポンス例
{"ok":true,"backups":[{"id":"bk-lz...","ts":1784600000000,"by":"system","reason":"日次自動バックアップ","sites":1}]}
GET/api/subadmin/backups-allsubadminlive 401
Scope-wide backup log for the logged-in sub-admin: last 120 snapshot rows across only their assigned tenants.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | | Sub-admin JWT (role==='subadmin'); also accepted as Authorization: Bearer. Required. |
レスポンス例
{"ok":true,"log":[{"tenantId":"t-nagoya","tenantName":"名古屋モバイル管理","id":"bk-...","ts":1784600000000,"by":"system","reason":"日次自動バックアップ","sites":0}]}
GET/api/export/statetokenlive 200
Full runtime-data dump (all tenants/sites/subadmins/backups/leads/signs/bookings/etc. + counts) for automated off-site backups.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | | Backup token; must equal env BACKUP_TOKEN (default 'kitting-backup-token'). Alternatively header x-backup-token. Required (one of the two). |
レスポンス例
{"ok":true,"exportedAt":1784675572317,"space":"Aisnsio/kitting","tenants":{"t-itabashi":{...}},"sites":{...},"backups":{...},"counts":{"tenants":5,"backups":5,...}}
POST/api/backup/:tenantIdadmin
Create an on-demand snapshot of one tenant (deep-copies tenant record + its sites) with an optional reason label.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | path | string | ○ | Tenant id to snapshot, e.g. t-itabashi. |
token | query | string | | Admin or scoped sub-admin JWT (or Authorization: Bearer). Required. |
reason | body | string | | Backup reason/label, sliced to 120 chars; defaults to '手動バックアップ'. |
リクエスト
{"reason":"設定変更前の手動バックアップ"}レスポンス例
{"ok":true,"backup":{"id":"bk-lz9x...","ts":1784675000000,"by":"マスター管理者","reason":"手動バックアップ","sites":1}}
POST/api/restore/:tenantId/:backupIdadmin
Rollback: restore a tenant (and its sites) from a stored snapshot; auto-snapshots current state first for recoverability.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
tenantId | path | string | ○ | Tenant id to roll back, e.g. t-itabashi. |
backupId | path | string | ○ | Snapshot id to restore, e.g. bk-lz9x2a (obtained from GET /api/backups/:tenantId). |
token | query | string | | Admin or scoped sub-admin JWT (or Authorization: Bearer). Required. |
レスポンス例
{"ok":true,"tenant":{"id":"t-itabashi","name":"板橋キッティングセンター","plan":"Business","sites":["itabashi-kitting"],...}} 公開フォーム・OAuth・その他 (Misc)
申込/パートナー/予約/署名/CRM・OAuth連携。
POST/api/synthia公開
Synthia AI chat proxy: forwards message+history to Anthropic Claude if ANTHROPIC_API_KEY is set, else returns a fallback flag so the client uses its built-in mock.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
message | body | string | ○ | User message (sliced to 4000 chars) |
history | body | array | | Prior turns [{role:'user'|'assistant',content}], last 8 used |
リクエスト
{"message":"string","history":[{"role":"user","content":"..."}]}レスポンス例
{"ok":false,"fallback":true,"reason":"no_key"} | {"ok":true,"text":"...","model":"claude-opus-4-8"}
GET/api/marketprice公開live 200
Live used-device market-price lookup for trade-in/buyback via SerpAPI / Yahoo Shopping / Google CSE; returns reference note if no key configured.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
model | query | string | ○ | Device model keyword (<=80 chars); 400 if missing |
cond | query | string | | Condition keyword (<=24 chars) |
レスポンス例
{"ok":true,"live":false,"model":"iPhone 13","note":"ライブ相場APIキーが未設定です…"}
GET/api/connect/status要サインインlive 200
Lists all 13 integration providers (MDM/Apple/shipping/accounting/chat) with connected state, configured flag, redirectUri and go-live guide.
レスポンス例
{"ok":true,"origin":"https://kit-agent.ai-sns.io","providers":[{"key":"intune","name":"Microsoft Intune","group":"MDM","authType":"oauth","connected":false,"configured":false,"guide":{...}}]}
GET/api/connect/:provider要サインインlive 200
Starts OAuth: redirects (302) to the provider authorize URL for oauth providers, or returns an HTML popup for credential providers / unset creds.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
provider | path | string | ○ | Provider key e.g. intune, freee, moneyforward, lineworks |
レスポンス例
HTTP 200 text/html popup, OR 302 redirect to authorize URL, OR 404 text 'unknown provider'
GET/api/connect/:provider/callback公開live 200
OAuth redirect callback: validates state, exchanges the code for tokens against the provider, stores them, and returns an HTML popup that postMessages the opener.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
provider | path | string | ○ | Provider key |
code | query | string | | OAuth authorization code |
state | query | string | | CSRF state from the authorize step |
error | query | string | | Provider error code |
レスポンス例
HTTP 200 text/html popup ('接続に失敗しました' on state mismatch / '接続が完了しました' on success)
POST/api/connect/:provider/credentials要サインイン
Saves API-key/token/webhook credentials for a credential-type provider; for webhook providers sends a real test POST to validate.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
provider | path | string | ○ | Credential provider key e.g. jamf, teams, sorachi |
fields | body | object | ○ | Map of field key->value; all provider fields required |
リクエスト
{"fields":{"webhook":"https://..."}}レスポンス例
{"ok":true,"tested":true} | {"ok":false,"error":"...が未入力です"}
POST/api/connect/:provider/disconnect要サインイン
Removes stored tokens/credentials for a provider.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
provider | path | string | ○ | Provider key |
レスポンス例
{"ok":true}
POST/api/site/publish公開
Publishes a white-label tenant marketing site (slug+HTML) served at /site/<slug>.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
slug | body | string | ○ | a-z0-9- slug (<=40); 400 if empty after sanitize |
html | body | string | ○ | Site HTML (<=800000 chars) |
name | body | string | | Display name (<=120) |
リクエスト
{"slug":"acme","html":"<html>..","name":"Acme"}レスポンス例
{"ok":true,"slug":"acme","url":"https://.../site/acme"}
GET/api/site/list公開live 200
Lists all published white-label sites with slug, name, updated timestamp and public URL.
レスポンス例
{"ok":true,"sites":[{"slug":"acme","name":"Acme","updated":1780000000000,"url":"https://.../site/acme"}]}
POST/api/site/:slug/unpublish公開
Deletes a published site by slug.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
slug | path | string | ○ | Site slug to remove |
レスポンス例
{"ok":true}
GET/site/:slug公開
Serves the published tenant site's raw HTML (not an /api path); 404 HTML page if slug unknown.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
slug | path | string | ○ | Published site slug |
レスポンス例
<!doctype html>...stored site HTML... (or 404 HTML)
POST/api/lead公開
Public lead capture: stores a CRM lead from site widgets/forms.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
name | body | string | | Contact name (<=120) |
email | body | string | | Email (<=160) |
company | body | string | | Company (<=160); at least one of name/email/company required |
phone | body | string | | Phone (<=40) |
message | body | string | | Message (<=1500) |
source | body | string | | Source tag (<=60), default 'web' |
tenant | body | string | | Owning tenant (<=80) |
リクエスト
{"name":"..","email":"..","company":"..","message":".."}レスポンス例
{"ok":true,"id":"L-xxxx"}
GET/api/leads公開live 200
Returns all CRM leads (newest first).
レスポンス例
{"ok":true,"leads":[{"id":"L-..","name":"QA Test","email":"qa@example.com","source":"signup","status":"new","created":1784675609773}]}
POST/api/lead/:id公開
Updates a lead's status/notes/contact fields.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Lead id |
status | body | string | | Lead status |
notes | body | string | | Notes (<=3000) |
name/company/email/phone | body | string | | Contact fields (<=200) |
リクエスト
{"status":"contacted","notes":".."}レスポンス例
{"ok":true,"lead":{...}} | 404 {"ok":false}
POST/api/lead/:id/delete公開
Deletes a lead by id.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Lead id |
レスポンス例
{"ok":true}
POST/api/sign/create公開
Creates a signable e-signature document (quote/contract) and returns its signing URL.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
title | body | string | | Doc title (<=160), default '御見積書' |
client | body | string | | Client name (<=120) |
email | body | string | | Signer email (<=160) |
amount | body | string | | Amount label (<=40) |
bodyHtml | body | string | | Document body HTML (<=200000) |
リクエスト
{"title":"..","client":"..","amount":"..","bodyHtml":".."}レスポンス例
{"ok":true,"id":"SG-xxxx","url":"https://.../sign.html?id=SG-xxxx"}
GET/api/sign/:id公開live 404
Fetches a signable document (public signing page reads this).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Document id SG-xxxx |
レスポンス例
{"ok":true,"doc":{"id":"SG-..","title":"..","status":"pending",...}} | 404 {"ok":false,"error":"not found"}
POST/api/sign/:id/submit公開
Signs a document: records signer name/signature/timestamp/IP and marks status 'signed'.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Document id |
name | body | string | ○ | Signer name (<=120) |
agreed | body | boolean | ○ | Consent flag; 400 if false/missing |
signature | body | string | | Signature image data (<=200000) |
リクエスト
{"name":"..","agreed":true,"signature":"data:.."}レスポンス例
{"ok":true,"status":"signed","signedAt":1780000000000}
GET/api/sign/:id/status公開live 404
Returns just the signing status of a document.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Document id |
レスポンス例
{"ok":true,"status":"pending","signedName":"","signedAt":0} | 404 {"ok":false}
GET/api/signs公開live 200
Lists all e-signature documents (metadata, newest first).
レスポンス例
{"ok":true,"signs":[{"id":"SG-..","title":"..","client":"..","status":"pending","created":..}]}
POST/api/booking公開
Public consultation booking; also creates a linked CRM lead with status 'meeting'.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
name | body | string | | Name (<=120) |
email | body | string | | Email (<=160); at least one of name/email required |
company | body | string | | Company (<=160) |
phone | body | string | | Phone (<=40) |
when | body | string | | Requested time (<=60) |
topic | body | string | | Topic (<=400) |
リクエスト
{"name":"..","email":"..","when":"..","topic":".."}レスポンス例
{"ok":true,"id":"B-xxxx"}
GET/api/bookings公開live 200
Lists all consultation bookings (newest first).
レスポンス例
{"ok":true,"bookings":[{"id":"B-..","name":"..","email":"..","status":"requested","created":..}]}
POST/api/booking/:id公開
Updates a booking's status.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Booking id |
status | body | string | | New status (<=30) |
リクエスト
{"status":"confirmed"}レスポンス例
{"ok":true,"booking":{...}} | 404 {"ok":false}
POST/api/referral公開
Creates or updates a referral code for a referrer and returns the shareable ?ref= URL.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
referrer | body | string | | Referrer name (<=120), default '紹介者' |
code | body | string | | Desired code (<=24); auto-generated if omitted |
リクエスト
{"referrer":"..","code":".."}レスポンス例
{"ok":true,"code":"abc12","url":"https://.../?ref=abc12"}
GET/api/referrals公開live 200
Lists all referral codes with click/lead counts.
レスポンス例
{"ok":true,"referrals":[{"code":"abc12","referrer":"..","clicks":0,"leads":0,"created":..}]}
POST/api/referral/:code/track公開
Increments the click counter for a referral code.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
code | path | string | ○ | Referral code (case-insensitive) |
レスポンス例
{"ok":true}
POST/api/signup公開
Self-service trial signup: creates a trial (Starter) tenant and a linked CRM lead, returns the prospect app URL.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
email | body | string | ○ | Email (<=160); 400 if empty |
company | body | string | | Company (<=80), used as tenant name |
name | body | string | | Contact name (<=80) |
plan | body | string | | Requested plan label (stored in lead message) |
リクエスト
{"email":"..","company":"..","name":".."}レスポンス例
{"ok":true,"tenant":{"id":"t-..","name":".."},"url":"https://.../app?prospect=.."}
POST/api/partner公開
Partner/agency program application; creates a partner record + referral code + linked lead.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
company | body | string | ○ | Company (<=120); 400 if empty |
email | body | string | ○ | Email (<=160); 400 if empty |
contact | body | string | | Contact name (<=120) |
region | body | string | | Region (<=60) |
note | body | string | | Note (<=800) |
リクエスト
{"company":"..","email":"..","contact":"..","region":".."}レスポンス例
{"ok":true,"id":"P-xxxx","code":"acme12","referralUrl":"https://.../?ref=acme12"}
GET/api/partners公開live 200
Lists all partner applications (newest first).
レスポンス例
{"ok":true,"partners":[{"id":"P-..","company":"..","email":"..","tier":"Silver","status":"applied","code":".."}]}
POST/api/partner/:id公開
Updates a partner's status/tier/deals/revenue/note.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Partner id |
status/tier/note | body | string | | Text fields (<=800) |
deals/revenue | body | number | | Numeric metrics (coerced with +) |
リクエスト
{"status":"active","tier":"Gold","deals":3,"revenue":500000}レスポンス例
{"ok":true,"partner":{...}} | 404 {"ok":false}
POST/api/review公開
Public review/NPS submission (stored unapproved pending moderation).
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
comment | body | string | ○ | Review text (<=800); 400 if empty |
rating | body | number | | 1-5 (clamped) |
name | body | string | | Name (<=80), default '匿名' |
company | body | string | | Company (<=120) |
nps | body | number | | 0-10 (clamped), default rating*2 |
リクエスト
{"rating":5,"comment":"..","name":"..","nps":9}レスポンス例
{"ok":true}
GET/api/reviews公開live 200
Lists approved reviews with computed average rating, count and NPS score.
レスポンス例
{"ok":true,"reviews":[{"id":"RV-965da4","name":"情シス部長","company":"北斗商事","rating":5,"nps":9,...}],"avg":4.7,"count":3,"nps":..}
GET/api/events公開live 200
Returns the static list of 3 upcoming webinars/events.
レスポンス例
{"ok":true,"events":[{"id":"ev-mdm","title":"30分でわかる:Intune × キッティング自動化","date":"2026-08-05 15:00","seats":100}]}
POST/api/event/:id/register公開
Registers for a webinar; creates a linked CRM lead (source 'webinar').
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
id | path | string | ○ | Event id (ev-mdm|ev-lcm|ev-ai) |
email | body | string | ○ | Email (<=160); 400 if empty |
name | body | string | | Name (<=120) |
company | body | string | | Company (<=160) |
リクエスト
{"email":"..","name":"..","company":".."}レスポンス例
{"ok":true} | 404 {"ok":false} for unknown event
POST/api/ab公開
Records an A/B landing test event (view or convert) for variant a or b.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
variant | body | string | | 'a' or 'b' (default 'a') |
event | body | string | | 'view' or 'convert' (default 'view') |
リクエスト
{"variant":"b","event":"convert"}レスポンス例
{"ok":true}
GET/api/ab公開live 200
Returns current A/B test counters for variants a and b.
レスポンス例
{"ok":true,"stats":{"a":{"view":0,"convert":0},"b":{"view":1,"convert":0}}}
GET/api/export/statetokenlive 401
Full runtime-data dump (all in-memory collections + counts) for automated backups; requires the backup token.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
token | query | string | ○ | Backup token; alternatively X-Backup-Token header. Must equal BACKUP_TOKEN |
レスポンス例
{"ok":false,"error":"unauthorized"} (no token) | {"ok":true,"exportedAt":..,"tenants":{..},"leads":{..},"counts":{..}}
GET/healthz公開live 200
Health check.
レスポンス例
{"status":"ok"}
GET/api/public/status公開
Data source for the embeddable status widget: a published portal’s state, last-touch string and a COUNT of that customer’s devices. Anonymous, CORS-enabled and rate-limited; when the fleet cannot be counted it says so (computed:false + reason) instead of showing a number.
| パラメータ | 位置 | 型 | 必須 | 説明 |
|---|
site | query | string | ○ | Portal slug, e.g. 'hokuto'. 400 {ok:false,error:'site が不正です'} when malformed, 404 when no published portal matches. |
レスポンス例
{"ok":true,"site":"hokuto","published":true,"status":"operational","label":"稼働中","devices":{"total":124,"active":124,"computed":true},"lastTouch":"3分前","generated":1787863942136,"total":124,"active":124}