開発者向けドキュメント
OKAERI の公開 API の使い方です。リード・配信・レポートの取得、除外リストの登録、キャンペーン・シナリオ・文面の下書きの作成、Webhook の送信先の管理を、自社のシステムや AI エージェントから行えます。各操作の詳細は API リファレンス を参照してください。
AI・コーディングエージェント向けにこのページの Markdown 版とllms.txtがあります。
はじめに
- ベース URL:
https://api.okaeri.tech/api/v1 - 機械可読の仕様(OpenAPI 3.1):
https://api.okaeri.tech/api/v1/openapi.json(認証不要)。API リファレンスはこの文書から作っています - リクエストの本文と応答は JSON(UTF-8)です
- 日時は ISO 8601・UTC・
Z付きで返します(例2026-01-01T00:00:00Z) - 一覧は cursor 方式です。
limit(最大200)とcursorで取得し、応答のnext_cursorを次のcursorに渡します。next_cursorがnullなら最後のページです - バージョンはパスの
v1です。応答に項目を足すことがあるので、知らない項目は無視する実装にしてください
認証とスコープ
API キーを Authorization ヘッダに Bearer で付けて送ります。クエリ文字列でのキーの受け渡しは受け付けません。
Authorization: Bearer okr_test_XXXXXXXXXXXXXXXXXXXXXX| キーの種類 | 形 | 動き |
|---|---|---|
| 本番キー | okr_live_… | 読み取り・書き込みとも本番のデータに反映します |
| テストキー | okr_test_… | 書き込みはドライランです(下記)。読み取りは本番のデータを返します |
- キーは管理画面の API キーの設定で発行します。発行できるのはオーナーと管理者です。キーの文字列は発行した直後に1回だけ表示されます
- キーを発行したメンバーが停止・削除されると、そのキーは使えなくなります(401
api_key_creator_inactive) - 連絡先(電話番号・メールアドレス)は常にマスクして返します(例
090-****-1234・us***@example.com)
スコープ
キーには発行時にスコープ(<資源>:<操作> の形の権限)を付けます。各操作に必要なスコープは、リファレンスと openapi.json の x-okaeri-scopes に載っています。足りない時は 403 permission_error(code は scope_insufficient)を返します。
API キーではできない操作
- キャンペーン・シナリオの状態の変更(有効化・停止・アーカイブ)は 403
session_requiredです。管理画面で行ってください - API キーで作ったキャンペーンは必ず停止中(
paused)、シナリオは必ず下書き(draft)になります。それ以外のstatusを指定すると 422api_key_draft_onlyです - テスト送信と、メール本文の HTML の直接入力(
body_html)は API キーでは使えません
レート制限
API キーのリクエストは、テナント単位(テナントの全キーの合計)で直近60秒の回数を数えます。上限はプランごとに次のとおりです。
| プラン | 上限(直近60秒) |
|---|---|
| Free | 60 回 |
| Starter | 60 回 |
| Growth | 300 回 |
| Pro | 1,000 回 |
- 応答ヘッダで
X-RateLimit-Limit(上限)・X-RateLimit-Remaining(残り)・X-RateLimit-Reset(次に1回分空く時刻・UNIX 秒)を返します - 上限を超えると 429
rate_limit_errorとRetry-After(秒)を返します。その秒数の後に再試行してください - 個別の上限が設定されている場合は、
X-RateLimit-Limitの値が正です - テストキーのリクエストも同じ枠で数えます
ドライラン(テストキー)
テストキー(okr_test_…)の書き込み(POST・PUT・PATCH・DELETE)はドライランです。検証・上限の判定・エラーの応答は本番キーと同じに行い、応答も同じ形で返しますが、何も保存しません。AI エージェントに試行錯誤させる時は、まずテストキーを渡してください。
- 応答ヘッダ
OKAERI-Dry-Run: trueが付きます - 作成の応答に含まれる
idは仮の値で、その資源は存在しません(続けて取得・更新すると 404) - タグの設定の配信・メールの通知などの外部への反映は行いません
- 読み取りはテストキーでも本番のデータを返します
Idempotency-Keyの記録も残らないため、同じキーで送り直しても毎回ドライランになります
再送の重複を防ぐ(Idempotency-Key)
POST は Idempotency-Key ヘッダを受け付けます。ネットワークの再試行などで同じ要求を2回送っても、作成は1回にまとまります。要求ごとに一意な値(UUID など)を付けてください。
- 同じキーで送り直すと、保存済みの応答をそのまま返します(24時間・応答ヘッダ
Idempotency-Replayed: true) - キーはテナントとエンドポイントごとに区別します
- 先の要求の処理中に送り直すと 409
idempotency_key_in_progressを返します - 5xx の応答は保存しません(同じキーで再試行できます)
- 秘密の値(Webhook の署名鍵
secretなど)は、送り直した時の応答では伏せて返します。平文は最初の応答の1回だけです
エラーの形
エラーは常に次の形で返します。分岐は HTTP ステータスでなく type と code で行ってください。
{
"error": {
"type": "permission_error",
"code": "scope_insufficient",
"message": "(何が足りないかの説明)",
"param": null,
"request_id": "req_01JXXXXXXXXXXXXXXXXXXXXXXX"
}
}| type | HTTP | 主な場面 |
|---|---|---|
invalid_request | 400・409・422 | パラメータの不正(param に項目名)・状態の衝突(409) |
authentication_error | 401 | API キーが無い・無効・失効している |
permission_error | 403 | スコープの不足・プランの上限・API キーではできない操作 |
not_found | 404 | 対象が無い(他のテナントの ID を指した時も同じ) |
rate_limit_error | 429 | レート制限の超過(Retry-After の秒数の後に再試行する) |
api_error | 5xx | サーバー側の失敗(request_id を添えて問い合わせる) |
messageは人と AI の両方が読む前提で、何が足りないかを日本語で書いていますparamは原因の項目名です(無ければnull)- 問い合わせの際は
request_idを添えてください - 各操作で起こりうるエラーは、リファレンスの「応答」の表に載っています
Webhook
登録した送信先(https の URL)へ、イベントの発生を POST で知らせます。送信先は API(Webhook)か管理画面で登録します。送信先の本数にはプランごとの上限があります。
イベント
| イベント | 送る時 | data の型 |
|---|---|---|
lead.captured | 入力途中の連絡先を取得した時 | LeadCapturedData |
lead.abandoned | 入力の途中で離脱したと判定した時 | LeadAbandonedData |
delivery.sent | SMS・メールの送信を送信事業者が受け付けた時 | DeliveryEventData |
delivery.failed | 送信の失敗が確定した時 | DeliveryEventData |
conversion.detected | 成果(コンバージョン)を検知した時 | ConversionDetectedData |
usage.threshold_reached | プランの送信枠の 80%・100% に達した時 | UsageThresholdReachedData |
本文に連絡先(電話番号・メールアドレス)は含みません。必要な時は lead_id でリードを取得してください。
本文の例
{
"id": "00000000-0000-7000-8000-000000000001",
"type": "lead.captured",
"created_at": "2026-01-01T00:00:00Z",
"tenant_id": "00000000-0000-7000-8000-000000000002",
"data": {
"lead_id": "00000000-0000-7000-8000-000000000003",
"campaign_id": "00000000-0000-7000-8000-000000000004",
"site_id": "00000000-0000-7000-8000-000000000005",
"channels": [
"email",
"sms"
]
}
}受け方と署名の検証
Outbound Webhook で送信先へ送る本文(POST・Content-Type: application/json・User-Agent: OKAERI-Webhook/1)。type ごとに data の形が決まる。連絡先(電話・メール)は含まない(必要なら lead_id で取得する)。
受け方
- 2xx を返すと成功。それ以外の応答・接続できない時は再試行する(初回+再試行5回=最大6回・間隔は 1分 → 5分 → 30分 → 2時間 → 6時間)。リダイレクトは追わない。連続して失敗し続けた送信先は自動で停止する(
disabled_reason) - 少なくとも1回届ける=同じイベントが2回以上届くことがある。
idで重複を除く(再試行・再送でもidは同じ) - ヘッダ
X-OKAERI-Event=typeと同じ値。X-OKAERI-Delivery=送信ごとの ID(再送で変わる)
署名の検証(ヘッダ X-OKAERI-Signature: t=<UNIX 秒>,v1=<16進>)
- ヘッダを
,で分け、tとv1を取り出す <t>.<受け取った本文のバイト列>を、送信先の作成時に1回だけ受け取ったsecret(whsec_…)を鍵にして HMAC-SHA256 で計算し、16進(小文字)にする。本文は JSON として読み直したものでなく、受け取ったバイト列そのままを使う- 計算した値と
v1を定数時間で比較する。一致しなければ破棄する tと受信時刻の差が許容範囲(推奨 5分)を超えたら破棄する(盗んだ本文の再送の対策)。tは送るたびにその時刻で付け直す(再試行・再送でも古くならない)
検証の例(Python):
import hashlib
import hmac
import time
TOLERANCE = 300 # 秒(推奨は5分)
def verify(secret: str, header: str, body: bytes) -> bool:
"""X-OKAERI-Signature を検証する。body は受け取ったバイト列そのまま"""
parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
t, v1 = parts.get("t", ""), parts.get("v1", "")
if not t.isdigit() or abs(time.time() - int(t)) > TOLERANCE:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, v1)契約の範囲
- 契約は API リファレンス(openapi.json)に載っている操作と項目です
- 載っていないエンドポイント(
/api/v1/admin/**・/api/v1/views/**を含む)は管理画面のためのもので、契約の外です。予告なく変わるので使わないでください - 「画面表示用・形は契約外」と書いた応答(サイトのタグの設定など)は、中の項目に依存した処理を組まないでください
- 値の一覧(
enum)は今後増えることがあります。知らない値を受けても処理が止まらないようにしてください - 後方互換を壊す変更は、パスのバージョン(
v1)を上げて行います
クイックスタート
動作の確認はテストキーで行ってください(書き込みは保存されません)。キーは環境変数で渡します。
export OKAERI_API_KEY="okr_test_XXXXXXXXXXXXXXXXXXXXXX"1. サイトの一覧を取得する
必要なスコープは sites:read。応答の data にサイトが並びます。
curl https://api.okaeri.tech/api/v1/sites \
-H "Authorization: Bearer $OKAERI_API_KEY"2. 期間のファネル(段ごとの件数)を取得する
必要なスコープは reports:read。期間は from 以上 to 未満で、日時はオフセット付き(+09:00 など)で指定します。
curl -G https://api.okaeri.tech/api/v1/reports/funnel \
-H "Authorization: Bearer $OKAERI_API_KEY" \
--data-urlencode "from=2026-01-01T00:00:00+09:00" \
--data-urlencode "to=2026-02-01T00:00:00+09:00"3. 除外リストに登録する(書き込み)
必要なスコープは suppressions:write。登録した宛先には配信しません。テストキーなら応答ヘッダに OKAERI-Dry-Run: true が付き、保存されません。
curl https://api.okaeri.tech/api/v1/suppressions \
-H "Authorization: Bearer $OKAERI_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"value": "[email protected]"}'