Skip to content

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-aspASP別コスト・出金予定
GET/reports/by-user担当者別実績

マスタ (Masters)

メソッドパス説明
GET/masters/channels応募媒体一覧
POST/masters/channels応募媒体登録
PUT/masters/channels/:id応募媒体更新
GET/masters/aspsASP一覧
POST/masters/aspsASP登録
PUT/masters/asps/:idASP更新
GET/masters/candidate-statuses求職者ステータス一覧
GET/masters/selection-statuses選考ステータス一覧
GET/masters/progress-ranks内定確度一覧
GET/masters/usersユーザー一覧

エンドポイント詳細

求職者

GET /candidates

求職者一覧を取得。

クエリパラメータ:

パラメータ説明
pagenumberページ番号(デフォルト: 1)
per_pagenumber1ページあたり件数(デフォルト: 20)
keywordstringフリーワード検索(氏名、フリガナ、会社名)
status_idnumber全体ステータスID
progress_rank_idnumber内定確度ID
agent_idnumber担当エージェントID
assistant_idnumber担当アシスタントID
channel_idnumber応募媒体ID
registered_fromdate登録日(From)
registered_todate登録日(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別コストと出金予定を取得。

クエリパラメータ:

パラメータ説明
fromdate期間開始日
todate期間終了日

レスポンス:

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ステータス説明
UNAUTHORIZED401認証エラー
FORBIDDEN403権限エラー
NOT_FOUND404リソースが見つからない
VALIDATION_ERROR400バリデーションエラー
DUPLICATE_ENTRY409重複エラー
INTERNAL_ERROR500サーバー内部エラー