Competrace API
Add, update and remove employees from your HR tool, and send finalized assessments to the tools your team uses. Most teams connect through Zapier and never call the API directly. The reference below is for both.
Connect with Zapier
The Competrace app on Zapier is invite-only until Zapier approves its public listing, so it doesn’t show up in Zapier’s search yet. Join Competrace on Zapier with your Zapier account, then add it to a zap and paste an API key when Zapier asks you to connect your account.
The app offers these steps:
- Find Employee. Finds an employee or a pending invite by email.
- Create Employee. Invites a new employee and sends the invite email. Does nothing if the person is already a member or invited.
- Update Employee. Changes an employee's department or level. Admins are skipped.
- Remove Employee. Permanently removes an employee and cancels their open assessments, or revokes a pending invite. Admins are skipped. This cannot be undone.
- Assessment Finalized. Triggers when an assessment is finalized. Sends a summary: person, department, level, date, overall readiness and a link.
- Import the people you already have with a CSV file first. Zapier only reacts to new hires, changes and leavers, never to records that already exist in your HR tool.
- Create an API key in Competrace under Settings → Integrations (admins only).
- Connect the key in Zapier and build your zaps in your own Zapier account.
When Competrace skips an event, for example a leaver who is an admin, the Zapier step shows as skipped rather than failed. When a limit is reached, Zapier waits and retries on its own.
API keys
Admins create keys in Settings → Integrations → Create API key. The key is shown once: copy it then, because Competrace stores only a hash of it. A key belongs to the organization and has full access to the endpoints below. There are no scopes.
- An admin can revoke a key at any time. It stops working at once.
- Keys are revoked automatically when the admin who created them leaves the organization or stops being an admin. Their zaps stop working until someone connects a new key. Settings shows the reason next to the revoked key.
- Everything done with a key appears in Activity as “API: key name”.
- Keep keys secret. Anything done with a key counts as done by your organization.
Requests
The base URL is https://competrace.com/api/v1. HTTPS only. Send the key as a Bearer token:
curl https://competrace.com/api/v1/me \
-H "Authorization: Bearer ct_your_api_key"- POST and PATCH bodies are JSON only, with
Content-Type: application/json, and at most 16 KB. - Unknown fields are refused with
400 bad_request, so a typo never goes unnoticed. - Emails are matched without regard to case and can be up to 254 characters. Names follow the same rules as in the app.
- Every response carries an
X-Request-Idheader. Quote it when you contact support. - The API is for servers and tools like Zapier. It sends no CORS headers and ignores browser cookies.
Who the API manages
- The API manages people with the member role: active employees and pending invites. Admins are found by Find Employee, but never changed or removed.
- Email is the identity. The
idof an employee is their email. - Department and level take an id or an exact name. Names are matched ignoring case and extra spaces, so
"sales"finds “Sales”. Only active departments and levels that are not archived count. An unknown or ambiguous value is refused with422 validation_failed, and the message lists the valid names. - Create is idempotent. If the person is already an employee or has a pending invite, Competrace returns them and sends no email. That includes an invite that has expired: it is not sent again. Press “Resend” on the Employees page for that person.
- People added through the API count like anyone else. On Free they count toward the 10-user cap. On Team they become billed seats when they accept their invite.
- The names of active employees can’t be changed through the API. Update covers department and level only.
Endpoints
Every example uses the base URL above and the Authorization header. All dates are ISO 8601 in UTC.
GET /me
The organization and the key you are using. Zapier uses it to test the connection.
{
"organization": {
"name": "Acme",
"slug": "acme",
"plan": "team"
},
"api_key": {
"name": "Zapier – BambooHR"
}
}GET /departments
Active departments, sorted by title.
[
{
"id": "5b0f8e2a-3c41-4d6e-9a7b-1f2c3d4e5a60",
"title": "Sales"
},
{
"id": "1a7c9e3b-5d2f-4b8a-8c6e-4f3a2b1c0d9e",
"title": "Support"
}
]GET /levels
Levels that are not archived, in their order in the app.
[
{
"id": "9d2e7c14-8b3a-4f51-a6c9-0e1d2f3a4b75",
"name": "Junior"
},
{
"id": "c3a1b5d7-6e24-4f8a-9b0c-7d6e5f4a3b21",
"name": "Middle"
}
]GET /employees?email=
Find Employee. Returns an empty list, or a list with the one active employee or pending invite for that email. A soft-deleted department reads as null.
curl "https://competrace.com/api/v1/employees?email=ann.lee%40example.com" \
-H "Authorization: Bearer ct_your_api_key"[
{
"id": "ann.lee@example.com",
"email": "ann.lee@example.com",
"name": "Ann",
"surname": "Lee",
"status": "active",
"department": {
"id": "5b0f8e2a-3c41-4d6e-9a7b-1f2c3d4e5a60",
"title": "Sales"
},
"level": {
"id": "c3a1b5d7-6e24-4f8a-9b0c-7d6e5f4a3b21",
"name": "Middle"
},
"joined_at": "2026-09-29T08:03:10.271+00:00",
"invited_at": null,
"invite_sent_at": null,
"invite_expires_at": null
}
]POST /employees
Create Employee. Invites the person and sends the invite email right away, from your organization’s name. email, department and level are required; name and surname are optional.
201: invited now.200: already an employee or invited. Nothing changed and no email was sent.- Both answer with the employee plus
warnings, a list of things worth knowing, such as an invite email that could not be sent. It is empty when there is nothing to report. invite_sent_atisnullwhen the invite email was not sent yet, or was sent but Competrace could not record it. On a201,warningssays which.429 invite_limitor403 seat_limit: see Limits.
curl -X POST https://competrace.com/api/v1/employees \
-H "Authorization: Bearer ct_your_api_key" \
-H "Content-Type: application/json" \
-d '{"email":"ann.lee@example.com","name":"Ann","surname":"Lee","department":"Sales","level":"Junior"}'{
"id": "ann.lee@example.com",
"email": "ann.lee@example.com",
"name": "Ann",
"surname": "Lee",
"status": "invited",
"department": {
"id": "5b0f8e2a-3c41-4d6e-9a7b-1f2c3d4e5a60",
"title": "Sales"
},
"level": {
"id": "9d2e7c14-8b3a-4f51-a6c9-0e1d2f3a4b75",
"name": "Junior"
},
"joined_at": null,
"invited_at": "2026-09-28T09:12:44.512+00:00",
"invite_sent_at": "2026-09-28T09:12:45.103+00:00",
"invite_expires_at": "2026-10-05T09:12:44.512+00:00",
"warnings": []
}PATCH /employees
Update Employee. Changes the department, the level, or both, of an active employee or a pending invite. Send at least one. null is refused: department and level can’t be cleared. Moving an active employee up a level sends the promotion email, as in the app. Changes to a pending invite appear in Activity too.
If the person is changed in Competrace at the same moment, the update answers 409 conflict. For an active employee the department is saved before the level, so part of the change may already be saved. Sending the same request again is safe.
{
"email": "ann.lee@example.com",
"level": "Middle"
}{
"result": "updated",
"reason": null,
"warnings": [],
"employee": {
"id": "ann.lee@example.com",
"email": "ann.lee@example.com",
"name": "Ann",
"surname": "Lee",
"status": "active",
"department": {
"id": "5b0f8e2a-3c41-4d6e-9a7b-1f2c3d4e5a60",
"title": "Sales"
},
"level": {
"id": "c3a1b5d7-6e24-4f8a-9b0c-7d6e5f4a3b21",
"name": "Middle"
},
"joined_at": "2026-09-29T08:03:10.271+00:00",
"invited_at": null,
"invite_sent_at": null,
"invite_expires_at": null
}
}DELETE /employees?email=
Remove Employee. An active employee is permanently deleted with their assessment history, the same as removing them on the Employees page. This can’t be undone. Any pending invite for the same email is revoked too. A pending invite on its own is revoked. Asking again for someone already gone returns a not_found skip, so a repeated call is safe.
curl -X DELETE "https://competrace.com/api/v1/employees?email=ann.lee%40example.com" \
-H "Authorization: Bearer ct_your_api_key"{
"result": "removed",
"reason": null,
"warnings": [
"1 in-progress assessment had them as its only assessor and was cancelled."
],
"employee": {
"id": "ann.lee@example.com",
"email": "ann.lee@example.com",
"name": "Ann",
"surname": "Lee",
"status": "active",
"department": {
"id": "5b0f8e2a-3c41-4d6e-9a7b-1f2c3d4e5a60",
"title": "Sales"
},
"level": {
"id": "c3a1b5d7-6e24-4f8a-9b0c-7d6e5f4a3b21",
"name": "Middle"
},
"joined_at": "2026-09-29T08:03:10.271+00:00",
"invited_at": null,
"invite_sent_at": null,
"invite_expires_at": null
}
}GET /assessments/finalized
The Assessment Finalized trigger. The 50 most recent finalized assessments, newest first. Each id is unique, so a poller can tell new ones from ones it has seen. Assessments that were deleted since are left out.
It is a summary only: the person, the department and level the assessment was run against, the date, their overall readiness and a link to it in Competrace. No per-skill scores.
overall_readiness is a whole number from 0 to 100: the percent of the next level’s requirements the person meets, the same number their page in Competrace shows. It is 100 only when every requirement is met. It is read when you call the API, not as of the finalized date, so an older item shows the person’s readiness today. It is null when there is nothing to score, for example when the person has no department or level, or is already at the top level.
[
{
"id": "48213",
"finalized_at": "2026-11-02T14:30:05.918+00:00",
"email": "ann.lee@example.com",
"name": "Ann",
"surname": "Lee",
"department": {
"id": "5b0f8e2a-3c41-4d6e-9a7b-1f2c3d4e5a60",
"title": "Sales"
},
"level": {
"id": "c3a1b5d7-6e24-4f8a-9b0c-7d6e5f4a3b21",
"name": "Middle"
},
"overall_readiness": 72,
"url": "https://competrace.com/acme/assessments/7e4d2c1b-9a8f-4e3d-b2c1-0f9e8d7c6b5a"
}
]Updates, removals and skips
PATCH and DELETE answer with a result object. result is updated, removed, revoked or skipped. warnings lists side effects worth knowing, such as assessments that were cancelled. employee is the person, or null when there is no one to show.
A skip is a 200, not an error, so Zapier does not retry it. If Competrace can’t record the skip, it answers 500 instead, and retrying is safe. Competrace records every skip in Activity, and admins get a daily summary of API removals, skips and invites whose email may not have gone out. The reason is one of:
admin- Skipped: this person is an admin in Competrace. The API never changes or removes admins.
not_found- Skipped: no employee or pending invite with this email in Competrace.
removals_paused- Skipped: API removals are paused because the removal limit, which refills over 24 hours, was reached. An admin can resume them in Settings → Integrations.
{
"result": "skipped",
"reason": "not_found",
"warnings": [],
"employee": null
}Errors
Errors share one shape:
{
"error": {
"code": "validation_failed",
"message": "No department named \"Salse\". Valid departments: Sales, Support."
}
}The message below is the default. Some errors replace it with a more precise one, like the list of valid names above.
400 bad_request- The request is malformed. Send a JSON body with only the documented fields.
401 invalid_api_key- The API key is missing, invalid or revoked. Create a new key in Settings → Integrations.
403 seat_limit- The Free plan allows up to 10 users, counting pending invites and invites sent in the last 24 hours and since revoked. Upgrade to Team to add more.
422 validation_failed- Some values are invalid. Check the email, department and level.
409 conflict- The person changed at the same moment. Try again.
429 rate_limited- Too many requests from your organization. Zapier will retry automatically.
429 invite_limit- Invite limit reached: this address was invited in the last 24 hours, or your organization used up its invite allowance, which refills over 24 hours. Zapier will retry later.
500 removal_unconfirmed- The removal may have gone through. Check Employees in Competrace before retrying.
500 internal_error- Something went wrong on our side, for example the request body could not be read. The message carries a reference; send it to support@competrace.com.
Limits
Limits are counted per organization, never per key or per IP address. Keys in the same organization share one budget, and one organization can never use up another’s. A refused call changes nothing, so it is always safe to retry.
- Reads: bursts of 60, then 120 per minute per organization.
- Creates, updates and invite revokes: bursts of 30, then 60 per minute per organization.
- Removing an active employee: bursts of 20, then 60 per hour per organization.
- Invite emails: up to 20 at once on Free and 500 on Team. Used ones come back gradually, the full amount over 24 hours.
- One invite email per address every 24 hours.
- Removals: up to 10% of your active employees at once, never fewer than 5. Used ones come back gradually, the full amount over 24 hours. If they run out, removals pause until an admin resumes them in Settings → Integrations.
- Over a limit, the API answers 429 with a Retry-After header, and Zapier retries automatically.
- On Free, the API counts active employees, pending invites, and invites sent in the last 24 hours and since revoked toward the 10-user cap. Past it, Create answers
403 seat_limit. - The removal allowance (the Removals line above) scales with your organization: with 100 active employees that is up to 10 at once, refilling 10 over 24 hours. Once it runs out, removals pause and every admin gets an email. While paused, Remove Employee answers with a skip,
removals_paused, and removes no one. - Removals stay paused until an admin clicks “Resume removals” in Settings → Integrations. That also refills the removal allowance, so the full amount is available again.
A 429 carries a Retry-After header in seconds. It is at least 60 seconds, with a little random spread so retries from many zaps don’t arrive together. Wait at least that long before trying again.
Versioning
/v1 only gets backward-compatible changes, such as new endpoints, new optional fields and new response fields. Anything that would break an existing integration goes to /v2.
Changelog
- : First version: employees, departments, levels and finalized assessments.
Questions about the API go to support@competrace.com.