FAQs
In this section you will find the frequently asked questions about our API.
Most questions that reach Tech Support fall into a handful of categories. This page answers them directly and links to the full guide where one exists.
If your question is not here, use the Technical support form — see Still need help? for what to include so we can answer on the first reply.
Getting started
Which base URL should I use?
| Environment | Application | API base URL |
|---|---|---|
| Production | app.factorialhr.com | api.factorialhr.com |
| Demo | app.demo.factorial.dev | api.eu2.demo.factorial.dev |
Both environments run the same code and are deployed at the same time, so anything that works in demo should work in production. See Environments: production and demo.
My requests succeed but every response is empty. Why?
The most common cause is a credentials/environment mismatch: production credentials sent to the demo server, or the reverse. Credentials are not shared between environments — check that the key or token you are sending was issued in the same environment as the base URL you are calling.
If the responses are not empty but individual fields are null, see the permissions answer under Authentication instead.
How do I get a sandbox to develop against?
Contact your Account Manager or Account Executive, who will provision a demo company and the OAuth credentials to generate tokens against it.
Demo data is not durableEverything in the demo environment can be deleted at any time. Do not rely on demo records persisting between test runs.
Authentication
Who can create an API Key, and how?
Company administrators. In Factorial, go to Configuration in the left sidebar → API → Create API Key. Full walkthrough: API Keys Setup Guide.
Can I restrict what an API Key is allowed to see?
No. An API Key always grants access to every scope available to the company, and this cannot be narrowed. If your integration should only reach a subset of company data, use OAuth 2.0 and restrict the application's scopes instead.
Do credentials expire?
| Credential | Expiry |
|---|---|
| API Key | Does not expire |
| OAuth Company Token | Does not expire |
| OAuth User Token | 1 hour |
A User Token needs refreshing; the other two do not. Build token refresh into any integration that uses User Tokens rather than treating a 401 as a fatal error.
Which method should I use?
API Keys are for internal tools inside your own company. OAuth 2.0 is required for marketplace integrations and any app that other companies will connect to — API Key authentication is being phased out for marketplace use. The full comparison is in Authentication methods.
Why do some fields come back as null?
Because the permission group of the user or token making the request does not have access to that information in Factorial. The API never returns more than the credential is allowed to see in the UI. A company administrator can grant the missing permission from the Factorial interface, after which the field is populated.
This applies to User Tokens most often, since their access is bounded by a specific employee's permission group.
API versioning
How do I pin a version?
The version is part of the request path:
https://api.factorialhr.com/api/2026-07-01/resources/...
Pin it explicitly in every integration. Webhook subscriptions carry their own api_version field, so the payload shape for a subscription stays fixed even as the platform default moves on.
How long is a version supported?
A new version ships at the start of each quarter and is supported for one year, which gives you roughly nine months to review the changelog and migrate before the version you are on falls out of support.
An unsupported version does not fail loudlyOnce a version is out of support, requests to it are served using the oldest supported version's schema — not the version you asked for, and not an error.
Field shapes can change underneath a working integration without a single failed request. Track your pinned version against the changelog and migrate before the support window closes.
Which version should I build against?
The most recent stable release. Newer versions may appear in the version selector while their release notes are still marked Beta — check the API versioning page for the current list and each version's release notes before pinning a beta in production.
Rate limits
What is the limit?
200 requests per minute for POST requests.
What should my client do when it hits the limit?
Back off and retry rather than looping. An exponential schedule — 2s, 4s, 8s, 16s, then alert — is enough to ride out a burst without amplifying it.
Design to stay under the limit, not to survive itBatch where the endpoint supports it, page with the maximum
limitof 100 rather than fetching records one at a time, and put write traffic on a queue so a backlog drains at a steady rate instead of arriving all at once.
Pagination
How many records come back per request?
- Pagination applies to every list request whether or not you ask for it, and 100 is both the default and the maximum page size — a larger
limitis capped, not honoured.
How do I fetch the next page?
Cursor pagination, sorted by ID ascending. Take meta.end_cursor from the response and pass it as after_id on the next request; before_id walks backwards. The response meta block also carries has_next_page, has_previous_page and total.
GET .../employees/employees?limit=100&after_id={end_cursor}
Full detail: Pagination.
How do I get a total count without downloading everything?
Request a single item and read meta.total from the response.
Errors
What do the common status codes mean?
| Status | Meaning | What to do |
|---|---|---|
400 | Bad request | Check payload structure, parameter types and datetime formatting. |
401 | Unauthorized | The credential is missing, malformed or expired. Refresh a User Token and retry once. |
403 | Forbidden | The credential is valid but lacks the scope or permission. Not a bug in the request. |
404 | Not found | The record does not exist, or is outside what this credential can see. |
422 | Business rule rejected | The request was understood but violates a rule. See below. |
429 | Rate limited | Back off and retry. |
5xx | Server error | Retry with backoff; check status.factorialhr.com. |
401 or 403 — which is which?
401 means we could not authenticate you: the credential is absent, malformed or expired. 403 means we authenticated you fine, but this credential is not permitted to do that — a missing OAuth scope, or a permission group that does not cover the resource. Retrying a 403 with the same credential will always fail.
How should I handle a 422?
Where the endpoint returns a machine-readable code alongside the message, branch on the code. Messages are localized and may be returned in Spanish, French or German depending on the account, so string-matching them is unsafe. Treat a 422 as non-retryable — it is a data or business-rule problem, and the same request will be rejected again.
Should I retry?
Retry 429 and 5xx with exponential backoff. Do not retry other 4xx responses. For endpoints that create records, check whether the record already exists before retrying — the API does not deduplicate requests, so a blind retry after an ambiguous failure can create a duplicate.
Is the API down?
Check status.factorialhr.com before raising a ticket for 5xx responses.
Webhooks
My subscription stopped delivering events. Why?
A delivery fails when your endpoint does not return 2xx or does not respond in time. Factorial retries up to 20 times across a 48-hour window, emailing you on attempts 1, 5, 10 and every attempt after that. When the window or the attempt limit is reached, a final email is sent and the subscription is disabled.
Once your service is healthy, re-enable the existing subscription — you do not need to recreate it. See Webhooks Policies.
I received the same event twice. Is that a bug?
No. Because deliveries are retried, your endpoint can receive the same event more than once, and payloads do not carry a unique per-delivery identifier. Your handler must be idempotent by design.
I am receiving events for some employees but not others.
Webhook permissions are attached to the token that created the subscription, and a subscription never receives payloads that its creating token could not see through the API or the UI. A subscription created with a User Token is bounded by that employee's permission group. Use a Company Token or an API Key when the integration needs company-wide visibility.
Who receives the failure emails?
The first contact that can be resolved, in order: partner email from a linked marketplace integration, the company's technical support contact, the OAuth application owner, the subscription author, then company administrators.
Set a technical support contact before you go liveIn Factorial, go to Settings → API Notifications and set the Technical support contact address. Without it, webhook failure and auto-disable alerts land with whoever happened to create the subscription rather than with the team responsible for the integration.
Specs and tooling
Where is the OpenAPI (Swagger) file?
The API Reference is generated from it, and you can download it directly:
https://api.factorialhr.com/oas/
Add a version query parameter for a specific version:
https://api.factorialhr.com/oas/?version=2026-07-01
Can I use this documentation with an AI coding assistant?
Yes. Two things make that reliable:
- Append
.mdto any documentation page URL to get its Markdown source — for example.../docs/pagination.md. - Fetch
llms.txtfor a complete index of every guide page and every endpoint, in Markdown and OpenAPI.
Pointing an assistant at those two, rather than at the rendered HTML, produces markedly better results.
Still need help?
Raise a ticket through the right form:
To get a useful answer on the first reply, include:
- The environment (production or demo) and the API version you are calling.
- The full request: method, path and query parameters, with credentials redacted.
- The response: status code and body, including any
codefield. - A timestamp with timezone for a specific failing request.
- The authentication method — API Key, Company Token or User Token.
Updated 3 days ago

