Payloads & Event Types

The structure of a Factorial webhook delivery and the events you can subscribe to.

A webhook delivery is an HTTP POST whose body is the JSON representation of the resource associated with the event. The fields are at the top level of the body — there is no wrapping envelope, and the shape depends on the subscription_type.

POST https://yourapp.com/webhooks/factorial
content-type: application/json
x-factorial-wh-challenge: 2zal4e6d

{ ...the resource that the event happened to... }
📘

The payload for each event is documented individually

Every subscription type in the Webhooks section of the API Reference lists its exact Callback payload, field by field. Treat that as the source of truth for the event you are integrating.

The payload is built using the api_version set on the subscription, so its shape stays stable even as newer API versions ship. Respond with a 200 to indicate the data was received successfully.

Example: employee event

A delivery for employees/employee/update carries the employee record:

{
  "id": 2,
  "access_id": 1,
  "first_name": "Ana",
  "last_name": "Blanco Perez",
  "full_name": "Ana Blanco Perez",
  "preferred_name": "Anita",
  "birth_name": "Anna",
  "gender": "female",
  "identifier": "QKG587532Y",
  "identifier_type": "passport",
  "identifier_expiration_date": "2033-12-31",
  "email": "[email protected]",
  "login_email": "[email protected]",
  "personal_email": "[email protected]",
  "communications_email": "[email protected]",
  "birthday_on": "1990-06-06",
  "nationality": "es",
  "country_of_birth": "Spain",
  "birthplace": "Barcelona",
  "address_line_1": "Calle Adaro 10 1 A",
  "address_line_2": null,
  "postal_code": "27004",
  "city": "Santander",
  "state": "Cantabria",
  "country": "es",
  "phone_number": "657483987",
  "bank_number": "ES6220809324751871912999",
  "bank_number_format": "iban",
  "swift_bic": "CAGLES2M510",
  "social_security_number": "150126298420",
  "company_id": 1,
  "company_identifier": "bb9d281e",
  "legal_entity_id": 1,
  "location_id": 1,
  "default_work_area_id": 1,
  "manager_id": 1,
  "timeoff_manager_id": 1,
  "age_number": 35,
  "pronouns": "She/Her",
  "disability_percentage_cents": 1200,
  "attendable": true,
  "active": true,
  "seniority_calculation_date": "2024-10-07",
  "contact_name": "Laura Delgado",
  "contact_number": "647384950",
  "is_terminating": false,
  "terminated_on": null,
  "termination_reason_type": null,
  "termination_reason": null,
  "termination_type_description": null,
  "termination_observations": null,
  "created_at": "2010-10-06T00:00:00.000Z",
  "updated_at": "2026-10-01T10:32:11.000Z"
}
FieldNotes
idIdentifier of the resource — here, the employee. It is not a per-delivery identifier, so the same id arrives on every future event for that employee.
created_atWhen the resource was created, not when the event fired.
updated_atWhen the resource was last modified. For update events this is effectively the event time.
⚠️

There is no unique delivery identifier

Because id identifies the resource rather than the delivery, you cannot deduplicate retries on it alone. Design your handler to be idempotent by converging on the resource's current state instead — see Make your handler idempotent.

⚠️

Payloads can contain personal data

Deliveries may include personal and financial fields such as national identifiers, bank details and social security numbers. Terminate TLS properly on your endpoint, restrict who can read your logs, and store only the fields your integration actually needs.

Event naming convention

Subscription types follow a domain/resource/action pattern:

timeoff / leave / approve
   │        │        │
   │        │        └── the action that happened
   │        └── the resource it happened to
   └── the Factorial domain it belongs to

You pass this exact string as subscription_type when creating a subscription.

Commonly used events

EventDescription
employees/employee/create_with_contractA new employee was created together with a contract
employees/employee/updateAn employee record was modified
employees/employee/terminateAn employee was marked as terminated
employees/employee/unterminateA termination was reverted
contracts/contract_version/createA new contract version was created for an employee
contracts/contract_version/updateAn existing contract version was modified
timeoff/leave/createA leave request was submitted
timeoff/leave/approveA leave request was approved
timeoff/leave/rejectA leave request was rejected
attendance/shift/clock_inAn employee clocked in a shift
ats/candidate/createA new candidate was created
bookkeepers_management/incidence/updateA compensation incidence was updated
api_public/webhook_subscription/createA webhook subscription was created
📘

This is not the full list

The table above covers the events used by most integrations. The complete, always-current list of subscription types — with the callback payload for each — is in the Webhooks section of the API Reference.

Request headers on a delivery

HeaderAlways sentDescription
x-factorial-wh-challengeOnly if a challenge was setEchoes the subscription's challenge value so you can verify the request.
x-factorial-author-idNoID of the employee or company that triggered the event.
x-factorial-author-typeNoEither employee or company.

Author headers are sent only when the author can be safely exposed to the subscriber; otherwise they are omitted. They are metadata only and never change the body.

Next step

See Webhooks Policies for what happens when a delivery fails, and Best Practices & Troubleshooting for how to build a handler that copes with it.


Did this page help you?