APIリファレンス
友だち・タグ・メッセージ・予約・Webhook購読をRESTで操作できます。Zapier連携もこのAPIの上に構築されています。
Base URL: https://l.oku-ai.co.jp/api/v1
認証
すべてのリクエストにAPIキーが必要です。キーはLokuダッシュボードの 開発者 > APIキー で発行します (発行は無料)。実データの読み書きは、対象プロジェクトのプランがProプラン以上のときに有効になります。 発行時にスコープ (権限) を選択でき、キーはリクエストに必要な最小のスコープだけを持つことを推奨します。
curl https://l.oku-ai.co.jp/api/v1/friends?limit=1 \ -H "Authorization: Bearer lk_xxxxxxxxxxxxxxxx"
キーが無効・期限切れ・プラン要件未達の場合は 401 を返します。スコープ不足は 403 です。
共通仕様
| ID | すべてのIDは26文字のULIDです (例: 01ARZ3NDEKTSV4RRFFQ69G5FAV)。 |
| レート制限 | 300リクエスト/分。超過時は 429 を返します。IPとAPIキーの両方で計測します。 |
| 冪等性 | POST /messages と POST /reservations は Idempotency-Key ヘッダが必須です (英数字と _ - で1–255文字)。同じキーでの再送は初回の結果を再生し、二重送信・二重予約を防ぎます。 |
| 命名規約 | 予約 (reservations) とWebhook購読 (webhook_endpoints) のレスポンスは snake_case、それ以外のリソースは camelCase です。各エンドポイントのレスポンス例が正です。 |
| 日時 | ISO 8601 (UTC) の文字列です。日付のみの項目は YYYY-MM-DD です。 |
友だち (Friends)
LINE公式アカウントの友だち。CRMの中心となるリソースです。
/friends友だちの一覧を取得します (作成日時の降順)。
friends:readクエリパラメータ
page | integer | ページ番号 (既定 1) |
limit | integer | 1ページの件数 (1–100、既定 20) |
search | string | 表示名の部分一致 |
status | string | following | blocked | unfollowed |
レスポンス例
{
"data": [
{
"id": "01ABC...",
"lineUserId": "U1234...",
"displayName": "山田 太郎",
"pictureUrl": "https://...",
"status": "following",
"score": 82,
"followedAt": "2026-07-01T00:00:00.000Z",
"lastMessageAt": "2026-07-10T09:30:00.000Z",
"messageCount": 12,
"createdAt": "2026-07-01T00:00:00.000Z"
}
],
"pagination": { "page": 1, "limit": 20, "total": 152, "totalPages": 8 }
}主なエラー
| 401 | Unauthorized | APIキーが無効・期限切れ・プラン要件未達 |
| 403 | Insufficient scope | friends:read が付与されていない |
| 429 | リクエストが多すぎます | レート制限超過 |
/friends/{id}/external-ids友だちに紐づく外部IDの一覧を取得します。
friends:readレスポンス例
{
"data": [
{
"id": "01XID...",
"friendId": "01ABC...",
"namespace": "shopify",
"externalId": "cus_12345",
"source": "api",
"createdAt": "2026-07-01T00:00:00.000Z",
"updatedAt": "2026-07-01T00:00:00.000Z"
}
]
}主なエラー
| 400 | invalid friend id | id が26文字のULIDでない |
| 404 | friend not found | 友だちが存在しない (他テナント含む) |
メッセージ (Messages)
友だちへのLINEメッセージ送信。月次のメッセージ配信枠を消費します。
/messagesテキストメッセージを送信します (最大5通/リクエスト)。
messages:writeヘッダ: Idempotency-Key 必須リクエストボディ (JSON)
friendId必須 | string | 友だちID (ULID)。status=following のみ送信可 |
messages必須 | array | { type: "text", text: string } の配列 (1–5件、text 最大5,000文字) |
レスポンス例
{ "success": true }同じ Idempotency-Key での再送は初回の結果を再生します (レスポンスに idempotent_replay: true が付きます)。
主なエラー
| 400 | Friend is not following | 友だちがブロック中・解除済み |
| 400 | Idempotency-Key header is required | ヘッダ欠如 |
| 409 | Request with same Idempotency-Key is already in-flight | 同キーの処理が進行中 |
| 429 | Monthly message quota exceeded | 月次メッセージ枠を超過 (remaining を含む) |
予約 (Reservations)
予約の作成・取得。一覧はカーソル方式 (Stripe互換のlist形式) です。レスポンスは snake_case です。
/reservations予約の一覧を取得します (新しい順)。
reservations:readクエリパラメータ
friendId | string | 友だちIDで絞り込み (ULID) |
eventId | string | 予約イベントIDで絞り込み (ULID) |
status | string | pending | confirmed | canceled | no_show | completed | cancel_requested |
from / to | string | 予約日の範囲 (YYYY-MM-DD) |
limit | integer | 件数 (1–100、既定 50) |
starting_after | string | カーソル (前ページ末尾の id) |
レスポンス例
{
"object": "list",
"data": [
{
"id": "01RESV...",
"event_id": "01EVT...",
"slot_id": "01SLOT...",
"friend_id": "01ABC...",
"status": "confirmed",
"payment_status": "not_required",
"reservation_date": "2026-07-20",
"start_time": "14:00",
"end_time": "15:00",
"number_of_guests": 1,
"staff_id": null,
"note": null,
"canceled_at": null,
"created_at": "2026-07-14T02:00:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}主なエラー
| 400 | status must be one of: ... | クエリの形式不正 |
/reservations/{id}予約を1件取得します。
reservations:readレスポンス例
{
"id": "01RESV...",
"event_id": "01EVT...",
"status": "confirmed",
"payment_status": "not_required",
"reservation_date": "2026-07-20",
"start_time": "14:00",
"end_time": "15:00",
"number_of_guests": 1,
"created_at": "2026-07-14T02:00:00.000Z"
}主なエラー
| 400 | id must be a 26-char ULID | idの形式不正 |
| 404 | reservation not found | 存在しない (他テナント含む) |
/reservations予約を作成します。枠の空きは原子的に確保され、満席時は 409 を返します。作成時に reservation.created Webhookが発火します。
reservations:writeヘッダ: Idempotency-Key 必須リクエストボディ (JSON)
eventId必須 | string | 予約イベントID (ULID) |
slotId必須 | string | 予約枠ID (ULID) |
friendId必須 | string | 友だちID (ULID) |
numberOfGuests | integer | 人数 (1–100、既定 1) |
note | string | メモ (最大500文字) |
staffId | string | 指名スタッフID (ULID) |
レスポンス例
{
"id": "01RESV...",
"event_id": "01EVT...",
"event_name": "カット予約",
"slot_id": "01SLOT...",
"reservation_date": "2026-07-20",
"start_time": "14:00",
"end_time": "15:00",
"number_of_guests": 1,
"friend_id": "01ABC...",
"staff_id": null,
"note": null
}成功時は 201 を返します。
主なエラー
| 400 | 予約は{n}分前までに行ってください | 受付締切 (minNotice) 超過 |
| 404 | event/slot/friend/staff not found | 対象が存在しない |
| 409 | slot_full | 満席 (または直前に枠が変更された) |
| 409 | duplicate_reservation | 同じ友だちの二重予約 |
/reservation_events予約イベント (メニュー) の一覧を取得します。active のもののみ返します。
reservations:readレスポンス例
{
"data": [
{
"id": "01EVT...",
"name": "カット予約",
"description": "所要60分",
"duration": 60,
"price": 5500,
"maxCapacity": 1,
"minNotice": 60,
"maxAdvance": 30,
"color": "#06C755",
"status": "active"
}
]
}/reservation_slots予約枠を取得します。既定では空きのある枠のみ返します (最大500件)。
reservations:readクエリパラメータ
eventId必須 | string | 予約イベントID (ULID) |
from | string | 開始日 YYYY-MM-DD (既定 今日) |
to | string | 終了日 YYYY-MM-DD (既定 from+30日) |
availableOnly | string | "false" で満席の枠も含める (既定 true) |
レスポンス例
{
"data": [
{
"id": "01SLOT...",
"eventId": "01EVT...",
"date": "2026-07-20",
"startTime": "14:00",
"endTime": "15:00",
"maxCapacity": 1,
"bookedCount": 0
}
],
"range": { "from": "2026-07-14", "to": "2026-08-13" },
"eventMaxCapacity": 1
}主なエラー
| 400 | eventId query param is required | eventId 欠如・形式不正 |
| 404 | event not found | イベントが存在しない |
外部ID (External IDs)
外部システム (Shopify・CRM等) の顧客IDとLokuの友だちを紐づけるIDマッピングです。1つの友だちは1つのnamespaceにつき1つの外部IDを持てます。
/external-ids外部IDを友だちに紐づけます。
friends:writeリクエストボディ (JSON)
friendId必須 | string | 友だちID (ULID) |
namespace必須 | string | 外部システムの識別子。英小文字・数字・_:- (最大64文字、例: shopify, shopify:store123) |
externalId必須 | string | 外部システム側のID (1–128文字) |
レスポンス例
{
"data": {
"id": "01XID...",
"friendId": "01ABC...",
"namespace": "shopify",
"externalId": "cus_12345",
"source": "api",
"createdAt": "2026-07-14T02:00:00.000Z",
"updatedAt": "2026-07-14T02:00:00.000Z"
}
}成功時は 201 を返します。
主なエラー
| 409 | external_id_in_use | 同じ namespace + externalId が別の友だちに紐づけ済み |
| 409 | friend_already_has_namespace | この友だちが同じ namespace で別の外部IDを保持 (付け替えは /external-ids/replace) |
/external-ids/replace外部IDを付け替えます (upsert)。同じ値なら何もしません。
friends:writeリクエストボディ (JSON)
friendId必須 | string | 友だちID (ULID) |
namespace必須 | string | 同上 |
externalId必須 | string | 同上 |
レスポンス例
{ "data": { ... }, "changed": "replaced" }changed は inserted (201) / replaced (200) / noop (200) のいずれか。
主なエラー
| 409 | external_id_owned_by_other_friend | 別の友だちが同じ外部IDを保持 |
/external-ids/{id}外部IDの紐づけを削除します。
friends:writeレスポンス例
{ "ok": true, "id": "01XID..." }主なエラー
| 404 | external-id not found | 存在しない (他テナント含む) |
Webhook購読 (Webhook Endpoints)
Lokuで発生したイベントを外部URLへ配信する購読を管理します。Zapier等の自動化ハブがプログラムから購読・解除するための経路です。レスポンスは snake_case です。
/webhook_endpoints購読の一覧を取得します。署名secretは接頭辞のみ返します。
webhooks:readレスポンス例
{
"object": "list",
"data": [
{
"id": "01WHEP...",
"object": "webhook_endpoint",
"url": "https://hooks.example.com/loku",
"description": null,
"events": ["friend.followed", "reservation.created"],
"status": "active",
"api_version": "2026-05-23",
"signing_secret_prefix": "whsec_0123456789",
"consecutive_failure_count": 0,
"last_success_at": "2026-07-14T02:00:00.000Z",
"last_failure_at": null,
"created_at": "2026-07-14T00:00:00.000Z",
"updated_at": "2026-07-14T02:00:00.000Z"
}
],
"has_more": false,
"next_cursor": null
}/webhook_endpoints購読を作成します。署名secret (whsec_...) はこのレスポンスでのみ返され、再表示できません。
webhooks:writeリクエストボディ (JSON)
url必須 | string | 配信先URL (https、最大2,000文字。プライベートIP等は拒否) |
events必須 | array | 購読するイベント種別の配列 (1–50件。下記「イベント種別」参照) |
description | string | メモ (最大500文字) |
レスポンス例
{
"id": "01WHEP...",
"object": "webhook_endpoint",
"url": "https://hooks.example.com/loku",
"events": ["friend.followed"],
"status": "active",
"api_version": "2026-05-23",
"signing_secret_prefix": "whsec_0123456789",
"signing_secret": "whsec_..." ,
"created_at": "2026-07-14T02:00:00.000Z"
}成功時は 201 を返します。signing_secret は必ず保存してください。
主なエラー
| 400 | invalid_url | URL がSSRF検証を通らない |
| 403 | plan_limit | プラン上限 (Pro=10、Suite=50。Starter以下は作成不可) |
/webhook_endpoints/{id}購読を解除します。
webhooks:writeレスポンス例
{ "id": "01WHEP...", "object": "webhook_endpoint", "deleted": true }主なエラー
| 404 | webhook_endpoint not found | 存在しない (他テナント含む) |
Webhookイベント種別
POST /webhook_endpoints の events に指定できる値です。
friend.followed | 友だち追加された |
friend.unfollowed | 友だち解除 (ブロック) された |
reservation.created | 予約が作成された |
reservation.cancelled | 予約がキャンセルされた |
form.submitted | フォームが回答された |
message.received | 友だちからメッセージを受信した (高頻度) |
member.registered | 会員登録された |
member.tier_changed | 会員ランクが変わった |
member.points_added | ポイントが付与された |
member.visited | 来店が記録された |
payment.succeeded | 決済が完了した |
payment.refunded | 返金された |
payment.failed | 決済に失敗した |
test.ping | テスト送信 (ダッシュボードから) |
Webhook配信と署名
イベントは以下のJSONで配信されます。配信はat-least-once (最大7回の指数バックオフで再試行) のため、 受信側は id で重複排除してください。連続50回失敗した購読は自動で無効化されます。
{
"id": "evt_01JCF...",
"type": "friend.followed",
"api_version": "2026-05-23",
"created": 1748131200,
"livemode": true,
"data": { /* イベント種別ごとのペイロード */ }
}すべての配信はHMAC-SHA256で署名されます。2方式を併記するため、どちらでも検証できます:
Loku-Signature | t=<unix>,v1=<hex> — 署名対象は `${timestamp}.${body}` (Stripe互換) |
webhook-signature | Standard Webhooks 互換 (webhook-id / webhook-timestamp と併用)。既製SDKで検証できます |
タイムスタンプが5分以上ずれた配信は拒否してください (リプレイ攻撃防止)。署名secretのローテーション中は旧secretも24時間有効です。