Stitchflow
Personio logo

Personio User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 11, 2026

Summary and recommendation

Personio exposes a REST API at https://api.personio.de/v1 using a proprietary Bearer token flow: POST credentials to /v1/auth and re-authenticate on 401. This is not RFC 6749 OAuth 2.0-standard OAuth client libraries expecting a /token endpoint will not work without modification.

API credentials are scoped at the attribute level in the Personio UI; fields not explicitly permitted are silently omitted from responses rather than returning an error. All field values in responses are wrapped in {"value": ..., "type": ...} objects and must be unwrapped before use.

Rate limiting is enforced at 200 requests per minute per credential; HTTP 429 is returned on breach, with X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers present but no Retry-After header documented.

API quick reference

Has user APIYes
Auth methodBearer token (client_credentials grant via Personio's own token endpoint; not standard OAuth 2.0 per official docs)
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredEnterprise

Authentication

Auth method: Bearer token (client_credentials grant via Personio's own token endpoint; not standard OAuth 2.0 per official docs)

Setup steps

  1. In Personio, navigate to Settings > Integrations > API Credentials.
  2. Create a new API credential set; assign the required attribute-level permissions (read/write per data category).
  3. Copy the generated Client ID and Client Secret.
  4. POST to https://api.personio.de/v1/auth with JSON body {"client_id": "", "client_secret": ""} to receive a Bearer token.
  5. Include the token in all subsequent requests as Authorization: Bearer .
  6. Tokens expire; re-authenticate when a 401 is returned.

Required scopes

Scope Description Required for
read:employees Read employee profile attributes GET /company/employees, GET /company/employees/{id}
write:employees Create and update employee records POST /company/employees, PATCH /company/employees/{id}
read:absences Read absence/time-off records GET /company/time-offs
write:absences Create absence records POST /company/time-offs
read:attendances Read attendance records GET /company/attendances

User object / data model

Field Type Description On create On update Notes
id integer Personio internal employee ID system-generated immutable Used as path param for individual employee operations
first_name string Employee first name required optional
last_name string Employee last name required optional
email string Work email address required optional Used for login; must be unique
gender string Gender (male/female/diverse) optional optional
status string Employment status (active/inactive/leave) optional optional
position string Job title/position optional optional
department object Department the employee belongs to optional optional References department by name or ID
office object Office/location assignment optional optional
hire_date date (YYYY-MM-DD) Date of hire required optional
contract_end_date date (YYYY-MM-DD) End date for fixed-term contracts optional optional Null for permanent contracts
weekly_working_hours number Contracted weekly hours optional optional
supervisor object Direct manager reference optional optional Contains supervisor employee ID
cost_centers array Cost center assignments with percentage splits optional optional
team object Team assignment optional optional
subcompany object Legal entity / subcompany optional optional
employee_type string Employment type (internal/external) optional optional
profile_picture string (URL) URL to employee profile image optional optional Read-only via API
dynamic_fields object Custom attribute fields defined in Personio configuration optional optional Field keys vary per company configuration

Core endpoints

Authenticate / Get Token

  • Method: POST
  • URL: https://api.personio.de/v1/auth
  • Watch out for: Token lifetime is not explicitly documented; treat as short-lived and re-authenticate on 401.

Request example

POST /v1/auth
Content-Type: application/json
{
  "client_id": "your_client_id",
  "client_secret": "your_client_secret"
}

Response example

{
  "success": true,
  "data": {
    "token": "eyJ..."
  }
}

List Employees

  • Method: GET
  • URL: https://api.personio.de/v1/company/employees
  • Watch out for: Response wraps each field in {"value": ..., "type": ...} objects. Max 200 records per page; iterate with offset for full dataset.

Request example

GET /v1/company/employees?limit=200&offset=0
Authorization: Bearer <token>

Response example

{
  "success": true,
  "data": [
    {"type": "Employee", "attributes": {"id": {"value": 123}, "email": {"value": "jane@example.com"}}}
  ]
}

Get Single Employee

  • Method: GET
  • URL: https://api.personio.de/v1/company/employees/{id}
  • Watch out for: Only attributes enabled in the API credential's permission set are returned.

Request example

GET /v1/company/employees/123
Authorization: Bearer <token>

Response example

{
  "success": true,
  "data": {
    "type": "Employee",
    "attributes": {
      "id": {"value": 123},
      "email": {"value": "jane@example.com"}
    }
  }
}

Create Employee

  • Method: POST
  • URL: https://api.personio.de/v1/company/employees
  • Watch out for: Creating an employee does NOT automatically send a Personio login invitation. A separate action in the UI or via the invite endpoint is needed.

Request example

POST /v1/company/employees
Authorization: Bearer <token>
Content-Type: application/json
{
  "first_name": "Jane",
  "last_name": "Doe",
  "email": "jane@example.com",
  "hire_date": "2024-01-15"
}

Response example

{
  "success": true,
  "data": {
    "id": 456,
    "message": "Employee created."
  }
}

Update Employee

  • Method: PATCH
  • URL: https://api.personio.de/v1/company/employees/{id}
  • Watch out for: Custom/dynamic attributes require the exact internal attribute key as configured in Personio; these keys differ per company.

Request example

PATCH /v1/company/employees/456
Authorization: Bearer <token>
Content-Type: application/json
{
  "position": "Senior Engineer",
  "department": "Engineering"
}

Response example

{
  "success": true,
  "data": {
    "message": "Employee updated."
  }
}

List Absences (Time-offs)

  • Method: GET
  • URL: https://api.personio.de/v1/company/time-offs
  • Watch out for: Date range filters are required; unbounded queries may be rejected or return unexpected results.

Request example

GET /v1/company/time-offs?start_date=2024-01-01&end_date=2024-12-31
Authorization: Bearer <token>

Response example

{
  "success": true,
  "data": [
    {"id": 1, "employee_id": 123, "type": "Vacation", "start_date": "2024-06-01"}
  ]
}

List Attendances

  • Method: GET
  • URL: https://api.personio.de/v1/company/attendances
  • Watch out for: Attendance data access requires the attendance read permission on the API credential.

Request example

GET /v1/company/attendances?start_date=2024-01-01&end_date=2024-01-31
Authorization: Bearer <token>

Response example

{
  "success": true,
  "data": [
    {"id": 99, "employee": {"id": 123}, "date": "2024-01-10", "start_time": "09:00", "end_time": "17:00"}
  ]
}

Get Employee Profile Picture

  • Method: GET
  • URL: https://api.personio.de/v1/company/employees/{id}/profile-picture/{width}
  • Watch out for: Returns binary image data, not JSON. Width parameter is required (pixels).

Request example

GET /v1/company/employees/123/profile-picture/200
Authorization: Bearer <token>

Response example

Binary image data (JPEG/PNG) returned directly in response body.

Rate limits, pagination, and events

  • Rate limits: Personio enforces rate limits per API credential. Official docs state a limit of 200 requests per minute per credential as of the current API version.
  • Rate-limit headers: Yes
  • Retry-After header: No
  • Rate-limit notes: When the limit is exceeded, the API returns HTTP 429. Headers X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset are included in responses. Retry-After header is not documented as standard.
  • Pagination method: offset
  • Default page size: 200
  • Max page size: 200
  • Pagination pointer: limit / offset
Plan Limit Concurrent
All plans (API access) 200 requests/minute 0
  • Webhooks available: Yes
  • Webhook notes: Personio supports webhooks for employee-related events. Webhooks are configured in Settings > Integrations > Webhooks. Payloads are sent as HTTP POST to a configured URL.
  • Alternative event strategy: Poll GET /company/employees with updated_since filter parameter for change detection if webhooks are not feasible.
  • Webhook events: employee.created, employee.updated, employee.deleted, absence.created, absence.updated, absence.deleted, attendance.created, attendance.updated, attendance.deleted

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: Enterprise
  • Endpoint: Not documented

Limitations:

  • Personio does not offer a native SCIM 2.0 endpoint.
  • Automated provisioning is available via Microsoft Entra ID integration (uses Personio's REST API under the hood, not SCIM protocol).
  • JumpCloud can provide a SAML bridge for SSO but does not enable SCIM provisioning natively.
  • Okta integration is available for SSO (OIDC) but SCIM provisioning is not natively supported.
  • Enterprise plan required for SSO and advanced integrations; provisioning automation requires custom integration or third-party middleware.

Common scenarios

Three integration patterns cover the majority of use cases. For onboarding, POST to /v1/company/employees with the required fields, capture the returned employee ID, then PATCH additional attributes in a follow-up call-note that employee creation does not trigger a Personio login invitation automatically.

For incremental sync into a data warehouse or identity graph, use GET /v1/company/employees?

updated_since= with limit=200 and paginate via offset; store the run timestamp as the next sync cursor and handle deletions separately via webhooks or status field checks, since offboarded employees are not returned in delta queries.

For SSO provisioning without native SCIM, the Microsoft Entra ID connector (Enterprise plan only) maps Entra ID attributes to Personio fields and uses Personio's REST API under the hood-this is not SCIM 2. 0, and attribute mapping is constrained to what Personio's connector exposes.

Onboard a new employee and sync to downstream systems

  1. POST /v1/auth to obtain Bearer token.
  2. POST /v1/company/employees with first_name, last_name, email, hire_date, department, position.
  3. Capture the returned employee ID from the response.
  4. PATCH /v1/company/employees/{id} to set additional attributes (supervisor, cost_center, custom fields).
  5. Trigger downstream provisioning (e.g., Entra ID sync) using the Personio employee ID as the source-of-truth identifier.
  6. Manually or via UI send Personio login invitation if the employee needs Personio access.

Watch out for: Employee creation does not auto-invite the user to Personio. Custom attribute keys must be known in advance and are company-specific.

Incremental employee sync to a data warehouse

  1. POST /v1/auth to obtain token.
  2. GET /v1/company/employees?updated_since=&limit=200&offset=0.
  3. Iterate pages by incrementing offset until data array is empty.
  4. Unwrap the nested {value, type} field structure for each employee attribute.
  5. Upsert records into the target system using Personio employee ID as the primary key.
  6. Store the current timestamp as last_sync_timestamp for the next run.

Watch out for: Ensure timezone consistency for updated_since; Personio expects ISO 8601 format. Deleted employees are not returned; handle offboarding separately via webhooks or status checks.

SSO provisioning via Microsoft Entra ID (no native SCIM)

  1. Confirm Enterprise plan is active in Personio.
  2. In Personio Settings > Integrations, enable the Microsoft Entra ID (Azure AD) integration.
  3. In Entra ID, configure the Personio enterprise application using the OIDC credentials provided by Personio.
  4. Map Entra ID user attributes to Personio employee fields within the Entra ID provisioning configuration.
  5. Enable automatic provisioning in Entra ID; Personio's integration uses its REST API (not SCIM) under the hood.
  6. Test with a pilot user group before enabling for all users.

Watch out for: This is not native SCIM; it is a Personio-built Entra ID connector. Provisioning capabilities and attribute mappings are limited to what Personio's connector supports, not the full SCIM 2.0 attribute set.

Why building this yourself is a trap

Personio has no native SCIM 2.0 endpoint. Automated provisioning requires either the Entra ID connector (Enterprise plan, limited attribute coverage) or a purpose-built integration.

For teams building against the REST API to populate an identity graph, two structural issues compound each other: custom attribute keys are company-specific and must be discovered at runtime via a GET call rather than being predictable across tenants, and the nested {value, type} response envelope means field extraction requires an unwrap step that breaks naive JSON path assumptions.

Okta supports OIDC-based SSO with Personio but does not enable SCIM provisioning natively. JumpCloud can provide a SAML bridge for SSO but does not add SCIM provisioning either.

Any integration that assumes standard SCIM semantics will require a middleware layer or a translation component-an MCP server with 60+ deep IT/identity integrations is one approach to avoid building and maintaining that translation layer in-house.

Automate Personio workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 11, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste