curl -X POST "https://api.dcycle.io/v2/own-workforce/employees" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 2c1b0a99-8877-4665-9443-322110ffeedd" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"nationality": "ES",
"location": "ES",
"contract_type": "temporary",
"contract_start_date": "2024-02-01",
"contract_end_date": "2024-12-31",
"contract_end_reason": "change_of_contract",
"working_hours_ratio": 1,
"labor_agreement": true,
"employment_category": "Lab technician",
"remuneration_amount": 28500,
"remuneration_currency_unit_id": "d3e37f2b-0fc3-4532-82f8-3890ab56ad37",
"remuneration_start_date": "2024-02-01",
"client_row_id": "HR-00412/1"
},
{
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"location": "ES",
"contract_type": "permanent",
"contract_start_date": "2025-01-01",
"working_hours_ratio": 0.8,
"labor_agreement": true,
"employment_category": "Lab technician",
"client_row_id": "HR-00412/2"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/own-workforce/employees",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={
"records": [
{
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"location": "ES",
"contract_type": "permanent",
"contract_start_date": "2025-01-01",
"working_hours_ratio": 0.8,
"labor_agreement": True,
"employment_category": "Lab technician",
}
]
},
timeout=60,
)
response.raise_for_status()
for item in response.json()["items"]:
print(item["source_row_index"], item["employee"]["id"])
{
"items": [
{
"source_row_index": 0,
"client_row_id": "HR-00412/1",
"employee": {
"id": "6f5e4d3c-2b1a-4f09-8e7d-6c5b4a392817",
"organization_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"nationality": "ES",
"created_at": "2026-10-19T09:12:44.031Z",
"updated_at": "2026-10-19T09:12:44.031Z"
}
},
{
"source_row_index": 1,
"client_row_id": "HR-00412/2",
"employee": { "id": "6f5e4d3c-2b1a-4f09-8e7d-6c5b4a392817", "...": "the same employee" }
}
]
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 1,
"client_row_id": "HR-00412/2",
"error_code": "CONTRACT_OVERLAP",
"error_params": { "field": "contract_start_date" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
Create Workforce Employees (v2)
Load up to 1,000 rows of the own workforce template in one request: employees, contracts and remunerations
POST
/
v2
/
own-workforce
/
employees
curl -X POST "https://api.dcycle.io/v2/own-workforce/employees" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 2c1b0a99-8877-4665-9443-322110ffeedd" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"nationality": "ES",
"location": "ES",
"contract_type": "temporary",
"contract_start_date": "2024-02-01",
"contract_end_date": "2024-12-31",
"contract_end_reason": "change_of_contract",
"working_hours_ratio": 1,
"labor_agreement": true,
"employment_category": "Lab technician",
"remuneration_amount": 28500,
"remuneration_currency_unit_id": "d3e37f2b-0fc3-4532-82f8-3890ab56ad37",
"remuneration_start_date": "2024-02-01",
"client_row_id": "HR-00412/1"
},
{
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"location": "ES",
"contract_type": "permanent",
"contract_start_date": "2025-01-01",
"working_hours_ratio": 0.8,
"labor_agreement": true,
"employment_category": "Lab technician",
"client_row_id": "HR-00412/2"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/own-workforce/employees",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={
"records": [
{
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"location": "ES",
"contract_type": "permanent",
"contract_start_date": "2025-01-01",
"working_hours_ratio": 0.8,
"labor_agreement": True,
"employment_category": "Lab technician",
}
]
},
timeout=60,
)
response.raise_for_status()
for item in response.json()["items"]:
print(item["source_row_index"], item["employee"]["id"])
{
"items": [
{
"source_row_index": 0,
"client_row_id": "HR-00412/1",
"employee": {
"id": "6f5e4d3c-2b1a-4f09-8e7d-6c5b4a392817",
"organization_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"nationality": "ES",
"created_at": "2026-10-19T09:12:44.031Z",
"updated_at": "2026-10-19T09:12:44.031Z"
}
},
{
"source_row_index": 1,
"client_row_id": "HR-00412/2",
"employee": { "id": "6f5e4d3c-2b1a-4f09-8e7d-6c5b4a392817", "...": "the same employee" }
}
]
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 1,
"client_row_id": "HR-00412/2",
"error_code": "CONTRACT_OVERLAP",
"error_params": { "field": "contract_start_date" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
← Own Workforce API
Load your headcount from your HR system: employees, their contracts and what they earn. A record is a row of the
own workforce import template: an employee, one of its contracts
and, optionally, one remuneration of that contract. Send one record per contract, or per remuneration of a
contract; the records of one employee share its
The overlaps are checked once every record is otherwise valid.
Request-level errors:
external_employee_id.
The request applies the records as the file import does and answers 201 Created with the employee of each record.
Nothing is calculated, so there is no job to follow.
Beta. Part of the bulk ingest API, currently in beta. The contract may still change before general availability.
curl -X POST "https://api.dcycle.io/v2/own-workforce/employees" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 2c1b0a99-8877-4665-9443-322110ffeedd" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"nationality": "ES",
"location": "ES",
"contract_type": "temporary",
"contract_start_date": "2024-02-01",
"contract_end_date": "2024-12-31",
"contract_end_reason": "change_of_contract",
"working_hours_ratio": 1,
"labor_agreement": true,
"employment_category": "Lab technician",
"remuneration_amount": 28500,
"remuneration_currency_unit_id": "d3e37f2b-0fc3-4532-82f8-3890ab56ad37",
"remuneration_start_date": "2024-02-01",
"client_row_id": "HR-00412/1"
},
{
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"location": "ES",
"contract_type": "permanent",
"contract_start_date": "2025-01-01",
"working_hours_ratio": 0.8,
"labor_agreement": true,
"employment_category": "Lab technician",
"client_row_id": "HR-00412/2"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/own-workforce/employees",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={
"records": [
{
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"location": "ES",
"contract_type": "permanent",
"contract_start_date": "2025-01-01",
"working_hours_ratio": 0.8,
"labor_agreement": True,
"employment_category": "Lab technician",
}
]
},
timeout=60,
)
response.raise_for_status()
for item in response.json()["items"]:
print(item["source_row_index"], item["employee"]["id"])
{
"items": [
{
"source_row_index": 0,
"client_row_id": "HR-00412/1",
"employee": {
"id": "6f5e4d3c-2b1a-4f09-8e7d-6c5b4a392817",
"organization_id": "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d",
"external_employee_id": "HR-00412",
"birth_date": "1988-04-12",
"gender": "F",
"disabled": "no_disability",
"nationality": "ES",
"created_at": "2026-10-19T09:12:44.031Z",
"updated_at": "2026-10-19T09:12:44.031Z"
}
},
{
"source_row_index": 1,
"client_row_id": "HR-00412/2",
"employee": { "id": "6f5e4d3c-2b1a-4f09-8e7d-6c5b4a392817", "...": "the same employee" }
}
]
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 1,
"client_row_id": "HR-00412/2",
"error_code": "CONTRACT_OVERLAP",
"error_params": { "field": "contract_start_date" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
How it works
Each record is applied as the file import applies a row, in the order sent:- The employee is found by
external_employee_idin thex-organization-idorganization, or created with the record’s personal data. An employee that already exists keeps its personal data (birth date, gender, disability, nationality): the record’s are not applied. - The contract is identified by its start date.
- A contract the employee already has with the same
contract_start_dateis updated with every field of the record except its dates. - A new contract is added. If the stored contract before it is open, that one is closed the day before the new
one starts. If there is none before it but a stored one after, an open new contract ends the day before that
one starts. Contracts sent in the same request are not closed for each other: send the end date of every
contract but the employee’s last, or the request answers
CONTRACT_OVERLAP.
- A contract the employee already has with the same
- The remuneration, when sent, is identified by its start date within the contract. One that already exists is left as it is. A new one closes the open remuneration before it, as contracts do.
422 listing every invalid record and nothing is written. Otherwise everything is written at once.
Request
Headers
string
required
Your API key (see Authentication).
string
required
The organization the employees belong to. A record cannot name another organization: send each organization’s
employees with its own
x-organization-id.string
required
1 to 255 characters, unique per batch. A retry with the same key and body answers the same employees (with
Idempotent-Replayed: true) instead of applying the records again; the same key with another body is
422 IDEMPOTENCY_KEY_REUSED, and while the first request is still being processed 409 IDEMPOTENCY_KEY_IN_PROGRESS.Query Parameters
boolean
default:"false"
Check only: the same
422 for invalid records, nothing written; 200 with
{"dry_run": true, "submitted": n, "valid": n} when every record is valid.Body Parameters
array[object]
required
The template rows, 1 to 1,000. A field not listed here is rejected (
SCHEMA_INVALID). The codes below are the
values of the template’s columns; Get Options Source lists them.Show record
Show record
string
required
The employee’s id in your HR system, up to 255 characters. It identifies the employee in the organization.
date
required
YYYY-MM-DD, from 1900, not in the future.string
required
M, F, O (other) or NS (not stated).string
required
The disability grade:
no_disability, with_disability, disability_33 or disability_65.string
The employee’s nationality, ISO 3166-1 alpha-2 (
ES, FR…).string
required
The country where the contract is worked, ISO 3166-1 alpha-2.
string
required
permanent or temporary.date
required
First day of the contract. It identifies the contract within the employee.
date
Last day of the contract, on or after its start. Omitted: the contract is open. Not applied to a contract
that already exists.
string
Why the contract ended:
resignation, termination, change_of_contract, retirement or fatality.number
required
Share of a full-time working day: over 0 and up to 1 (
0.5 is half time).boolean
required
Whether the contract is covered by a collective bargaining agreement.
string
required
Your job category for the employee, as free text, up to 255 characters.
number
Hours a year the contract establishes: over 0 and up to 2,928. On a contract that already exists, sending
null clears it and omitting the field leaves it as it is.number
What the employee earns in the remuneration’s period, 0 or more. Send it with
remuneration_currency_unit_id and remuneration_start_date.uuid
Euros:
d3e37f2b-0fc3-4532-82f8-3890ab56ad37. Other currencies are not accepted yet.date
First day the remuneration applies. It identifies the remuneration within the contract.
date
Last day the remuneration applies, on or after its start. Omitted: until further notice.
string
Your own reference for the record, up to 255 characters, echoed in errors and in the answer.
Response
201 Created with items: one per record, in the order sent, with its source_row_index, client_row_id and the
employee it was applied to. The records of one employee answer the same employee. Its id is the one
Delete Workforce Employees (v2) takes. Read the contracts and
remunerations with List Workforce Contracts.
Errors
Problem details. Per-record codes inerrors[].error_code of a 422 BULK_RECORDS_REJECTED:
error_code | Cause |
|---|---|
SCHEMA_INVALID | The record does not match the schema: a missing or unknown field (e.g. organization_id), a code that is not one of the listed values (gender, disabled), working_hours_ratio out of range, an end date before its start, a remuneration without its amount, currency or start date… error_params.errors says where. |
REFERENCE_NOT_FOUND | location, nationality, contract_type or contract_end_reason is not a known value. error_params.field says which. |
UNIT_NOT_SUPPORTED | remuneration_currency_unit_id is not euros. |
CONTRACT_OVERLAP | The record’s contract would overlap another contract of the employee: one already stored, or one of another record. |
REMUNERATION_OVERLAP | The record’s remuneration would overlap another remuneration of the contract. |
401 for a missing or invalid API key, 403 LOGGED_USER_NOT_MEMBER when the key’s user is not a
member of the organization, 422 REQUEST_VALIDATION_FAILED for a malformed body (no records, more than 1,000, no
Idempotency-Key), 422 IDEMPOTENCY_KEY_REUSED, 409 IDEMPOTENCY_KEY_IN_PROGRESS, and 429 beyond the
rate limits.
Limits
| Limit | Value |
|---|---|
| Records per request | 1 to 1,000 |
Related Endpoints
Delete Workforce Employees (v2)
Delete employees by id
Own Workforce Import Templates
The template the records follow
Get Options Source
The accepted codes
List Workforce Contracts
The contracts of an employee
Was this page helpful?