Contents
SmartHR API の全体像と主要エンドポイント
SmartHR が提供する RESTful API は、社員情報・組織構造・勤怠・給与明細など HR 業務の中心領域を一元的に取得できるよう設計されています。本章では、代表的なエンドポイントと取得可能なデータ項目、リクエスト/レスポンスの基本形を把握することで、実装時の設計判断をスムーズに行えるようになることを目的としています。
エンドポイント一覧(カテゴリ別)
以下は公式ドキュメントで公開されている主要エンドポイントの概要です。実際に利用する際は、最新版の URL とパラメータ仕様を必ず確認してください。
| カテゴリ | エンドポイント例 (GET) | 主な取得対象 | 共通クエリパラメータ |
|---|---|---|---|
| 社員情報 | /v1/employees |
氏名・メールアドレス・入社日・所属部門など | page, per_page, fields |
| 部門・組織 | /v1/departments |
部門コード・上位部門・担当者情報 | page, per_page |
| 勤怠・休暇 | /v1/attendance_records |
出退勤日時・ステータス・勤務形態 | employee_id, date_from, date_to, page |
| 給与明細 | /v1/payrolls/{payroll_id} |
支給額・控除項目・税金情報 | なし(パスパラメータ) |
| Webhook 設定 | /v1/webhooks (POST) |
イベント通知先 URL と対象イベント種別 | url, events |
注:上記エンドポイントは例示です。実際のベース URL は環境(本番 / サンドボックス)に応じて
{BASE_URL}で置き換えて使用します。
社員情報取得レスポンスの JSON スキーマ例
社員一覧取得時の典型的な JSON 構造を示します。fields パラメータで返却項目を絞り込むことができ、通信コストと処理負荷の削減に有効です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
{ "employees": [ { "id": "emp_12345", "name": "山田 太郎", "email": "taro.yamada@example.com", "joined_at": "2024-01-15", "department_id": "dept_001", "custom_fields": { "employee_number": "A0001" } } ], "meta": { "page": 1, "per_page": 100, "total": 452 } } |
認証方式とトークン管理
HR データは機密性が極めて高いため、SmartHR API では OAuth2.0 クライアントクレデンシャルフロー と API キー の二つの認証手段を提供しています。本章では、推奨される OAuth2 の取得手順と安全にトークンを取り扱うベストプラクティスを解説します。
OAuth2.0 クライアントクレデンシャルフローの概要
OAuth2 は「最小権限・短期間有効」のアクセストークンを発行できるため、認証情報漏洩時のリスクが低減します。取得手順は次の通りです。
- クライアント ID/シークレット を SmartHR デベロッパーポータルで作成し、安全に保管する(例:HashiCorp Vault、AWS Secrets Manager)。
POST {BASE_URL}/oauth/tokenに対してgrant_type=client_credentialsを送信し、アクセストークンと有効期間 (expires_in) を受領する。- 取得したトークンは
Authorization: Bearer <token>ヘッダーで API 呼び出しに付与する。
実装例(認証情報は環境変数から取得)
Python
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import os, requests, time BASE_URL = os.getenv("SMARTHR_BASE_URL", "https://api.smarthr.jp") TOKEN_ENDPOINT = f"{BASE_URL}/oauth/token" CLIENT_ID = os.getenv("SMARTHR_CLIENT_ID") CLIENT_SECRET = os.getenv("SMARTHR_CLIENT_SECRET") def get_access_token(): resp = requests.post( TOKEN_ENDPOINT, data={"grant_type": "client_credentials"}, auth=(CLIENT_ID, CLIENT_SECRET) ) resp.raise_for_status() payload = resp.json() # expires_in は秒単位。取得時にタイムスタンプを保持しておく return { "token": payload["access_token"], "expires_at": time.time() + int(payload.get("expires_in", 3600)) } # 使用例 creds = get_access_token() print(f"Access token (expires at {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(creds['expires_at']))})") |
JavaScript(Node.js)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
require('dotenv').config(); const fetch = require('node-fetch'); const BASE_URL = process.env.SMARTHR_BASE_URL || 'https://api.smarthr.jp'; const TOKEN_URL = `${BASE_URL}/oauth/token`; async function getAccessToken() { const resp = await fetch(TOKEN_URL, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'client_credentials' }) .toString(), // Basic 認証ヘッダーは環境変数から生成 credentials: 'include', redirect: 'follow' }); if (!resp.ok) throw new Error(`Token request failed (${resp.status})`); const data = await resp.json(); return { token: data.access_token, expiresAt: Date.now() + (data.expires_in * 1000 || 3600000) }; } // 呼び出し例 getAccessToken().then(c => console.log('Token:', c.token)); |
トークン更新のベストプラクティス
| ポイント | 推奨実装 |
|---|---|
| 有効期限確認 | expires_at を保持し、残り 5 分未満になったら自動で再取得する。 |
| 失効時リトライ | API 呼び出しで 401 が返った場合は即座に新しいトークンを取得して再試行する。 |
| スコープの最小化 | 必要なエンドポイントだけが許可されたスコープ(例:employees.read)を付与する。 |
| ロギング | トークン取得・更新時は 機密情報(シークレット) をログに出さないようマスク処理を行う。 |
サンドボックス環境でのテストとレートリミット
開発フェーズでは本番データへの影響を避けるため、SmartHR が提供するサンドボックスエンドポイントを利用します。本章では、実際に適用されているレートリミット とそれに対応したエラーハンドリングの実装例、テスト時に留意すべきベストプラクティスをまとめます。
レートリミットの具体数値
| 項目 | 制限内容 |
|---|---|
| リクエスト数 | 1 分間あたり最大 120 件(IP / クライアント ID 共通) |
| 同時接続数 | 最大 5 接続 |
| バースト許容 | 短時間での突発的な増加は 10 秒以内に 20 件まで許可 |
※ 上記は執筆時点で公表されている上限例です。実運用前に最新ドキュメントで確認してください。
エラーハンドリング実装例(指数バックオフ)
Python
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
import os, time, requests, random BASE_URL = os.getenv("SMARTHR_SANDBOX_BASE", "https://sandbox.api.smarthr.jp/v1") TOKEN = os.getenv("SMARTHR_SANDBOX_TOKEN") def request_with_retry(endpoint, max_retries=5): url = f"{BASE_URL}{endpoint}" headers = {"Authorization": f"Bearer {TOKEN}"} for attempt in range(max_retries): resp = requests.get(url, headers=headers) if resp.status_code == 200: return resp.json() # 429: Rate limit if resp.status_code == 429: wait = (2 ** attempt) + random.uniform(0.1, 0.5) print(f"Rate limited – retry after {wait:.2f}s") time.sleep(wait) continue # 5xx 系サーバーエラー if 500 <= resp.status_code < 600: wait = (2 ** attempt) * 1.5 print(f"Server error ({resp.status_code}) – retry after {wait:.1f}s") time.sleep(wait) continue # 401: 認証エラーは即座に例外送出 if resp.status_code == 401: raise RuntimeError("認証失敗。トークンを再取得してください。") # その他はそのまま例外化 resp.raise_for_status() raise RuntimeError("リトライ上限に達しました。") # 使用例 employees = request_with_retry("/employees") print(f"取得件数: {len(employees.get('employees', []))}") |
JavaScript(fetch)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
const BASE_URL = process.env.SMARTHR_SANDBOX_BASE || 'https://sandbox.api.smarthr.jp/v1'; const TOKEN = process.env.SMARTHR_SANDBOX_TOKEN; async function fetchWithRetry(endpoint, retries = 5) { const url = `${BASE_URL}${endpoint}`; for (let i = 0; i < retries; i++) { const resp = await fetch(url, { headers: { Authorization: `Bearer ${TOKEN}` } }); if (resp.ok) return await resp.json(); // Rate limit if (resp.status === 429) { const wait = Math.pow(2, i) * 1000 + Math.random() * 500; console.warn(`Rate limited – retry in ${wait}ms`); await new Promise(r => setTimeout(r, wait)); continue; } // Server error if (resp.status >= 500) { const wait = Math.pow(2, i) * 1500; console.warn(`Server error (${resp.status}) – retry in ${wait}ms`); await new Promise(r => setTimeout(r, wait)); continue; } // Authentication error if (resp.status === 401) throw new Error('認証失敗。トークンを確認してください'); const err = await resp.text(); throw new Error(`Unexpected error (${resp.status}): ${err}`); } throw new Error('リトライ上限に達しました'); } // 使用例 fetchWithRetry('/departments') .then(d => console.log('部門数:', d.departments?.length ?? 0)) .catch(console.error); |
テスト時のベストプラクティス
- データクリーンアップ:テストで作成したレコードは必ず
DELETEエンドポイントで削除し、サンドボックスを汚染しない。 - リクエストロギング:ヘッダー情報・ステータスコード・レイテンシを集中管理できるよう統一フォーマットで保存する(例:ELK スタック)。
- 固定シードデータ:社員番号や部門コードは事前に決めたサンプル値(
EMP_TEST_001など)を使用し、テストケースの再現性を担保。 - タイムアウト設定:ネットワーク障害時に無限待ちにならないよう、クライアント側で
timeout=10s程度を設定する。
実務で使える活用シナリオとサンプルコード
本章では SmartHR API を実際の業務フローに組み込む代表的ユースケースを 4 つ 紹介します。各シナリオは「目的 → 必要エンドポイント → コード例」の流れで示し、GitHub に公開中のサンプルリポジトリへのリンクも併記しています。
1. 社員オンボーディング自動化
新入社員が HR システムに登録されたタイミングで、Slack 招待・メールアカウント作成を自動的に実行します。
必要エンドポイント
- POST /v1/employees(社員作成)
- GET /v1/departments(所属部門取得)
Python サンプル
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import os, requests, json BASE_URL = os.getenv("SMARTHR_BASE_URL", "https://api.smarthr.jp/v1") TOKEN = os.getenv("SMARTHR_TOKEN") def create_employee(payload): resp = requests.post( f"{BASE_URL}/employees", json=payload, headers={"Authorization": f"Bearer {TOKEN}"} ) resp.raise_for_status() return resp.json() new_emp = { "name": "佐藤 花子", "email": "hanako.sato@example.com", "joined_at": "2026-09-01", "department_id": "dept_002" } emp_res = create_employee(new_emp) print("作成された社員 ID:", emp_res["id"]) |
ポイント:作成直後に取得した
employee.idを Slack 招待 API や社内メールプロビジョニングツールへ渡すだけで、手動入力が不要になります。
サンプルリポジトリ: https://github.com/example/smarthr-onboarding
2. 給与データ連携
給与計算システムから取得した明細情報を SmartHR に送信し、会計ソフトへ自動インポートします。
必要エンドポイント
- GET /v1/payrolls/{payroll_id}(個別給与明細取得)
- GET /v1/employees(対象社員リスト取得)
Node.js(JavaScript)サンプル
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
require('dotenv').config(); const fetch = require('node-fetch'); const BASE_URL = process.env.SMARTHR_BASE_URL || 'https://api.smarthr.jp/v1'; const TOKEN = process.env.SMARTHR_TOKEN; async function getPayroll(payrollId) { const resp = await fetch(`${BASE_URL}/payrolls/${payrollId}`, { headers: { Authorization: `Bearer ${TOKEN}` } }); if (!resp.ok) throw new Error(`Failed to fetch payroll (${resp.status})`); return resp.json(); } // 例:2026年7月分給与明細を取得 getPayroll('202607') .then(data => { // 必要項目だけ抽出し、CSV に変換する等の処理を実装 console.log(JSON.stringify(data, null, 2)); }) .catch(console.error); |
ポイント:給与明細は機密情報です。取得後は必ず AES‑256 GCM で暗号化し、KMS に保存したキーで管理してください。
サンプルリポジトリ: https://github.com/example/smarthr-payroll-sync
3. 休暇残高のリアルタイム表示
社内ポータルに従業員が自分の有給・特別休暇残数を即時確認できるウィジェットを提供します。
必要エンドポイント
- GET /v1/leave_balances?employee_id=...(休暇残高取得)
Python(キャッシュ付き実装)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
import os, json, time, requests from pathlib import Path BASE_URL = os.getenv("SMARTHR_BASE_URL", "https://api.smarthr.jp/v1") TOKEN = os.getenv("SMARTHR_TOKEN") CACHE_PATH = Path("/tmp/leave_balance_cache.json") TTL_SECONDS = 300 # 5 分間キャッシュ有効 def load_cache(): if not CACHE_PATH.is_file(): return {} data = json.loads(CACHE_PATH.read_text()) if time.time() - data.get("timestamp", 0) > TTL_SECONDS: return {} return data.get("balances", {}) def save_cache(balances): CACHE_PATH.write_text(json.dumps({ "timestamp": time.time(), "balances": balances })) def get_leave_balance(emp_id): cache = load_cache() if emp_id in cache: return cache[emp_id] resp = requests.get( f"{BASE_URL}/leave_balances", params={"employee_id": emp_id}, headers={"Authorization": f"Bearer {TOKEN}"} ) resp.raise_for_status() balance = resp.json()["balances"] cache[emp_id] = balance save_cache(cache) return balance print(get_leave_balance("emp_98765")) |
ポイント:キャッシュにより 1 分間あたりの API 呼び出し回数を大幅に削減でき、レートリミット超過のリスクが低減します。
サンプルリポジトリ: https://github.com/example/smarthr-leave-widget
4. 勤怠情報の双方向連携
既存のタイムカードシステムから取得した出退勤データを SmartHR に送信し、給与計算へ自動反映させます。
必要エンドポイント
- POST /v1/attendance_records(勤怠レコード登録)
- GET /v1/employees?email=...(社員 ID 取得)
JavaScript(fetch)サンプル
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
const BASE_URL = process.env.SMARTHR_BASE_URL || 'https://api.smarthr.jp/v1'; const TOKEN = process.env.SMARTHR_TOKEN; async function findEmployeeByEmail(email) { const resp = await fetch(`${BASE_URL}/employees?email=${encodeURIComponent(email)}`, { headers: { Authorization: `Bearer ${TOKEN}` } }); const data = await resp.json(); return data.employees?.[0]; // メールは一意と想定 } async function registerAttendance(employeeId, record) { const resp = await fetch(`${BASE_URL}/attendance_records`, { method: 'POST', headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ employee_id: employeeId, ...record }) }); if (!resp.ok) throw new Error(`Attendance registration failed (${resp.status})`); return resp.json(); } // タイムカードから取得した例 const timeCard = { date: "2026-07-20", clock_in: "09:00", clock_out: "18:00", status: "present" }; (async () => { const emp = await findEmployeeByEmail('hanako.sato@example.com'); if (!emp) throw new Error('社員が見つかりません'); const result = await registerAttendance(emp.id, timeCard); console.log('勤怠登録完了:', result); })(); |
ポイント:失敗したレコードはキュー(例: RabbitMQ)に退避し、一定時間後にリトライする仕組みを導入するとデータロス防止につながります。
サンプルリポジトリ: https://github.com/example/smarthr-attendance-sync
セキュリティ・コンプライアンスとバージョニング
HR データは個人情報保護法(APPI)や欧州 GDPR など、複数の法令で厳格に取り扱いが求められます。本章では 具体的な対応策 と API バージョン管理 のポイントをまとめ、運用上のチェックリストとして活用できる形に整理します。
法令対応の具体策(APPI・GDPR)
| 項目 | 具体的実装例 |
|---|---|
| データ最小化 | API リクエスト時に fields パラメータで必要属性だけ取得し、不要な個人情報は保存しない。 |
| 利用目的の明示 | 取得したデータを社内システムに連携する際は、内部ポリシーで「給与計算」「勤怠管理」など目的をタグ付けしてログに残す。 |
| 本人同意管理 | 同意取得フローは別途 Web フォームで実装し、同意ステータス(consent_id)を SmartHR のカスタムフィールドとして保存する。 |
| データ保持期間の設定 | 法定保存期間(例: 給与明細は 7 年)が過ぎたら自動的に暗号化されたバックアップから削除するバッチジョブを実装。 |
| 第三者提供制限 | 外部ベンダーへデータ提供する場合は、契約書で「目的外使用禁止」「保存期間遵守」を明記し、API スコープも read-only に限定。 |
| 国際転送(GDPR) | EU 在住者のデータは日本国内サーバーに保持せず、欧州リージョンのエンドポイントへ転送するオプションがある場合はそちらを選択。 |
アクセス制御と監査ログ
- 最小権限スコープ
- クライアントごとに
employees.read,attendance.writeなど必要最低限のスコープだけ付与。 - IP ホワイトリスト
- API キー利用時は SmartHR コンソールで許可 IP を登録し、社内ネットワークからのみアクセス可能にする。
- 集中ロギング
- すべてのリクエストは request_id, user_id, timestamp, endpoint, response_status を含む JSON 形式で ELK/Splunk に送信。機密情報(アクセストークン等)はハッシュ化して記録。
- 不正検知
- 同一 IP からの 1 分間あたりリクエスト数がレートリミットを超えた場合は自動でアラートを発し、トークンを即時失効させる仕組みを導入。
データ暗号化・保管
| 項目 | 推奨方式 |
|---|---|
| 転送 | TLS 1.2 以上(サーバ証明書は公的 CA 発行) |
| 保存 | 静止データは AES‑256 GCM、キーは Cloud KMS / AWS KMS で管理 |
| バックアップ | 暗号化されたスナップショットを別リージョンに保持し、復元テストは半年に一度実施 |
バージョニングと変更通知
- バージョン表記:エンドポイントパスに
v1,v2と明示的に付与。旧バージョンはリリース日から 12 ヶ月間サポートし、終了 30 日前までに Changelog にて告知。 - 変更通知の取得方法:SmartHR デベロッパーポータルの「API リリースノート」ページの RSS フィードを購読するか、Webhook
api.version_updatedを登録して自動受信できる。 - 互換性チェックツール:公式が提供する OpenAPI 仕様書(Swagger)をローカルで比較し、破壊的変更があるか事前に CI パイプラインで検証。
運用チェックリスト(まとめ)
| No. | 確認項目 | 推奨ツール / 方法 |
|---|---|---|
| 1 | HTTPS が強制されているか | ネットワークスキャン、curl -I https://... |
| 2 | スコープが最小化されているか | IAM コンソールで権限一覧をレビュー |
| 3 | トークン有効期限管理が実装済みか | ログに expires_at を記録し、リフレッシュロジックをテスト |
| 4 | 監査ログが集中管理されているか | ELK / Splunk のダッシュボードで検索可能か |
| 5 | データ暗号化設定が適用されているか | KMS キー使用状況と暗号化ポリシーを確認 |
| 6 | レートリミット超過時のバックオフ実装があるか | ユニットテストで 429 を模倣し、リトライ回数・待機時間を検証 |
| 7 | バージョンアップ通知を受け取っているか | RSS/Webhook の受信履歴を確認 |
ポイント:上記チェックリストを社内の「API セキュリティ基準」ドキュメントに組み込み、定期的(四半期ごと)にレビューすれば、法令遵守だけでなく運用コスト削減にもつながります。
まとめ
本稿では SmartHR API の全体像・認証・レートリミット・実務シナリオ・セキュリティ・コンプライアンスという観点から、設計・実装・運用までの一連の流れを網羅的に整理しました。
- 正確なエンドポイント情報は必ず公式ドキュメントで最新確認し、ハードコーディングされた認証情報は環境変数やシークレット管理サービスへ置き換えること。
- レートリミット(120 req/分)と指数バックオフ実装により、サンドボックスでも本番でも安定稼働が可能です。
- APPI・GDPR 対応策をコードや運用手順に落とし込み、監査ログ・暗号化・最小権限の原則でリスクを低減します。
これらを踏まえて開発チームは「安全かつスケーラブル」な HR システム連携基盤を構築できるはずです。疑問点や実装上の課題が生じた場合は、SmartHR デベロッパーポータルのサポート窓口または公式コミュニティで質問してください。