factorial-api-sdks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFactorial API SDKs
Factorial API SDK集合
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
(see References).
reference/- TypeScript: (npm)
@factorialco/api-client - Python: (PyPI)
factorial-api-client - Ruby: (RubyGems)
factorial_api
官方SDK集合,通过领域命名空间客户端、分页辅助工具和简洁的身份验证封装了Factorial公开API。本技能将指导Agent如何使用这些SDK以及如何处理Factorial webhook。生成的参考数据存放在目录中(参见参考资料)。
reference/- TypeScript:(npm)
@factorialco/api-client - Python:(PyPI)
factorial-api-client - Ruby:(RubyGems)
factorial_api
Install
安装
bash
npm install @factorialco/api-client # TypeScript
pip install factorial-api-client # Python
gem install factorial_api # Rubybash
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 |
|---|---|
| |
| |
The Factorial API key is JWT-formatted but is sent as, not as a bearer token.x-api-key
Credentials can be passed explicitly or read from the environment
(, , optional ).
FACTORIAL_API_KEYFACTORIAL_TOKENFACTORIAL_BASE_URLts
import { FactorialClient } from "@factorialco/api-client";
const client = new FactorialClient({ apiKey: process.env.FACTORIAL_API_KEY });python
from factorial_api_client import FactorialClient
client = FactorialClient(api_key="YOUR_KEY") # or token="...", or from envruby
require "factorial_api"
api = F::Api.new(api_key: ENV["FACTORIAL_API_KEY"]) # or token:, or from env提供API密钥或OAuth令牌(也可同时提供):
| 选项 | 发送的请求头 |
|---|---|
| |
| |
Factorial API密钥采用JWT格式,但需通过请求头发送,而非作为Bearer令牌。x-api-key
凭据可显式传递,也可从环境变量中读取(、,可选)。
FACTORIAL_API_KEYFACTORIAL_TOKENFACTORIAL_BASE_URLts
import { FactorialClient } from "@factorialco/api-client";
const client = new FactorialClient({ apiKey: process.env.FACTORIAL_API_KEY });python
from factorial_api_client import FactorialClient
client = FactorialClient(api_key="YOUR_KEY") # or token="...", or from envruby
require "factorial_api"
api = F::Api.new(api_key: ENV["FACTORIAL_API_KEY"]) # or token:, or from envCalling endpoints
调用端点
The client is organized as . Namespaces and
resources mirror the REST path .
client.<namespace>.<resource>.<method>/api/<version>/resources/<namespace>/<resource>Method naming is deterministic:
| HTTP / shape | SDK method |
|---|---|
| |
| |
| |
| |
| |
Custom action (e.g. | camelCased action, e.g. |
ts
const { data } = await client.employees.employees.list();
const created = await client.apiPublic.webhookSubscriptions.create({ /* body */ });python
employees = client.employees.employee.list()
sub = client.api_public.webhook_subscription.create(body={...})Ruby differs: it exposes one accessor per resource ()
whose methods are the spec operationIds; required params are positional and
optional query params go in a hash.
api.employees_employeequery_params:ruby
employees = api.employees_employee.employees_employees_get(true, false).dataTo discover the exact namespace/resource/method for any endpoint, consult
(every endpoint with its TS and Ruby calls, grouped)
or the online API reference.
reference/sdk-methods.md客户端采用的组织形式。命名空间和资源与REST路径一一对应。
client.<命名空间>.<资源>.<方法>/api/<版本>/resources/<命名空间>/<资源>方法命名遵循固定规则:
| HTTP方法/请求类型 | SDK方法 |
|---|---|
| |
| |
| |
| |
| |
自定义操作(如 | 驼峰式命名的操作,例如 |
ts
const { data } = await client.employees.employees.list();
const created = await client.apiPublic.webhookSubscriptions.create({ /* body */ });python
employees = client.employees.employee.list()
sub = client.api_public.webhook_subscription.create(body={...})Ruby有所不同:它为每个资源暴露一个访问器(如),其方法为规范的operationId;必填参数为位置参数,可选查询参数需放在哈希中。
api.employees_employeequery_params:ruby
employees = api.employees_employee.employees_employees_get(true, false).data如需查询任意端点对应的命名空间/资源/方法,请查阅(所有端点及其TS和Ruby调用示例,按组划分)或在线API参考文档。
reference/sdk-methods.mdPagination
分页
List endpoints return .
{ data: T[], meta: { end_cursor?, has_next_page, ... } }- — one raw page
list() - — async iterator (TS) / sync or async generator (Python)
paginate() - (TS) /
all()(Python) — fetch every page into one array/listcollect_all() - Ruby: wraps any list call in a lazy Enumerator
F::Api.paginate { |page| ... }
ts
for await (const emp of client.employees.employees.paginate()) { /* ... */ }
const everyone = await client.employees.employees.all({ maxItems: 500 });python
for emp in client.employees.employee.paginate(max_items=50): ...
everyone = client.employees.employee.all()列表端点返回格式的数据。
{ data: T[], meta: { end_cursor?, has_next_page, ... } }- — 返回原始单页数据
list() - — 异步迭代器(TS)/ 同步或异步生成器(Python)
paginate() - (TS)/
all()(Python) — 获取所有页面数据并合并为一个数组/列表collect_all() - Ruby:将任意列表调用包装为惰性枚举器
F::Api.paginate { |page| ... }
ts
for await (const emp of client.employees.employees.paginate()) { /* ... */ }
const everyone = await client.employees.employees.all({ maxItems: 500 });python
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 has no effect. Cursor pagination is sequential (page
N+1 needs page N's ), so on a big dataset (e.g. a full
company-month of ) means dozens of sequential
requests. To keep request counts and latency sane:
limitend_cursorall()attendance/worked_times- Filter first. Use the endpoint's query params (date ranges, ,
ids, …) instead of pulling everything and filtering locally.employee_ids - Sync incrementally. Where an endpoint supports -style filters, fetch only what changed since your last run and cache locally — don't re-pull the full dataset per report.
updated_at - 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_itemsso a bug or unexpected data volume can't turn into an unbounded crawl.maxItems
There is no server-side aggregation endpoint; totals (e.g. hours per employee
per period) must be computed client-side from the raw records.
每页最多返回100条数据——这是服务器端的硬限制,而非默认值(参见分页文档)。传入更大的参数不会生效。游标分页是顺序执行的(获取第N+1页需要第N页的),因此对大型数据集(例如某公司整月的数据)调用会触发数十次顺序请求。为了控制请求数量和延迟,请遵循以下建议:
limitend_cursorattendance/worked_timesall()- 先过滤。使用端点的查询参数(日期范围、、
ids等)进行过滤,而非拉取全部数据后在本地过滤。employee_ids - 增量同步。如果端点支持类过滤条件,仅拉取自上次运行以来变更的数据并在本地缓存——不要每次生成报告时都重新拉取完整数据集。
updated_at - 拆分大查询。将一个大型查询拆分为多个带过滤条件的子查询(按时间窗口或ID分段)并并发执行——每个子查询仍需顺序分页,但整体耗时会大幅降低。总请求数量不变,因此需注意速率限制。
- 合理设置上限。传入/
max_items参数,避免因bug或意外的数据量导致无限制的爬取。maxItems
服务器端没有聚合端点;统计数据(例如每个员工每个时段的工时)必须从原始记录中在客户端计算得出。
Errors
错误处理
The client throws on any non-2xx response (bad/expired token, wrong base URL,
server errors) instead of silently returning empty data. Wrap calls in
try/catch (TS) or try/except (Python).
客户端会在任何非2xx响应(无效/过期令牌、错误的基础URL、服务器错误)时抛出异常,而非静默返回空数据。请将调用代码包裹在try/catch(TypeScript)或try/except(Python)块中。
API versioning
API版本控制
The SDK targets a specific Factorial API date version (e.g. ), pinned
by the SDK's major version. Newer API versions ship as new SDK majors.
2026-04-01SDK针对特定的Factorial API日期版本(例如),该版本由SDK的主版本号固定。更新的API版本会作为新的SDK主版本发布。
2026-04-01Webhooks
Webhook
Webhooks are HTTP POSTs Factorial sends to your when an event happens.
target_urlHow delivery works
- You create a webhook subscription for a (e.g.
subscription_type), pointing at aats/application/create, with yourtarget_url.company_id - When the event fires, Factorial sends a to
POST. The payload is the resource object at the top level — it is not wrapped in atarget_urlenvelope. Use a distinct URL per event if you need to tell them apart easily.{ type, data } - If you set a when subscribing, Factorial echoes it back in the
challengerequest header so you can verify the source.x-factorial-wh-challenge - Optional author headers /
x-factorial-author-id(x-factorial-author-typeoremployee) identify who triggered the event, when safe to expose.company - 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
ts
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,
});python
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:
ts
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]) { /* ... */ }python
from factorial_api_client import AtsApplicationCreateWebhook, WEBHOOK_PAYLOAD_TYPES
def on_application_created(payload: AtsApplicationCreateWebhook) -> None: ...Webhook是Factorial在事件发生时发送到您指定的HTTP POST请求。
target_urlRuntime 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`.- 您需要为特定(例如
subscription_type)创建一个webhook订阅,指定ats/application/create和您的target_url。company_id - 当事件触发时,Factorial会向发送一个
target_url请求。负载为顶层的资源对象——不会被包裹在POST格式的信封中。如果您需要轻松区分不同事件,请为每个事件使用独立的URL。{ type, data } - 如果您在订阅时设置了参数,Factorial会在
challenge请求头中返回该参数,以便您验证请求来源。x-factorial-wh-challenge - 可选的请求头/
x-factorial-author-id(x-factorial-author-type或employee)会标识触发事件的对象(在安全可暴露的前提下)。company - 重试策略:最多重试20次,持续48小时;超过次数后订阅将被禁用,并向您发送邮件通知。可通过请求重新启用订阅。
PUT { enabled: true }
References
通过SDK管理订阅
Generated, SDK-specific lookup tables (consult these for exact names/shapes):
- — every webhook event, its
reference/webhooks.md, and payload fields.subscription_type - — every REST endpoint grouped by namespace/resource, with the SDK call.
reference/sdk-methods.md
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
ts
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,
});python
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,
})—
为处理程序负载添加类型定义
—
使用导出的webhook类型:
ts
import type { AtsApplicationCreateWebhook, WebhookPayloadMap, WebhookSubscriptionType }
from "@factorialco/api-client";
// 直接别名
function onApplicationCreated(payload: AtsApplicationCreateWebhook) { /* ... */ }
// 根据运行时subscription_type进行类型化分发
function handle<T extends WebhookSubscriptionType>(type: T, payload: WebhookPayloadMap[T]) { /* ... */ }python
from factorial_api_client import AtsApplicationCreateWebhook, WEBHOOK_PAYLOAD_TYPES
def on_application_created(payload: AtsApplicationCreateWebhook) -> None: ...—
根据subscription_type在运行时查找模型类
—
model_cls = WEBHOOK_PAYLOAD_TYPES["ats/application/create"]
完整的事件列表、对应的`subscription_type`及负载字段可在`reference/webhooks.md`中查看。—
参考资料
—
生成的SDK专属查询表(如需精确名称/格式,请查阅这些文档):
- — 所有webhook事件、对应的
reference/webhooks.md及负载字段。subscription_type - — 所有REST端点按命名空间/资源分组,并附带SDK调用示例。
reference/sdk-methods.md
如需详细指南(OAuth流程、API密钥、版本控制、webhook策略等),请使用在线文档——始终保持最新,而非本地存储的版本: