Log In Get Started

Employee API reference (for developers)

Endpoints, authentication, request and response formats, error codes, limits and examples for the HIPAA Certify Employee API v1.

Administrators Updated Oct 27, 2026 5 min read
https://hipaacertify.com/support/article/employee-api-reference
Download PDF

Base URL: https://hipaacertify.com/api/v1. HTTPS only, JSON in and out.

Authentication#

Send the key in the Authorization header:

Authorization: Bearer hck_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

Keys belong to one organization and carry permissions: read, write, deactivate. After 20 invalid-key attempts from an IP in 10 minutes, further invalid attempts from that IP are refused for a while (valid keys keep working).

Endpoints#

Method Path Permission Purpose
GET /ping read Check the key: returns organization, permissions and limits
GET /employees read List employees (?page=, ?per_page= up to 500, ?email=, ?external_id=, ?active=true/false)
POST /employees/upsert write Add or update up to 1,000 employees
POST /employees/sync write (+ deactivate) Send your whole roster, up to 10,000. Adds and updates, then deactivates people missing from the list

Employee record#

{
  "external_id": "E10442",
  "email": "[email protected]",
  "name": "Jane Doe",
  "job_title": "Registered Nurse",
  "department": "Emergency",
  "hire_date": "2024-03-18",
  "job_codes": ["RN"],
  "active": true
}
Field Required Notes
email Yes Lower-cased. Unique per organization.
name New employees Never blanked on update
external_id Recommended Your HR employee ID: a string or a whole number, up to 100 printable characters (exact match, case-sensitive). Matching uses it first, so email changes are safe.
job_title, department, hire_date No hire_date is YYYY-MM-DD. Unknown departments are created unless "create_departments": false.
job_codes No Must already exist and be active under Job Codes. An unknown code rejects that record.
active No Defaults to true. false deactivates the person, which needs the deactivate permission.

Fields you leave out are not changed. Any other field is rejected, so don't send personal health information or extra HR data.

Matching rules#

  1. external_id already linked to someone → that person, even if their email changed.
  2. Otherwise email → the employee with that email, and the external_id gets linked to them.
  3. No match → a new employee. This uses a plan slot; Medium allows 100, Large and invoiced are unlimited.
  4. A removed employee who comes back (rehire) is restored with their training history.
  5. Owner and manager accounts are never matched or changed. Their emails are rejected with email_belongs_to_admin.

Request options#

{
  "employees": [ { ... }, { ... } ],
  "dry_run": false,
  "send_invites": false,
  "create_departments": true,
  "deactivate_missing": "linked",
  "confirm_mass_deactivation": false,
  "include_unchanged": false
}
  • dry_run: runs everything and returns the results, then undoes it. Use it before going live.
  • send_invites: emails the "set up your password" invitation to new, restored or reactivated people who were never invited. Leave it false if your organization signs in with SSO.
  • deactivate_missing (sync only):
    • "linked" (default): deactivate people the API has seen before who aren't in this list
    • "all": any employee not in the list
    • "none": never deactivate
  • confirm_mass_deactivation: needed when a sync would deactivate more than the safety limit. The default limit is 10% or 25 people.
  • include_unchanged: also list records that didn't change in results.

An empty employees list is always refused, so a broken export can't deactivate everyone.

Response#

{
  "mode": "sync",
  "dry_run": false,
  "summary": { "received": 412, "created": 3, "updated": 7, "restored": 1, "reactivated": 0,
               "deactivated": 2, "unchanged": 398, "errors": 1 },
  "results": [
    { "index": 5, "external_id": "E10442", "email": "[email protected]", "user_id": 3051, "status": "updated", "changed": ["job_title"] },
    { "index": 9, "external_id": "E20017", "email": "bad@", "status": "error",
      "error": { "code": "invalid_record", "message": "The email field must be a valid email address." } },
    { "index": null, "external_id": "E00881", "email": "[email protected]", "user_id": 2210, "status": "deactivated", "reason": "not_in_sync" }
  ]
}

A request that is processed returns 200, even if some records had errors. Check summary.errors. A record with an error that matches an existing employee is still treated as "present", so it never causes that person to be deactivated.

Idempotency#

Send Idempotency-Key: <unique value> on POST requests. For 24 hours, retrying the same key and body returns the original response, with header Idempotent-Replayed: true, and nothing is applied twice. Reusing the key with a different body returns idempotency_key_reused.

Limits#

  • 120 requests per minute per key. Headers X-RateLimit-Limit and X-RateLimit-Remaining are included, and Retry-After is sent on 429.
  • Calls for one organization are processed one at a time, so parallel requests can't exceed your plan limit.

Errors#

HTTP error.code Meaning
401 invalid_key, key_expired Missing, wrong, revoked or expired key
403 ip_not_allowed Key restricted to other IP addresses
403 api_not_enabled, api_disabled Not enabled by HIPAA Certify, or turned off by the owner
402 plan_not_eligible, subscription_inactive, payment_failed Plan or billing doesn't allow API use right now
403 insufficient_scope Key lacks the needed permission
409 mass_deactivation_guard Sync would deactivate too many. Nothing was changed.
409 sync_in_progress Another request for your organization is still running. Retry in a minute.
413 too_many_records More than 1,000 (upsert) or 10,000 (sync)
422 invalid_body, empty_request, unknown_options, idempotency_key_reused Fix the request
429 rate_limited, too_many_failed_attempts Slow down; see Retry-After
503 api_unavailable Temporarily switched off by HIPAA Certify

Record-level codes (inside results):

  • invalid_record, unknown_fields, duplicate_in_request
  • email_in_use, email_belongs_to_admin, email_held_by_removed_employee
  • external_id_conflict, unknown_job_code, unknown_department
  • employee_limit_reached, insufficient_scope

Example#

curl -X POST https://hipaacertify.com/api/v1/employees/sync \
  -H "Authorization: Bearer $HC_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: nightly-2026-10-28" \
  -d '{"dry_run": true, "employees": [
        {"external_id":"E10442","email":"[email protected]","name":"Jane Doe","department":"Emergency"}
      ]}'

Last updated October 27, 2026 · Steps checked against the platform on October 28, 2026.

Was this article helpful?

Still need help?

Our support team answers every message. Tell us what you were trying to do and what you saw on screen.

Contact support