使い方
- インストールコマンドをコピーし、自分の環境へ導入する前にソースを確認します。
- 必要なツール、認証情報、対応環境を原文で確認します。
- 入力と成果物を指定し、出力を資料と照合します。
npx skills add https://github.com/factorialco/factorial-api-sdks --skill factorial-api-sdks開始前の確認
- 導入にはモデル契約、API枠、ファイルへのアクセス許可は含まれません。
- スター数や導入数は活動の指標であり、独立した品質・安全性試験ではありません。
見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。
Factorial API SDKs
Official SDKs that wrap the Factorial public API with a domain-namespaced client, pagination helpers, and clean auth. This skill teaches an agent how to use them and how to work with Factorial webhooks. Generated reference data lives in reference/ (see References).
- TypeScript:
@factorialco/api-client(npm) - Python:
factorial-api-client(PyPI) - Ruby:
factorial_api(RubyGems)
Install
npm install @factorialco/api-client # TypeScript
pip install factorial-api-client # Python
gem install factorial_api # RubyAuthentication
Provide either an API key or an OAuth token (or both):
| Option | Header sent |
|---|---|
apiKey | x-api-key: <value> |
token | Authorization: Bearer <value> |
The Factorial API key is JWT-formatted but is sent as x-api-key, not as a bearer token.Credentials can be passed explicitly or read from the environment (FACTORIAL_API_KEY, FACTORIAL_TOKEN, optional FACTORIAL_BASE_URL).
import { FactorialClient } from "@factorialco/api-client";
const client = new FactorialClient({ apiKey: process.env.FACTORIAL_API_KEY });from factorial_api_client import FactorialClient
client = FactorialClient(api_key="YOUR_KEY") # or token="...", or from envrequire "factorial_api"
api = F::Api.new(api_key: ENV["FACTORIAL_API_KEY"]) # or token:, or from envCalling endpoints
The client is organized as client.<namespace>.<resource>.<method>. Namespaces and resources mirror the REST path /api/<version>/resources/<namespace>/<resource>.
Method naming is deterministic:
| HTTP / shape | SDK method |
|---|---|
GET collection | list() / paginate() / all() |
GET by id | get(id) |
POST collection | create(body) |
PUT/PATCH by id | update(id, body) |
DELETE by id | delete(id) |
Custom action (e.g. apply) | camelCased action, e.g. apply() |
const { data } = await client.employees.employees.list();
const created = await client.apiPublic.webhookSubscriptions.create({ /* body */ });employees = client.employees.employee.list()
sub = client.api_public.webhook_subscription.create(body={...})Ruby follows the same shape in snake_case, with required params — path, query or form alike — as named keywords; optional query params go in a query_params: hash and bodies under their *_request keyword. (The raw low-level accessors, e.g. api.employees_employee.employees_employees_get(true, false), keep working underneath.)
employees = api.employees.employee.list(only_active: true, only_managers: false).data
team = api.teams.team.create(teams_teams_post_request: { name: "Platform" })To discover the exact namespace/resource/method for any endpoint, consult reference/sdk-methods.md (every endpoint with its TS and Ruby calls, grouped) or the online API reference.
Pagination
List endpoints return { data: T[], meta: { end_cursor?, has_next_page, ... } }.
list()— one raw pagepaginate()— async iterator (TS) / sync or async generator (Python)all()(TS) /collect_all()(Python) — fetch every page into one array/list- Ruby:
paginate(lazy Enumerator, caps vialimit:/max_items:) andall
on every listable resource; F::Api.paginate { |page| ... } wraps raw calls
for await (const emp of client.employees.employees.paginate()) { /* ... */ }
const everyone = await client.employees.employees.all({ maxItems: 500 });for emp in client.employees.employee.paginate(max_items=50): ...
everyone = client.employees.employee.all()High-volume retrieval
Pages are capped at 100 items — a server-side hard max, not a default (see the pagination docs). Passing a larger limit has no effect. Cursor pagination is sequential (page N+1 needs page N's end_cursor), so all() on a big dataset (e.g. a full company-month of attendance/worked_times) means dozens of sequential requests. To keep request counts and latency sane:
- Filter first. Use the endpoint's query params (date ranges,
ids,
employee_ids, …) instead of pulling everything and filtering locally.
- Sync incrementally. Where an endpoint supports
updated_at-style
filters, fetch only what changed since your last run and cache locally — don't re-pull the full dataset per report.
- Shard big pulls. Split one large query into filtered sub-queries (by
date window or id chunks) and run those concurrently — each sub-query still paginates sequentially, but wall-clock time drops. Total request count is unchanged, so watch rate limits.
- Cap defensively. Pass
max_items/maxItemsso a bug or unexpected
data volume can't turn into an unbounded crawl.
There is no server-side aggregation endpoint; totals (e.g. hours per employee per period) must be computed client-side from the raw records.
Errors
Every SDK fails loudly on a non-2xx response (bad/expired token, wrong base URL, server errors) instead of silently returning empty data. Each raises a typed error carrying the status and the response body:
| SDK | Class | Status | Body |
|---|---|---|---|
| TypeScript | FactorialApiError (extends Error) | status | body (parsed), plus method, url |
| Python | UnexpectedStatus | status_code | content (bytes) |
| Ruby | F::Api::ApiError | code | response_body |
// TypeScript — results have no `error` field; failures throw.
import { FactorialApiError } from "@factorialco/api-client";
try {
const { data } = await client.employees.employees.get({ path: { id: "42" } });
} catch (err) {
if (err instanceof FactorialApiError) console.error(err.status, err.url, err.body);
else throw err; // transport failure (DNS/connection), not an HTTP response
}# Python
from factorial_api_client.generated.errors import UnexpectedStatus
try:
employees = client.employees.employees.list()
except UnexpectedStatus as e:
print(e.status_code, e.content)# Ruby
begin
api.employees.employee.list
rescue F::Api::ApiError => e
puts e.code, e.response_body
endAPI versioning
The SDK targets a specific Factorial API date version (e.g. 2026-04-01), pinned by the SDK's major version. Newer API versions ship as new SDK majors.
Webhooks
Webhooks are HTTP POSTs Factorial sends to your target_url when an event happens.
How delivery works
- You create a webhook subscription for a
subscription_type(e.g.
ats/application/create), pointing at a target_url, with your company_id.
- When the event fires, Factorial sends a
POSTtotarget_url. **The payload is
the resource object at the top level* — it is not* wrapped in a { type, data } envelope. Use a distinct URL per event if you need to tell them apart easily.
- If you set a
challengewhen subscribing, Factorial echoes it back in the
x-factorial-wh-challenge request header so you can verify the source.
- Optional author headers
x-factorial-author-id/x-factorial-author-type
(employee or company) identify who triggered the event, when safe to expose.
- Retry policy: up to 20 retries over 48h; after that the subscription is
disabled and you are emailed. Re-enable with a PUT { enabled: true }.
Manage subscriptions via the SDK
await client.apiPublic.webhookSubscriptions.create({
subscription_type: "ats/application/create",
target_url: "https://example.com/webhooks/factorial",
challenge: "a-secret-you-choose",
company_id: 55,
});client.api_public.webhook_subscription.create(body={
"subscription_type": "ats/application/create",
"target_url": "https://example.com/webhooks/factorial",
"challenge": "a-secret-you-choose",
"company_id": 55,
})Type the handler payload with the exported webhook types:
import type { AtsApplicationCreateWebhook, WebhookPayloadMap, WebhookSubscriptionType }
from "@factorialco/api-client";
// Direct alias
function onApplicationCreated(payload: AtsApplicationCreateWebhook) { /* ... */ }
// Typed dispatch keyed on the runtime subscription_type
function handle<T extends WebhookSubscriptionType>(type: T, payload: WebhookPayloadMap[T]) { /* ... */ }from factorial_api_client import AtsApplicationCreateWebhook, WEBHOOK_PAYLOAD_TYPES
def on_application_created(payload: AtsApplicationCreateWebhook) -> None: ...
# Runtime lookup of the model class for a subscription_type
model_cls = WEBHOOK_PAYLOAD_TYPES["ats/application/create"]The full list of events, their subscription_type, and payload fields is in reference/webhooks.md.
References
Generated, SDK-specific lookup tables (consult these for exact names/shapes):
reference/webhooks.md— every webhook event, itssubscription_type, and payload fields.reference/sdk-methods.md— every REST endpoint grouped by namespace/resource, with the SDK call.
For prose guides (OAuth flows, API keys, versioning, webhook policies, etc.), use the live docs — always current, not vendored here:
- Getting started: <https://apidoc.factorialhr.com/docs/getting-started>
- Full API reference: <https://apidoc.factorialhr.com/reference>




