Authentication and API keys
Create scoped v2 API keys, and learn how the role ceiling, expiry, rotation and revocation work.
Every v2 request needs an API key in the Authorization header, as a bearer token. v2 keys start with vt_live_:
curl "https://api.vacationtracker.io/v2/leaves?startDate=2026-10-01&endDate=2026-10-31" \
-H "Authorization: Bearer vt_live_..."A missing or unknown key returns 401 UNAUTHORIZED. A revoked or expired key returns 403 KEY_REVOKED or 403 KEY_EXPIRED. See Errors.
v1 keys (sent in x-api-key) do not work on v2. Create a v2 key instead. See Migrating from v1.
Creating a key
In the dashboard, go to Add-ons → Open API and create a key. Only administrators can create keys today.
- Key name. Up to 64 letters, numbers, spaces,
_,.or-, unique in your workspace. Only administrators see it, in the list of keys and in company logs. - Integration name (optional). Same characters, up to 64. Everyone sees this name: a change made through the key is shown as "Name (through HR_BOT)" in the leave timeline, notifications and company logs, so choose one your colleagues recognize. Without it, changes show only the person's name, and company logs still show the key and its owner.
- What you plan to build (optional). A short description that helps us improve the API.
- Permissions. Start from a template (Read only, Leave management, Workspace setup, or Custom), then set each capability to No access, Read or Read & write. Read & write includes read, and also allows deleting.
- Sensitive actions. Approving and denying requests, and acting on behalf of other users, are separate checkboxes. Turn them on only when the integration needs them.
- Expiration. See Expiration.
The secret is shown once, right after you create the key. Store it in a secret manager. Vacation Tracker keeps only a hash, so a lost secret cannot be recovered: rotate the key to get a new one.
You can change the name, permissions and expiration of an active key later, within your role ceiling.
Scopes
Each capability has a <capability>:read scope and a <capability>:write scope. Write implies read.
| Capability | Scopes | Covers |
|---|---|---|
leaves | leaves:read, leaves:write | Leave requests |
toil | toil:read, toil:write | Time off in lieu requests |
balances | balances:read | Leave balances (read only) |
users | users:read, users:write | Users and their settings |
locations | locations:read, locations:write | Locations and work weeks |
departments | departments:read, departments:write | Departments |
leave-types | leave-types:read, leave-types:write | Leave types |
leave-policies | leave-policies:read, leave-policies:write | Leave policies per location |
holidays | holidays:read, holidays:write | Public holidays per location |
labels | labels:read, labels:write | Labels |
automations | automations:read, automations:write | Automations |
notifications | notifications:read, notifications:write | Notifications and scheduled reports |
webhooks | webhooks:read, webhooks:write | Webhooks |
Three special scopes cover the sensitive actions. No :write scope implies them:
| Scope | Grants | Also implies |
|---|---|---|
leaves:approve | Approve and deny leave and TOIL requests. | leaves:read, toil:read |
users:act-on-behalf | Send requests as another user with X-On-Behalf-Of. See Acting on behalf of users. | users:read |
leave-types:read-hidden | See the names of hidden leave types. Without it, a leave type hidden by its leave policy shows as "Hidden Leave Type", like in the dashboard: in the embedded leaveType.name of leaves (with expand=leaveType) and in the leaveTypeName of balances and quotas. leaveTypeId and every amount are kept. | leave-types:read |
A request that needs a scope the key does not have returns 403 INSUFFICIENT_SCOPE, with the missing scopes in details.requiredScopes.
Role ceiling and data reach
A key never has more access than the person who created it. Scopes above the creator's role cannot be selected, and the key never sees more data than its owner can see.
| Owner role | Highest scopes | Data reach |
|---|---|---|
| Administrator | Every scope | The whole workspace |
| Location admin | Approver scopes, plus users:write, locations:read/write, departments:read/write, leave-policies:read/write, holidays:write, notifications:read/write | Users in the locations they manage |
| Approver | User scopes, plus leaves:approve, users:act-on-behalf, leave-types:read-hidden | Users in the departments they approve |
| User | leaves:read/write, toil:read/write, balances:read, users:read, holidays:read, leave-types:read | Only themselves |
Substitute approver assignments do not widen a key. Resources outside the data reach behave as if they do not exist and return 404 RESOURCE_NOT_FOUND.
The data reach depends only on the owner's role, so a key can see less than its owner sees in Vacation Tracker. For example, a user's key reads only their own leaves, even though the user sees colleagues' approved leaves in the calendar. The Visibility add-on does not change the data reach: it is already limited to what the add-on allows.
Expiration
| Option | Key stops working |
|---|---|
| 30 days | 30 days after creation |
| 90 days | 90 days after creation |
| 1 year (default) | 1 year after creation |
| Never | Only when revoked |
An expired key returns KEY_EXPIRED. Create a new key to continue. Choose Never only for integrations you monitor closely.
Rotation
Rotating a key creates a new secret, shown once, and keeps the same key, name and permissions. Choose what happens to the previous secret:
| Option | Previous secret |
|---|---|
| 24 hours (recommended) | Keeps working for 24 hours, so you can deploy the new secret without downtime |
| 7 days | Keeps working for 7 days |
| Stop now | Stops working immediately. Use this when a secret has leaked |
Only one previous secret works at a time: rotating again ends any overlap still running. Revoked keys cannot be rotated.
Revocation
A revoked key stops working within a minute. Keys are revoked in two ways:
- Manually, by an administrator in the dashboard, with an optional note.
- Automatically, when the owner loses privileges the key relies on: they are no longer an administrator, they manage fewer locations or approve fewer departments, or they are deactivated or deleted. The owner's webhooks are revoked in the same way.
A request with a revoked key returns KEY_REVOKED. details.revocationCode says why the key was revoked and details.revocationReason explains it in words:
{
"error": {
"code": "KEY_REVOKED",
"name": "KeyRevoked",
"description": "The API key has been revoked.",
"resolution": "See details.revocationReason for why it was revoked, then ask a Vacation Tracker admin to create a new key.",
"requestId": "...",
"details": { "revocationCode": "...", "revocationReason": "..." }
}
}The same reason is shown on the key in the dashboard. Revoked keys stay in the key list, so administrators can see what happened. Related codes are KEY_OWNER_INACTIVE (the owner was deactivated or deleted) and KEY_SCOPES_EXCEED_ROLE (the owner's role no longer allows the key's scopes).