Partner API
API keys
Create a server-side key for the partner API, pick its scopes, and rotate it without downtime.
The partner API is for platforms acting on behalf of employers. There is no person in the loop and no consent screen: you authenticate with an API key your own developer account owns.
Keys are server-side only
A tbo_sk_ key in browser JavaScript, a mobile binary, or a public repository is
compromised the moment you ship it. If your integration has no backend, the partner
API is not the right tool.
Create a key
Open your app
On the developer platform, open Applications and pick the app you created. If you have not made one yet, the quickstart covers it.
An app can hold several API keys. One per environment, or one per customer if you want to be able to cut off a single integration, are both reasonable.
Add a key
On the app's Credentials tab, choose Create key. Give it a name you will recognise in six months ("production", "staging", "acme-crm"), tick the scopes it needs, and set an expiry if the key is for a trial. API keys in the platform's sidebar lists every key across all your apps, which is where you rename or revoke one later.
Copy the secret
The key is shown once, in full, at creation. It is stored hashed, so nobody, including Tabbio support, can show it to you again. If you lose it, create a new one and delete the old.
Terminalexport TABBIO_API_KEY="tbo_sk_9Fh3xK7mQr2tV0nLzB6yWd4Sc8Jg1Up5Rt7"
Using a key
Same bearer header as an access token, against the same host.
curl https://server.tabbio.com/v1/companies \
-H "Authorization: Bearer $TABBIO_API_KEY"const response = await fetch("https://server.tabbio.com/v1/companies", {
headers: { Authorization: `Bearer ${process.env.TABBIO_API_KEY}` },
});
const { data: companies, meta } = await response.json();
// meta is { page, pageSize, total, totalPages }import os
import requests
response = requests.get(
"https://server.tabbio.com/v1/companies",
headers={"Authorization": f"Bearer {os.environ['TABBIO_API_KEY']}"},
)
response.raise_for_status()
companies = response.json()["data"]Scopes
Scopes are chosen at creation and cannot be changed afterwards. To widen a key's access, make a new one.
| Scope | Grants |
|---|---|
companies:read | List and read the companies your app manages. |
companies:write | Create company pages and update their details. |
jobs:read | List and read jobs at those companies. |
jobs:write | Create, publish, update, close and archive jobs. |
candidates:read | List applicants and read one application in full. |
Give a key the narrowest set that works. A key that only publishes jobs does not need
candidates:read, and separating them means a leak of the publishing key exposes no
applicant data.
Company scopes and user scopes never mix. An API key cannot read anybody's CV through
/v1/me, and a user access token cannot post a job. Two different questions, two
different credentials.
What a key can see
A key reaches only the companies your app created. A company a Tabbio user made for themselves is invisible to you, even if the name matches, and even if that user is you.
Reading a company your app does not manage returns 403:
{
"data": null,
"error": {
"code": "COMPANY_NOT_MANAGED_BY_APP",
"message": "This company was not created by your app."
},
"meta": null
}Rotating
Old and new keys work at the same time, so rotation has no window where calls fail.
Create the replacement
Same scopes, a name that says when it was made.
Deploy it
Update the secret in your environment and roll out. Both keys are valid, so a partial rollout is fine.
Watch the old key
Its Last used timestamp in Settings stops moving once nothing is using it. That is your signal that the rollout finished.
Delete the old key
Revocation is immediate. Anything still holding it starts getting
API_KEY_INVALIDon the next call.
Rotate on a schedule, and immediately if a key has been in a log, a screenshot, a support ticket, or a repository.
Optional expiry
A key can carry an expiry date. It stops working at that moment and answers
API_KEY_INVALID like any revoked key. Useful for a key you hand to a contractor or an
integration you expect to end; a nuisance for production, where an unattended expiry is
an outage nobody scheduled.
Errors
| Code | HTTP | Meaning |
|---|---|---|
API_KEY_INVALID | 401 | The API key is missing, expired, or revoked. |
API_KEY_INSUFFICIENT_SCOPE | 403 | The key is valid but does not carry the scope this endpoint needs. |
COMPANY_NOT_MANAGED_BY_APP | 403 | The company, job or application belongs to a company your app did not create. |
PUBLIC_API_VALIDATION_ERROR | 400 | The body failed validation, or a job did not satisfy the publish contract. The message names the field. |
JOB_NOT_FOUND | 404 | No job with this id. |
APPLICATION_NOT_FOUND | 404 | No application with this id. |
CV_NOT_FOUND | 404 | The account has no CV to share yet. |
CV_LINK_NOT_FOUND | 404 | The durable CV link is unknown, was revoked, or the user disconnected the app. |
CONNECTION_NOT_FOUND | 404 | The connection behind this token is gone. |
DEVELOPER_PLATFORM_DISABLED | 404 | The developer platform is switched off for this deployment. |