Skip to content
Documentation

9bx user guide

HR (HRM)

Integrations and API

Connect HR to your other tools: read HR data from your own software with an API key, get a signed message the moment something happens (an employee is added, a leave request is decided), compare your Google or Microsoft user directory with your employees, and send approved hours to Jira or Asana.

Contents: Who can use it · API keys · What the API returns · API reference · Webhooks · Zapier and Make · Connectors · Example · FAQ

Who can use it

Every plan has the API, webhooks and connectors.

  • API keys and webhooks: staff with the List permission for HR integrations can look. Create lets them make and change keys and webhooks. Delete lets them revoke a key or remove a webhook.
  • Connectors: staff with the connector List, Create, Update and Delete permissions.

Where: HR → Integrations, with three tabs: API keys, Webhooks and Connectors.

API keys

A key lets your own software read HR data. Click New API key and fill in:

  • Name, so you remember what uses it.
  • What it may do: one tick per kind of data the key may read: employees, departments and branches, leave, attendance, document records, payroll run summaries, approvals. Add Manage webhook endpoints if Zapier or Make will subscribe for you.
  • Allowed IP addresses, Requests a minute, Expires on and Branch (all optional). Click Create key. A key tied to a branch sees only that branch and the ones under it.

The key is shown once, when you save. Copy it then. Revoke it any time with Revoke; it stops working at once.

A key reads as the person who made it. It can see only what that person may see on screen: their branches, their data scope and their field privacy. Personal details that person is not allowed to see (date of birth, national ID, pay, bank, medical and similar) come back empty, and the answer lists which fields were hidden. You can tick only scopes you could use yourself; the others are greyed out. If that person later loses access, or leaves, the key stops reading that data. After a person leaves, make a new key.

What the API returns

  • Employees: the list has names, code, work email and phone, department, designation, branch, manager, status and dates. One employee adds the personal fields you may see.
  • Departments, designations, branches, positions.
  • Leave: leave types, leave requests and balances. The reason a person gave for leave is never returned.
  • Attendance: one row per person per day. With no dates, the last 31 days.
  • Document records: title, type, status and dates. Never the file. Sensitive document types appear only if the key's owner may open them.
  • Payroll runs: how many people, status and dates, and the run total only if the key's owner may see pay. Never anyone's individual pay.
  • Approvals: approval requests. A key whose owner may reassign approvals sees all of them; otherwise only the owner's own.

A key can also add, change, deactivate and reactivate employees when you tick that permission; see Employee sync and the employee API, which also covers reading employees from your payroll or ERP software.

Lists come in pages (page, per_page up to 100) with a meta block, and errors always look the same. Each key has a limit of requests per minute; the answer says how many are left, and how long to wait when you go over.

API reference

Your base address and a live description of every call (an OpenAPI document) are on the Integrations screen, under API keys. Send the key in the Authorization header.

GET /api/public/v1/hr/employees?status=active&department_id=3&per_page=50
Authorization: Bearer 9bx_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
{
  "data": [
    {
      "id": 42,
      "employee_code": "E-0042",
      "full_name": "Rahim Uddin",
      "email": "rahim@example.com",
      "department": { "id": 3, "name": "Sales" },
      "branch": { "id": 1, "name": "Dhaka", "code": "DHK" },
      "status": "active",
      "joining_date": "2026-01-05"
    }
  ],
  "meta": { "page": 1, "per_page": 50, "total": 1, "last_page": 1 }
}
Call Scope Filters
GET /hr/employees employees search, status, department_id, designation_id, branch_id, employment_type, updated_since
GET /hr/employees/{id} employees one employee with personal fields
GET /hr/departments, /hr/designations, /hr/branches departments and branches search, status
GET /hr/positions departments and branches search, status, department_id, vacant
GET /hr/leave/types leave
GET /hr/leave/requests leave employee_id, status (0 pending, 1 approved, 2 rejected, 3 cancelled, 4 processing), leave_type_id, from, to
GET /hr/leave/balances leave employee_id, leave_type_id, fiscal_year_id
GET /hr/attendance attendance employee_id, status, from, to
GET /hr/documents document records employee_id, document_type_id, category, status, expiring_within (days)
GET /hr/payroll/runs, /hr/payroll/runs/{id} payroll status, from, to (as 2026-10)
GET /hr/approvals approvals status, subject_type

Common answers: 401 the key is wrong, revoked or expired; 402 your workspace does not have the HR module; 403 the key lacks the scope, or its owner may no longer see that data; 404 not found or not visible to this key; 422 a filter is not valid; 429 too many requests.

Webhooks

