The Jadiroon public API lets your ERP, HR system or data platform push and pull every core record of your workspace: the Organizational and Functional Structure, positions, employees, candidates, openings, applications, interviews and offers. Webhooks tell your systems about every change, bulk upsert loads up to 1,000 records per request for migrations, and an audit feed streams the audit log to your SIEM.
The API applies the same rules as the app. A record written through the API goes through the same validation, permission checks and side effects as the same action on screen, and it is recorded in the audit log with your integration as the actor.
Base URL and format
- Base path:
https://<your Jadiroon domain>/api/v1, for examplehttps://jadiroon.com/api/v1. - JSON only, in and out (
Content-Type: application/json). Field names are camelCase; bilingual text is{ "ar": "…", "en": "…" }; times are ISO 8601 in UTC. - The machine-readable description is at
/api/v1/openapi.json(OpenAPI 3.1, no key needed). The API reference page renders it. - Ids, pagination, errors and idempotency follow the conventions.
Integrations and API keys
An integration is a named machine client, for example "Oracle HCM – production". It owns:
- a permission scope (which modules it may read or change),
- one or more API keys,
- an optional IP allowlist,
- zero or more webhook endpoints.
Only holders of the locked Workspace Admin role manage integrations, in Settings → Integrations (see Integrations). Each environment (for example a test and a production workspace) needs its own integration and keys.
Keys
- A key looks like
jdr_live_followed by 40 letters and digits. It is shown once, when it is created; Jadiroon stores only a SHA-256 hash and the first 12 characters (the prefix you see in the list and in the audit log). - A key may have an expiry date. A revoked or expired key gets
401 unauthorized. - An integration can hold several keys at once, so you can rotate without downtime: create a new key, deploy it, check its Last used time, then revoke the old one.
- Keep keys in a secret store, never in source code or browser apps.
Authenticating
Send the key as a bearer token on every request:
curl https://jadiroon.com/api/v1/ping -H "Authorization: Bearer $JADIROON_KEY"
GET /api/v1/ping needs no permission and answers with the integration the key belongs to, which makes it a good first call:
{ "ok": true, "actor": { "kind": "integration", "integrationId": "cmv…", "name": "Oracle HCM – production", "keyPrefix": "jdr_live_8Gq" }, "workspaceId": "cmv…" }
Scopes (permissions)
An integration holds workspace permission keys from the same catalog as the Roles tab: View and Act per module, plus a few named keys. Its data scope is always the whole workspace.
| Key | What it unlocks in the API |
|---|---|
structure.org.view / structure.org.act | Read / write org units and positions |
structure.functional.view / structure.functional.act | Read the Functional Structure (sectors, sub-sectors, job families) and job descriptions / write job descriptions |
structure.functional.salary | Read pay scales and the pay fields of positions and job descriptions (without it they are null in responses and webhook payloads, and pay changes are left out of the audit feed); write pay fields |
people.view / people.act | Read / write employees |
recruitment.view / recruitment.act | Read candidates, openings, applications, interviews and offers / create and update candidates, applications and interviews |
recruitment.hiring.open | Open and close openings |
recruitment.offer.issue | Move an application to the offer stage |
recruitment.hire | Hire a candidate (also needs recruitment.candidate.contact) |
recruitment.candidate.contact | See candidate email, phone and national ID; without it they are masked in responses and webhook payloads, and CV and offer-letter downloads return 403 |
users.view | Read staff users and roles |
audit.view | Read the audit feed and subscribe to audit.recorded |
Rules that come with the catalog:
- Act includes View for the same module.
recruitment.candidate.contactdoes not imply anything else. - A key without the permission an endpoint needs gets
403 forbiddenwithdetails.requiredlisting the keys. - Webhooks follow the same scope: an endpoint only receives events of resources its integration can read.
- Each resource page lists the exact permission per endpoint, and so does the reference (
x-permission).
Rate limits
- 600 requests per minute per key, counted in fixed one-minute windows shared by every server.
- Above the limit the API answers
429 rate_limitedwith aRetry-Afterheader (seconds until the next window). Wait that long, then continue. - A bulk upsert request counts as one request, whatever its row count.
- Each key has its own budget; rotating keys does not reset anyone else's.
IP allowlist
A Workspace Admin can restrict an integration to IP addresses or CIDR ranges (IPv4 or IPv6), in the integration's Access tab. A request from any other address gets 403 forbidden ("Request IP is not on the integration allowlist"). An empty list allows every address for a workspace integration. A deployment-level read integration must always have a list.
The address checked is the client's public IP as seen by the server. On Jadiroon's cloud it comes from the hosting edge, which overwrites X-Forwarded-For and X-Real-IP, so a client cannot fake it by sending those headers. On a self-hosted deployment, the operator sets TRUSTED_PROXY to the number of reverse proxies in front of Jadiroon (for example 1) or to a comma list of the proxies' IP addresses or CIDR ranges; the client IP is then read from the right of X-Forwarded-For, skipping those proxies, and entries a client added on the left are ignored. A self-hosted deployment behind a proxy must set TRUSTED_PROXY. Without it, the deployment trusts no forwarded header: every integration with an allowlist gets 403 forbidden, and sign-in limits apply per account instead of per address (someone who knows an account's email can keep that account rate-limited).
Responses that stop every request
| Status and code | When |
|---|---|
401 unauthorized | No Authorization: Bearer header, an unknown key, or a revoked or expired key. |
403 forbidden | The integration is paused, or the request IP is not on its allowlist. Every key of the integration gets this until it is resumed or the list is fixed. |
403 workspace_inactive | The workspace is suspended or deleted. |
429 rate_limited | Too many requests this minute; see Retry-After. |
What is recorded
- Every write is recorded in the workspace audit log with the actor Integration · (integration name), the key prefix, and the request's IP address and user agent. It shows in Settings → Audit log and in the record's History panel.
- Reads are not recorded.
- Key, permission, allowlist and endpoint changes are recorded as
settings.integration.*actions.
Where to go next
- Conventions: ids, slugs, pagination,
updatedSince, the deletion feed, errors, idempotency. - ERP sync guide: link your records with external ids and keep both sides in step.
- Bulk migration: load an existing workforce.
- Webhooks and the event catalog.
- Audit feed for SIEM.
- The stage pages, from Setup and configuration to Onboarding.