Authentication
Send your key as a bearer token on every request to an organisation endpoint. Keys only work from servers: never put one in browser or mobile app code. Public endpoints (company, jobs, verification, profile cards) need no key and allow CORS.
GET /api/v1/postings HTTP/1.1
Host: one-career.com
Authorization: Bearer oc_live_4f2a…- Who can create keys: owners and admins of a verified organisation, in Organisation console → Developers. Up to 20 active keys per organisation.
- Shown once: we store only a peppered SHA-256 hash. If you lose a key, rotate it.
- Acts as its creator: the key has the creator's permissions, limited by its scopes. If the creator leaves or loses the admin role, the key stops working (
invalid_key). - Rotate: rotating issues a new secret with the same name and scopes and revokes the old one immediately.
- Expiry: optional (e.g. 90 days). Expired keys return
invalid_key. - Last used: the console shows when each key was last used, plus daily usage per endpoint.
Test and live keys
oc_test_… keys use the same data and rules as live keys but have a lower rate limit (120/min). Use them for development and CI so a bug can't burn your production quota. Actions are real: a test key that moves an application moves it. Create a separate sandbox posting for experiments.
Scopes
Give each integration only what it needs. A key without a scope gets insufficient_scope (403) for endpoints that need it.
| Scope | Allows |
|---|---|
org:read | Read your organisation profile and grade |
postings:read | List and read job postings |
postings:write | Create, edit, submit, pause, resume and close postings |
applications:read | List and read applications (masked by OC-Reveal) |
applications:write | Move applications between stages |
assessments:read | Read assessment campaigns and aggregate results |
interviews:read | List interview rounds |
interviews:write | Create meeting rooms for interview rounds |
webhooks:manage | Create, update, test and delete webhooks; read the delivery log and redeliver |
offers:read | Read offers: terms, approval chain, signing status and vault copies |
vault:read | List and download your organisation's offer paperwork (Vault) |
Rate limits
| Caller | Limit |
|---|---|
| Live key | 600 requests / minute per key |
| Test key | 120 requests / minute per key |
| Public endpoints | 120 requests / minute per client (hashed IP), cached at the edge for 60 s |
Over the limit you get 429 with Retry-After: <seconds>. Back off and retry. The SDK does this for you. Repeated abuse is reported to Trust & Safety and can lead to keys being revoked.
Errors
Errors always use the same shape. Quote request_id (also in the X-Request-Id header) when you contact support.
{
"error": {
"code": "insufficient_scope",
"message": "This key lacks the postings:write scope",
"request_id": "req_8f1c2b9a0e4d4f7a9b3c2d1e"
}
}| code | HTTP | Meaning |
|---|---|---|
invalid_key | 401 | Missing, malformed, revoked, expired or unknown key, or the admin who created it no longer manages the organisation. |
insufficient_scope | 403 | The key doesn't have the scope this endpoint needs. Create a key with that scope. |
org_inactive | 403 | The organisation is suspended, frozen or no longer verified. |
restricted | 403 | Trust & Safety has restricted hiring for this organisation. |
forbidden | 403 | The action isn't allowed for this record (e.g. a stage move the hiring rules block). |
not_found | 404 | The record doesn't exist or doesn't belong to your organisation (we don't tell you which). |
invalid_request | 400 | A parameter or body field is missing or invalid. The message says which. |
conflict | 409 | The change clashes with existing data. |
rate_limited | 429 | Too many requests. Wait for the number of seconds in the Retry-After header. |
internal | 500 | Our fault. Retry with backoff and contact support with the request_id if it persists. |
Safe retries (Idempotency-Key)
Every write (POST, PATCH, DELETE) accepts an Idempotency-Key header: any unique string up to 100 characters, such as a UUID. If the network drops and you retry with the same key within 24 hours, you get the first response back (with Idempotent-Replayed: true) and the action is not done twice. Reusing a key for a different request returns 409 conflict. Keys are per API key.
curl -X POST https://one-career.com/api/v1/applications/{id}/move \
-H "Authorization: Bearer $ONE_CAREER_API_KEY" \
-H "Idempotency-Key: 7b0c1d2e-move-to-interview" \
-H "Content-Type: application/json" -d '{"stage":"interview"}'Check a key at any time with GET /api/v1/me: its scopes, expiry, rate limit and 30 days of usage.
Pagination
List endpoints return newest first, 20 items by default (limit up to 100). Pass next_cursor back as cursor until has_more is false. Cursors are opaque, so don't parse them. They stay stable while new items arrive.
{ "object": "list", "data": [ … ], "next_cursor": "MjAyNi0xMC0yNFQxMDox…", "has_more": true }ETags and caching
Every GET response carries a weak ETag. Send it back as If-None-Match and you get 304 Not Modified with an empty body when nothing changed. Public responses may be cached by CDNs for 60 seconds. Organisation responses are private, no-store.
Versioning
The version is in the path (/api/v1) and every response has an OC-Api-Version header. Within v1 we only make additive changes: new endpoints, new optional parameters, new fields and new event types. Your code should ignore fields it doesn't know. Breaking changes get a new version and at least 6 months' notice in the changelog.