Appearance
API設計書
転職エージェント業務管理システム
概要
- ベースURL:
https://your-api-xxxxx-an.a.run.app/api/v1 - 認証: Cloudflare Access JWT(
CF-Access-JWT-Assertionヘッダー) - レスポンス形式: JSON
共通仕様
認証
すべてのAPIはCloudflare Access JWTで保護される。リクエスト時に CF-Access-JWT-Assertion ヘッダーが自動的に付与される。
エラーレスポンス
json
{
"error": {
"code": "ERROR_CODE",
"message": "エラーメッセージ"
}
}ページネーション
一覧取得APIはページネーションをサポート。
GET /candidates?page=1&per_page=20レスポンス:
json
{
"data": [...],
"pagination": {
"page": 1,
"per_page": 20,
"total": 150,
"total_pages": 8
}
}エンドポイント一覧
求職者 (Candidates)
| メソッド | パス | 説明 |
|---|---|---|
| GET | /candidates | 求職者一覧取得 |
| GET | /candidates/:id | 求職者詳細取得 |
| POST | /candidates | 求職者登録 |
| PUT | /candidates/:id | 求職者更新 |
| DELETE | /candidates/:id | 求職者削除 |
面談メモ (Candidate Notes)
| メソッド | パス | 説明 |
|---|---|---|
| GET | /candidates/:id/notes | メモ一覧取得 |
| POST | /candidates/:id/notes | メモ登録 |
| PUT | /candidates/:id/notes/:noteId | メモ更新 |
| DELETE | /candidates/:id/notes/:noteId | メモ削除 |
企業 (Companies)
| メソッド | パス | 説明 |
|---|---|---|
| GET | /companies | 企業一覧取得 |
| GET | /companies/:id | 企業詳細取得 |
| POST | /companies | 企業登録 |
| PUT | /companies/:id | 企業更新 |
| DELETE | /companies/:id | 企業削除 |
選考 (Applications)
| メソッド | パス | 説明 |
|---|---|---|
| GET | /applications | 選考一覧取得(横断) |
| GET | /applications/:id | 選考詳細取得 |
| POST | /applications | 選考登録 |
| PUT | /applications/:id | 選考更新 |
| DELETE | /applications/:id | 選考削除 |
| POST | /applications/:id/status | ステータス更新(履歴追加) |
| GET | /applications/:id/histories | ステータス履歴取得 |
ダッシュボード (Dashboard)
| メソッド | パス | 説明 |
|---|---|---|
| GET | /dashboard/tasks | 未完了タスク一覧 |
| GET | /dashboard/interviews/today | 今日の面接予定 |
| GET | /dashboard/interviews/week | 今週の面接予定 |
| GET | /dashboard/pending-results | 選考結果待ち一覧 |
| GET | /dashboard/summary | 実績サマリ(前月比較付き) |
集計・レポート (Reports)
| メソッド | パス | 説明 |
|---|---|---|
| GET | /reports/daily | 日次実績 |
| GET | /reports/monthly | 月次実績 |
| GET | /reports/by-channel | 媒体別売上・コスト |
| GET | /reports/by-asp | ASP別コスト・出金予定 |
| GET | /reports/by-user | 担当者別実績 |
マスタ (Masters)
| メソッド | パス | 説明 |
|---|---|---|
| GET | /masters/channels | 応募媒体一覧 |
| POST | /masters/channels | 応募媒体登録 |
| PUT | /masters/channels/:id | 応募媒体更新 |
| GET | /masters/asps | ASP一覧 |
| POST | /masters/asps | ASP登録 |
| PUT | /masters/asps/:id | ASP更新 |
| GET | /masters/candidate-statuses | 求職者ステータス一覧 |
| GET | /masters/selection-statuses | 選考ステータス一覧 |
| GET | /masters/progress-ranks | 内定確度一覧 |
| GET | /masters/users | ユーザー一覧 |
エンドポイント詳細
求職者
GET /candidates
求職者一覧を取得。
クエリパラメータ:
| パラメータ | 型 | 説明 |
|---|---|---|
| page | number | ページ番号(デフォルト: 1) |
| per_page | number | 1ページあたり件数(デフォルト: 20) |
| keyword | string | フリーワード検索(氏名、フリガナ、会社名) |
| status_id | number | 全体ステータスID |
| progress_rank_id | number | 内定確度ID |
| agent_id | number | 担当エージェントID |
| assistant_id | number | 担当アシスタントID |
| channel_id | number | 応募媒体ID |
| registered_from | date | 登録日(From) |
| registered_to | date | 登録日(To) |
レスポンス:
json
{
"data": [
{
"id": 1,
"name": "山田 太郎",
"name_kana": "ヤマダ タロウ",
"status": {
"id": 2,
"name": "活動中"
},
"progress_rank": {
"id": 1,
"name": "A"
},
"channel": {
"id": 1,
"name": "ビズリーチ"
},
"agent": {
"id": 1,
"name": "鈴木 一郎"
},
"registered_at": "2025-01-15T09:00:00Z",
"application_count": 5
}
],
"pagination": {
"page": 1,
"per_page": 20,
"total": 150,
"total_pages": 8
}
}GET /candidates/:id
求職者詳細を取得。
レスポンス:
json
{
"data": {
"id": 1,
"name": "山田 太郎",
"name_kana": "ヤマダ タロウ",
"gender": "男性",
"birth_date": "1990-05-15",
"age": 34,
"phone": "090-1234-5678",
"email": "yamada@example.com",
"current_status": "現職",
"current_company": "株式会社ABC",
"current_salary": 500,
"expected_salary": 600,
"channel": {
"id": 1,
"name": "ビズリーチ",
"fee_rate": 0.35
},
"asp": null,
"status": {
"id": 2,
"name": "活動中"
},
"progress_rank": {
"id": 1,
"name": "A",
"description": "内定見込み高"
},
"agent": {
"id": 1,
"name": "鈴木 一郎"
},
"assistant": {
"id": 2,
"name": "田中 花子"
},
"registered_at": "2025-01-15T09:00:00Z",
"applications": [
{
"id": 1,
"company": {
"id": 1,
"name": "株式会社XYZ"
},
"status": {
"id": 5,
"name": "一次面接合格"
},
"entry_date": "2025-01-20",
"updated_at": "2025-02-01T10:00:00Z"
}
],
"notes": [
{
"id": 1,
"title": "初回面談メモ",
"created_by": {
"id": 1,
"name": "鈴木 一郎"
},
"created_at": "2025-01-15T10:00:00Z"
}
]
}
}POST /candidates
求職者を登録。
リクエストボディ:
json
{
"name": "山田 太郎",
"name_kana": "ヤマダ タロウ",
"gender": "男性",
"birth_date": "1990-05-15",
"phone": "090-1234-5678",
"email": "yamada@example.com",
"current_status": "現職",
"current_company": "株式会社ABC",
"current_salary": 500,
"expected_salary": 600,
"channel_id": 1,
"asp_id": null,
"status_id": 1,
"progress_rank_id": null,
"agent_id": 1,
"assistant_id": 2
}選考
POST /applications/:id/status
選考ステータスを更新し、履歴を追加。
リクエストボディ:
json
{
"status_id": 6,
"status_date": "2025-02-10",
"remarks": "二次面接に進むことが決定"
}レスポンス:
json
{
"data": {
"id": 1,
"candidate_id": 1,
"company_id": 1,
"status": {
"id": 6,
"name": "二次面接"
},
"histories": [
{
"id": 3,
"status": {
"id": 6,
"name": "二次面接"
},
"status_date": "2025-02-10",
"remarks": "二次面接に進むことが決定",
"created_by": {
"id": 1,
"name": "鈴木 一郎"
},
"created_at": "2025-02-10T14:00:00Z"
}
]
}
}ダッシュボード
GET /dashboard/summary
実績サマリを取得(前月比較付き)。
レスポンス:
json
{
"data": {
"current_month": {
"period": "2025-02",
"applications": 25,
"phone_interviews": 20,
"zoom_interviews": 15,
"offers": 5,
"joins": 3
},
"previous_month": {
"period": "2025-01",
"applications": 22,
"phone_interviews": 18,
"zoom_interviews": 12,
"offers": 4,
"joins": 2
},
"comparison": {
"applications": { "diff": 3, "rate": 0.136 },
"phone_interviews": { "diff": 2, "rate": 0.111 },
"zoom_interviews": { "diff": 3, "rate": 0.25 },
"offers": { "diff": 1, "rate": 0.25 },
"joins": { "diff": 1, "rate": 0.5 }
}
}
}集計・レポート
GET /reports/by-asp
ASP別コストと出金予定を取得。
クエリパラメータ:
| パラメータ | 型 | 説明 |
|---|---|---|
| from | date | 期間開始日 |
| to | date | 期間終了日 |
レスポンス:
json
{
"data": {
"by_asp": [
{
"asp": {
"id": 1,
"name": "ASP A"
},
"total_acquisitions": 10,
"total_cost": 150000,
"approved_count": 8,
"pending_count": 2,
"rejected_count": 0
}
],
"payment_schedule": [
{
"payment_date": "2025-03-31",
"asp": {
"id": 1,
"name": "ASP A"
},
"amount": 75000,
"acquisitions": 5
},
{
"payment_date": "2025-04-30",
"asp": {
"id": 1,
"name": "ASP A"
},
"amount": 75000,
"acquisitions": 5
}
]
}
}ドメインロジック
ASP出金日計算
typescript
function calculatePaymentDate(
applicationDate: Date,
cutoffDay: number,
paymentMonthOffset: number
): Date {
const day = applicationDate.getDate()
const baseDate = new Date(applicationDate)
if (day > cutoffDay) {
// 締め日を過ぎている場合、オフセット+1ヶ月後
baseDate.setMonth(baseDate.getMonth() + paymentMonthOffset + 1)
} else {
// 締め日以前の場合、オフセット月後
baseDate.setMonth(baseDate.getMonth() + paymentMonthOffset)
}
// 月末を取得
return new Date(baseDate.getFullYear(), baseDate.getMonth() + 1, 0)
}ASP成果承認判定
typescript
function getAspApprovalStatus(candidate: Candidate): 'approved' | 'rejected' | 'pending' {
// Zoom面談日が入っている = 承認
if (candidate.zoomInterviewDate) {
return 'approved'
}
// 終了理由がある = 否認
if (candidate.terminationReason || candidate.phoneTerminationReason) {
return 'rejected'
}
// それ以外 = 対応待ち
return 'pending'
}売上計算
typescript
function calculateRevenue(application: Application): {
grossRevenue: number
cost: number
netRevenue: number
} {
const finalSalary = application.finalSalary || 0
const feeRate = application.candidate.channel.feeRate
const aspCost = application.candidate.asp?.costPerAcquisition || 0
// スカウト媒体経由の場合
if (feeRate > 0) {
const grossRevenue = finalSalary * 10000 // 万円→円
const cost = grossRevenue * feeRate
return {
grossRevenue,
cost,
netRevenue: grossRevenue - cost,
}
}
// 自社サイト経由の場合
const grossRevenue = finalSalary * 10000
return {
grossRevenue,
cost: aspCost,
netRevenue: grossRevenue - aspCost,
}
}エラーコード一覧
| コード | HTTPステータス | 説明 |
|---|---|---|
| UNAUTHORIZED | 401 | 認証エラー |
| FORBIDDEN | 403 | 権限エラー |
| NOT_FOUND | 404 | リソースが見つからない |
| VALIDATION_ERROR | 400 | バリデーションエラー |
| DUPLICATE_ENTRY | 409 | 重複エラー |
| INTERNAL_ERROR | 500 | サーバー内部エラー |