---
updatedAt: 2026-10-06T08:28:13.000Z
agentTools:
  projectIndex: https://apidoc.factorialhr.com/llms.txt
---

# 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](https://apidoc.factorialhr.com/docs/contact-page) — see [Still need help?](#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](https://app.factorialhr.com/)       | `api.factorialhr.com`        |
| **Demo**       | [app.demo.factorial.dev](https://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](https://apidoc.factorialhr.com/docs/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.

<Callout icon="🚧" theme="warn">
  ### Demo data is not durable

  Everything in the demo environment can be deleted at any time. Do not rely on demo records persisting between test runs.
</Callout>

## 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](https://apidoc.factorialhr.com/docs/api-keys).

**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](https://apidoc.factorialhr.com/docs/oauth-2) and restrict the application's [scopes](https://apidoc.factorialhr.com/docs/oauth-scopes) instead.

**Do credentials expire?**

| Credential          | Expiry          |
| ------------------- | --------------- |
| API Key             | Does not expire |
| OAuth Company Token | 1 hour          |
| 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](https://apidoc.factorialhr.com/docs/authentication).

**Why do some fields come back as&#x20;**`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-10-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.

<Callout icon="❗️" theme="error">
  ### An unsupported version does not fail loudly

  Once 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](https://apidoc.factorialhr.com/changelog) and migrate before the support window closes.
</Callout>

**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](https://apidoc.factorialhr.com/docs/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.

<Callout icon="📘" theme="info">
  ### Design to stay under the limit, not to survive it

  Batch where the endpoint supports it, page with the maximum `limit` of 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.
</Callout>

## Pagination

**How many records come back per request?**

100. 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 `limit` is 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](https://apidoc.factorialhr.com/docs/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](https://status.factorialhr.com/).  |

`401`**&#x20;or&#x20;**`403`**&#x20;— 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&#x20;**`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](https://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](https://apidoc.factorialhr.com/docs/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.

<Callout icon="📘" theme="info">
  ### Set a technical support contact before you go live

  In 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.
</Callout>

## Specs and tooling

**Where is the OpenAPI (Swagger) file?**

The [API Reference](https://apidoc.factorialhr.com/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 `.md` to any documentation page URL to get its Markdown source — for example `.../docs/pagination.md`.
* Fetch [`llms.txt`](https://apidoc.factorialhr.com/llms.txt) for 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:

> **➡️**[**Partner Developers Tech Support**](https://portal.support.factorialhr.com/servicedesk/customer/portal/347/group/478/create/1119)

> **➡️**[**Clients Customer Support**](https://portal.support.factorialhr.com/servicedesk/customer/portal/5/group/7/create/19)

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 `code` field.
* A **timestamp with timezone** for a specific failing request.
* The **authentication method** — API Key, Company Token or User Token.