A webhook sends a message to your own web address whenever something happens. Open the Webhooks tab and click New endpoint: enter the Endpoint URL (it must start with https:// and be reachable from the internet), tick the Events to send, and click Create endpoint. The signing secret is shown once; copy it.

Event Sent when
employee.created An employee is added
employee.updated An employee record changes. It names the fields that changed
employee.status_changed An employee becomes active, inactive or separated
leave.decided A leave request is approved, rejected or cancelled
attendance.punched An employee punches in or out
approval.decided An approval request is approved, rejected or withdrawn
payroll.run_finalized A payroll run is locked
candidate.hired A candidate is hired

What is in the message. Only open fields: the same shape as the API lists. Personal details (date of birth, ID numbers, pay, bank, medical) are never sent, and a change to only those fields sends nothing. Where someone punched from (map position) and the selfie are never sent. A leave reason is never sent. A payroll message has no total. If you need more, read it from the API with a key whose owner may see it.

Check the signature. Every message has X-9bx-Timestamp and X-9bx-Signature: sha256=..., the SHA-256 HMAC of the timestamp, a dot and the raw body, made with your signing secret. Reject messages with an old timestamp.

X-9bx-Event: leave.decided
X-9bx-Timestamp: 1790000000
X-9bx-Signature: sha256=5d41402abc4b2a76b9719d911017c592...

Delivery and retries. Answer with any 2xx within 10 seconds. If you do not, 9bx tries again after 1 minute, 5 minutes, 30 minutes, 2 hours and 6 hours. The Delivery log (on each endpoint) shows every attempt: status, how long it took, what your server answered and the exact message. Click Send again to resend one, or Send test to check your setup. After many failed deliveries in a row the endpoint is switched off and the admins get an email; turn it back on with Turn on once fixed. New signing secret makes a new secret; messages are signed with it from then on.

Zapier and Make

Both tools work on top of the API and webhooks. Create a key with the scopes you need and Manage webhook endpoints, then:

  • Zapier: use the 9bx HR app definition (link under API keys). Triggers fire instantly for each event above; searches read employees, departments, leave, attendance, payroll runs and approvals. The key is the only thing you paste.
  • Make: import the 9bx HR app definition (link under API keys). It has an instant trigger for each event and a List module for each kind of data.

Each Zap or scenario creates and removes its own webhook; you can see them in the Webhooks tab.

Connectors

The Connectors tab lists what you can connect. Click Connect, fill in the fields, and 9bx checks the details before saving. Saved passwords and keys are never shown again; leave a field empty when editing to keep it. Test checks the connection again. Disconnect removes it.

Google Workspace and Microsoft 365: directory check. 9bx reads your user directory and compares it with your employees by work email. It never writes to your directory or to HR. Click the connection to open it and click Compare directory; the result shows the counts and lists:

  • Only in directory: people in your directory with no employee record.
  • Only in HR: active employees with no directory account.
  • Disabled in directory: active employees whose account is switched off, a leaver HR may not have closed yet.

For Google you need a service account with directory read access, its key file, and an admin email to act as. For Microsoft you need the directory ID, application ID and a client secret of an app that may read users.

Jira and Asana: time sync. Approved timesheet hours go to Jira as work logs and to Asana as time entries.

  1. Connect Jira (site address, account email, API token) or Asana (personal access token).
  2. Click the connection to open it, and under Project mapping match each HR project, or one of its tasks, to a Jira issue key such as WEB-7 or an Asana task ID. Hours on projects with no match are skipped. Click Save.
  3. Click Send approved hours, or turn on Send automatically every night.

Each timesheet entry is sent once. An entry that failed is tried again on the next run. If Jira or Asana refuses the saved details, the run stops and the connector shows the problem; fix the details and save to start again. Only approved timesheets are sent, never drafts.

Already connected elsewhere. Calendar and mail (each person connects their own Google or Microsoft account in their settings) and Zoom meeting links (under Hiring, Connections) keep their own screens. The Connectors tab shows whether they are connected.

Example scenario

Padma Net Support keeps its staff directory in Microsoft 365 and tracks project work in Jira. HR admin Farzana Akter opens Integrations → Connectors, connects Microsoft 365 and clicks Compare directory. The result shows one leaver whose account is still active in HR. She then connects Jira, maps the project "Khulna Fibre Rollout" to the issue KFR-12, and turns on Send automatically every night. Next, on API keys, she creates "Payroll sync" with the employees and leave scopes for the payroll vendor. On Webhooks she adds the vendor's https address for employee.created and leave.decided, and copies the signing secret into the vendor's settings.

Tips

  • Make one key per tool, with only the scopes it needs, and set an expiry.
  • Make keys with an account that has just the access the tool needs, not a full administrator.
  • Use Send test after setting up a webhook, and keep the signing secret private.
  • Use updated_since to read only what changed since your last call.

Troubleshooting and FAQ

The key works but a field is empty. The person who made the key may not see that field. The answer lists hidden_fields. Make the key with someone who may.

403 "no longer has access". The key's owner lost the permission. Give it back, or make a new key.

"This API key's owner is no longer active". The person who made the key left. Make a new one.

My webhook says "points at a private network address". The address must be reachable from the internet over https.

The webhook was switched off. It failed many times in a row. Fix your server, open the Delivery log to see why, then Turn on the endpoint.

I get no employee.updated for a salary change. By design: changes to personal and pay fields are never sent.

Time sync skipped entries. The project is not mapped, or the timesheet is not approved yet.

Employees · Timesheets · Leave · Approvals · Privacy · Access control