API と Webhook で外部システムとつなぐ

基幹システムや通知ツールと ROSTER をつなぐための REST API(API キー)と Webhook(送信)の使い方。認証・回数制限・エンドポイント・署名の検証方法をまとめています。

← ヘルプセンター

設定・メンバー・プラン

最終更新: 2026-09-12

ROSTER の外側にあるシステム(自社の基幹システム、Slack 通知、スプレッドシートの自動更新など)とつなぐための 2 つの仕組みです。どちらも Max プランでお使いいただけ、設定 > 連携 からオーナー・管理者が自分で設定できます。

  • API(REST API) … 外部のシステムから ROSTER の候補者・求人・選考を読んだり、活動記録や面談予定を登録したりします。「外から ROSTER に聞きに来る」向きです。
  • Webhook(送信) … ROSTER で出来事(候補者の登録・選考のステージ変更など)が起きた瞬間に、指定した URL へお知らせを送ります。「ROSTER から外へ知らせに行く」向きです。

このページの後半は、つなぎ込みを行う技術担当の方向けの正確な仕様です。前半だけ読めば「何ができるか」は分かるようにしています。

できること・できないこと

できることできないこと
候補者の検索・詳細の取得・新規登録候補者の更新・削除(個人情報の上書きと消去は画面で人が行います)
求人 1 件の詳細の取得求人・企業の登録や更新
進行中の選考の一覧選考ステージの変更・成約の登録(不可逆な操作は画面で)
活動記録の追加・面談予定の登録請求・返金・メール送信

API は、ROSTER の「AI と接続」(MCP)と同じ処理を通ります。画面・AI・API のどこから登録しても、同じ行が同じ副作用(活動記録・監査ログ・カレンダー同期)付きで作られます。

API キーを発行する

  1. 設定 > 連携 を開き、「API キー」の「API キーを発行」を押します(オーナー・管理者のみ)
  2. 連携先が分かる名前(例「基幹システム連携」)を付けて「発行する」を押します
  3. 表示されたキー(rk_live_ で始まる文字列)をコピーして、連携先の設定に貼り付けます

キーが表示されるのはこの 1 回だけです。ROSTER にはキーそのものは保存されず、照合用の値(ハッシュ)だけが残るため、後から再表示できません。無くしたときは「失効」させてから新しいキーを発行してください。

キーは組織単位です。失効させると、そのキーを使う連携はすぐに動かなくなります(他のキーには影響しません)。

API の使い方(技術担当の方向け)

認証

すべてのリクエストに Authorization ヘッダーでキーを載せます。

Authorization: Bearer rk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • 基点 URL は https://roster.co.jp/api/v1。すべて JSON(Content-Type: application/json)です。
  • キーが無い・失効している → 401。Max プラン以外の組織のキー → 403(plan_required)。

回数制限

キー 1 本あたり 1 分間に 120 回まで。超えると 429 を返し、Retry-After ヘッダーに「あと何秒で再開できるか」が入ります。

エラーの形

エラーは常に次の形です(code は機械で判定する識別子、message は日本語の説明)。

{ "error": { "code": "not_found", "message": "指定された候補者は見つかりませんでした。" } }

主な code: unauthorized(401)/ plan_required(403)/ rate_limited(429)/ validation_error(400)/ not_found(404)/ duplicate・time_conflict(409)。

エンドポイント一覧

GET /api/v1/candidates?q=&limit=

候補者をフリーワードで検索します(氏名・フリガナ・現職・スキル)。limit は最大 20。

GET /api/v1/candidates?q=山田&limit=5
{ "query": "山田", "count": 1, "candidates": [ { "id": "6f1c…", "name": "山田 太郎", "url": "https://roster.co.jp/candidates?open=6f1c…" } ] }

GET /api/v1/candidates/:id

候補者 1 名の詳細(プロフィール・進行中の選考・直近の活動 10 件)。活動の本文は 120 文字の抜粋だけで、メール本文の全文は返しません。

{ "found": true, "candidate": { "id": "6f1c…", "name": "山田 太郎", "email": "…", "currentCompany": "…", "skills": ["Python"], "status": "active", "url": "…" }, "applications": [ { "id": "…", "jobTitle": "…", "companyName": "…", "stageName": "一次面接", "daysInStage": 3 } ], "recentActivities": [ { "id": "…", "type": "call", "at": "2026-09-10T06:00:00Z", "excerpt": "…" } ] }

POST /api/v1/candidates

候補者を 1 名登録します。name は必須。同じ人がすでに登録されている可能性があるときは登録せず `409` を返し、matches に見つかった候補者を入れます。別人だと確認できたときだけ force: true を付けて呼び直してください。

{ "name": "山田 太郎", "kana": "やまだ たろう", "email": "taro@example.com", "current_company": "株式会社サンプル", "skills": ["Python", "AWS"] }

成功(200):

{ "ok": true, "id": "6f1c…", "name": "山田 太郎", "forced": false, "url": "https://roster.co.jp/candidates?open=6f1c…" }

重複(409):

{ "error": { "code": "duplicate", "message": "同じ方がすでに登録されている可能性があるため…" }, "matches": [ { "id": "…", "name": "山田 太郎", "matchedBy": "email", "url": "…" } ] }

GET /api/v1/jobs/:id

求人 1 件の詳細(企業・条件・進行中の選考数・適合度の高い候補者 上位 3 名)。

{ "found": true, "job": { "id": "…", "title": "バックエンドエンジニア", "companyName": "株式会社サンプル", "status": "open", "salaryMin": 600, "salaryMax": 900, "url": "…" }, "activeApplicationCount": 4, "topMatchingCandidates": [ { "id": "…", "name": "…", "url": "…" } ] }

GET /api/v1/applications?candidateId=&jobId=&stage=&status=&limit=

進行中の選考の一覧。candidateId / jobId で絞り込み、stage はステージの id か名前(完全一致)。status は active(既定)/ waiting_on_us(自社が動く番)/ waiting_on_client(企業の返事待ち)/ stalled(停滞)。limit は最大 50。

{ "status": "active", "count": 2, "totalMatched": 2, "applications": [ { "id": "…", "candidateId": "…", "jobId": "…", "candidateName": "…", "jobTitle": "…", "companyName": "…", "stageId": "…", "stageName": "一次面接", "daysInStage": 3, "waitingOn": "company", "waitingOnLabel": "企業の返事待ち", "stalled": false, "url": "…" } ] }

POST /api/v1/activities

活動記録を 1 件追加します。subject_type は candidate / company / application、kind は call / email / visit / meeting / note(既定)。occurred_at に未来の日時は指定できません。

{ "subject_type": "candidate", "subject_id": "6f1c…", "kind": "call", "body": "電話で現況を確認。転職意欲は高い。", "occurred_at": "2026-09-12T10:00:00+09:00" }
{ "ok": true, "id": "…", "subjectType": "candidate", "subjectName": "山田 太郎", "occurredAt": "2026-09-12T01:00:00.000Z", "url": "…" }

POST /api/v1/interviews

面談・面接の予定を 1 件登録します。application_id か candidate_id のどちらかが必須。type は initial(候補者面談)/ company_interview(企業面接)/ follow_up / other。担当者の予定と重なるときは登録せず 409(time_conflict)を返し、conflicts に重なる予定を入れます。それでも入れる場合は force: true。

{ "application_id": "…", "type": "company_interview", "starts_at": "2026-09-15T14:00:00+09:00", "duration_minutes": 60, "location": "先方本社" }
{ "ok": true, "id": "…", "scheduledAt": "2026-09-15T05:00:00.000Z", "durationMin": 60, "type": "company_interview", "subject": "山田 太郎 × 株式会社サンプル「バックエンドエンジニア」", "calendarRegistered": 1, "url": "…" }

GET /api/v1/webhooks/events

Webhook で送られる出来事の一覧(名前と日本語の説明)。

Webhook(送信)を設定する

  1. 設定 > 連携 の「Webhook(送信)」で「エンドポイントを追加」を押します(オーナー・管理者のみ)
  2. お知らせを受け取る URL(https:// から始まる公開 URL)と、送る出来事を選んで「追加する」を押します
  3. 表示された署名シークレット(whsec_ で始まる文字列)をコピーして、受け取り側の設定に保存します
  4. 「テスト送信」を押すと ping という出来事が届きます。受け取り側で 2xx を返せていれば成功です

送り先ごとに「有効 / 無効」を切り替えられ、「最近の配信」で送れたかどうか(成功・失敗・再送待ち)を確認できます。

送られる出来事

出来事名いつ送られるか
candidate.created候補者が登録された(画面・AI・API のどこからでも)
candidate.updated候補者が更新された(項目名だけ。値は送りません)
application.stage_changed選考のステージが変わった(一括辞退・成約登録も含む)
placement.created成約が登録された(金額は送りません)
interview.scheduled面談の予定が入った
ping「テスト送信」を押したとき

送る内容は id と名前・タイトル・ステージ名などの要約だけです。連絡先・メモ・給与などの詳しい情報は含みません。必要なら受け取り側が API で取りに来る、という組み合わせにしてください。

本文の形

{
  "id": "配信ごとの id(再送でも同じ id)",
  "event": "application.stage_changed",
  "created_at": "2026-09-12T01:23:45.000Z",
  "org_id": "組織の id",
  "data": { "id": "選考の id", "from_stage": { "id": "…", "name": "書類選考" }, "to_stage": { "id": "…", "name": "一次面接", "is_terminal": false, "is_won": false }, "source": "app" }
}

ヘッダー: X-Roster-Event(出来事名)、X-Roster-Delivery(配信 id)、X-Roster-Signature(署名)。

署名の検証(Node.js の例)

X-Roster-Signature は t=<UNIX 秒>,v1=<HMAC-SHA256 の 16 進> の形で、<t>.<本文そのまま> を署名シークレットで HMAC-SHA256 した値です。本文は JSON に解釈する前の生の文字列で検証してください。

const crypto = require("node:crypto");
function verify(secret, header, rawBody) {
  const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // 5 分より古い署名は拒否
  const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
  return expected.length === parts.v1.length && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}

再送のきまり

  • 受け取り側が 2xx を 10 秒以内に返せば成功です。重い処理は 2xx を返してから非同期で行ってください。
  • 失敗(タイムアウト・2xx 以外)したときは、1 分後 → 10 分後 → 60 分後の 3 回まで再送し、それでも届かなければ打ち切ります(「最近の配信」に「失敗(打ち切り)」と出ます)。
  • 再送は同じ id(X-Roster-Delivery)で届きます。二重処理を避けるには、この id で重複を排除してください。
  • 配信の記録は 30 日で消えます。

困ったときは

  • 401 が返る … キーの先頭が rk_live_ になっているか、失効させていないかを確認してください。
  • 403(plan_required)が返る … Max プラン以外では API と Webhook は使えません。設定 > プランと請求 でご確認ください。
  • Webhook が届かない … 受け取り側が https:// の公開 URL か(社内ネットワークのアドレスは指定できません)、10 秒以内に 2xx を返しているかを確認し、「テスト送信」で試してください。

解決しないときはお問い合わせからご連絡ください。

関連記事

解決しませんでしたか。

ご利用中の方は、画面右下の「AI相談」で操作方法を質問できます。それでも分からないときは お問い合わせフォーム からご連絡ください。2営業日以内(土日祝日および当社の年末年始休業日を除く)にご返信します。