Personal tokens
For Developers

Personal tokens

A personal token opens the dashboard API to a script on your behalf: with your role in each project, in the areas and projects you pick, from allowed addresses.

A personal token is your key to the dashboard API, the same API the interface runs on. A script with the token acts on your behalf: it reads and changes what you see and change in the dashboard, in all your projects. A token never adds to your rights, it only narrows them: your role in each project is checked as in the dashboard, and on top of it come the token’s areas, projects, expiry and allowed addresses.

Project key or personal token

WhatProject keyPersonal token
Formatsk_…shp_…
Belongs toThe projectYou
OpensThe public API /api/v1The dashboard API /api/…
ProjectsOne, its ownAll of yours, or the ones you pick
RightsThe project’s whole public APIYour role, narrowed by areas
Who issues itThe project owner and adminsAny member, for themselves
WhereSettings → API KeysProfile → Personal API
Expiry and addressesNone30, 90 or 365 days or no expiry, up to 20 addresses

The public API, /api/v1, takes only a project key, and the dashboard API takes only a sign-in: a session or a personal token. Connect the project’s integrations (a CRM, a bot, a channel of your own) with a key: it doesn’t depend on people and their roles. A personal token is for scripts that do what you do in the dashboard: exports, reports, bulk edits, work across several projects at once.

Issuing a token

  1. Open Profile → Personal API and click Issue token. Every project member has this section: you issue tokens for yourself.
  2. Fill in the form:
    • Name: 1 to 100 characters, to tell the token apart in the list and in the email.
    • Valid for: 30 days, 90 days, 1 year or no expiry. 90 days by default.
    • Access: Full access (read and write in every project area), Read only, or Pick by area.
    • Projects: All my projects, Selected, or, for superusers only, All projects on the platform.
    • Allowed addresses: optional, one per line.
    • Current password: a token works without a 2FA code, so issuing one takes your password.
  3. Click Issue and copy the token right away. It’s shown in full only once: only its hash is stored, and the list shows its start, the first 8 characters (shp_ and four more).
  • For every new token you get an email, “Personal API token created”, with the name, areas, projects, expiry, address and time of issue. The token itself isn’t in it. If you didn’t issue the token, revoke it and change your password. Changing the password in your profile doesn’t end tokens by itself.
  • One person can have at most 25 tokens, expired ones included. Past that, issuing answers 400 PERSONAL_TOKEN_LIMIT until you revoke one you don’t need.
  • A wrong password counts as a failed sign-in. After 10 failures in a row, each within 15 minutes of the one before, signing in and issuing tokens are closed until 15 minutes have passed since the last failure (429 AUTH_LOGIN_LOCKED), even with the right password.
  • Signing in with SSO and don’t know your password? Set one via “Forgot password”.
  • Tokens are issued and revoked only in the dashboard: a token can’t issue or revoke tokens.
  • In someone else’s account, entered by a platform admin from the admin area, issuing is closed. Viewing and revoking tokens works there.
  • Enterprise Dedicated with the license in read-only mode: issuing is closed, revoking works. Tokens still read with GET and HEAD, while any POST, PUT, PATCH or DELETE answers 423.

First request

Pass the token in the Authorization header with the Bearer scheme:

Authorization: Bearer shp_xxxxxxxxxxxxxxxxxxxxxxxx

The dashboard API’s paths start with /api. Almost everything about a project lives under /api/w/{workspace_id}/…, where workspace_id is the project’s id. The list of your projects gives it: GET /api/workspaces/, with the trailing slash. It needs the Projects area with read. Without it the answer is 403 PERSONAL_TOKEN_SCOPE with {"area": "projects", "need": "read"} in detail:

bash
curl https://supporthub.cc/api/workspaces/ \
  -H "Authorization: Bearer shp_xxxxxxxxxxxx"
200 OKjson
{
  "items": [
    { "id": "6f1c2a7e-…", "name": "Altair Cloud", "slug": "altair", "my_role": "admin", … }
  ],
  "total": 1,
  "page": 1,
  "page_size": 20,
  "pages": 1
}

The list is paged: page and page_size (up to 100). A token limited to selected projects sees only those, and their ids are also in the answer of GET /api/personal-tokens/current, with no Projects area needed.

Then call the project’s routes, for example the list of tickets (the Tickets area with read):

bash
curl https://supporthub.cc/api/w/<project_id>/tickets \
  -H "Authorization: Bearer shp_xxxxxxxxxxxx"

The answer is a page of tickets in the same shape: items, total, page, page_size, pages.

Three requests are open to any working token, with no areas:

  • GET /api/personal-tokens/current: the token itself, with its name, areas, projects, addresses, expiry, and when and where it was last used
  • GET /api/auth/me: whose token it is
  • GET /api/openapi.json?scope=personal: the endpoints the token opens (add lang=en for English)

A token that won’t do gets its own error from all three, in the common envelope: PERSONAL_TOKEN_INVALID for no such token or a revoked one, PERSONAL_TOKEN_EXPIRED for a term that’s over, PERSONAL_TOKEN_IP for an address that isn’t allowed.

GET /api/personal-tokens/currentjson
{
  "id": "5d1e0b44-…",
  "name": "CRM export",
  "token_prefix": "shp_Ab3x",
  "scopes": { "tickets": "read", "contacts": "read" },
  "projects_kind": "listed",
  "workspace_ids": ["6f1c2a7e-…"],
  "allowed_ips": ["203.0.113.0/24"],
  "expires_at": "2027-02-04T09:30:00",
  "last_used_at": "2026-11-06T09:41:12",
  "last_used_ip": "203.0.113.7",
  "created_at": "2026-11-06T09:30:00"
}

Times in answers are UTC. The same reference, by area, with models and request examples, is in the API docs tab of Profile → Personal API.

Areas

An area is a part of the dashboard API, and its level is read or write. Write includes read. Reads are GET and HEAD requests, writes are the other methods. Three POST requests that change nothing count as reads: the project log export to CSV, the preview of a project email and the preview of a platform broadcast. Assistant suggestions and message translation, though, are writes: each call spends the project’s budget.

AreaWhat it opens
tickets
Tickets
Tickets and messages, notes, assignment, inbox folders and views, reactions, conversation links and PDF statements, assistant suggestions in a ticket, BILLmanager data in a ticket, live visitors, bell notifications
contacts
Contacts
Contacts, a contact’s emails, the client card
knowledge
Knowledge base
Articles and categories, news and polls
templates
Reply templates
Templates, macros, quick replies, your own greeting and signature
channels
Channels
Channels, proxies, the widget and its builder, visitor notifications
broadcasts
Broadcasts
Broadcasts to contacts, sending news to every contact
team
Team
Operators, departments, project invitations
ai
AI assistant
Assistant settings, memory, connections, sources, the reply journal
analytics
Analytics
Reports, the project log, exports of tickets, contacts and conversations
settings
Project settings
Project settings and texts, project emails and their look, file storage
billing
Plan and billing
The project’s plan and its renewal, plan changes, promo codes, balance top-ups, payment history
projects
Projects
The list of your projects, a project by slug, creating a project, the order of projects in the list
media
Files
Uploading and downloading files
profile
Profile
Your status, your departments and email for notifications, your work hours
admin
Platform admin
The platform’s users and projects, moderation, payments, revenue and plans read-only, expense records, platform broadcasts and announcements, the changelog, proxies, platform health. Platform admins only

The Platform admin area goes only to platform admins, and never to a token with selected projects: the admin area is one for the whole platform. A superuser gets it only together with All projects on the platform.

Projects

  • All my projects: the projects you’re a member of, including ones you join later. Leave a project and the token stops working there.
  • Selected: only the projects you tick, 1 to 1000 of them, and only ones you’re a member of. A request to another project answers 403 PERSONAL_TOKEN_PROJECT.
  • All projects on the platform: superusers only. Any project on the platform, as they have it in the dashboard.

A token doesn’t change your role in a project: an operator’s token with Full access still can’t change the project settings, just as in the dashboard. A superuser keeps superuser rights only with an All projects on the platform token. With All my projects or Selected, a superuser is an ordinary member of their projects for the request, with no access to other projects or to the admin area.

Routes with no project in the path, for example GET /api/tickets/{ticket_id}/locate or POST /api/announcements/{ann_id}/ack, take the project of the object they find and check it the same way.

Expiry and addresses

A token works until its term is over: 30, 90 or 365 days from issue, or no expiry. A term can’t be extended: issue a new token. An expired token answers 401 PERSONAL_TOKEN_EXPIRED and stays in the list, marked Expired, until you revoke it. Until then it counts toward the 25.

Allowed addresses are optional: an empty list means from anywhere. The list takes up to 20 IP addresses or networks, IPv4 and IPv6, for example 203.0.113.7, 203.0.113.0/24, 2001:db8::/32. A network is stored by its network address: 203.0.113.9/24 becomes 203.0.113.0/24. An address with the ::ffff: prefix is refused: write the IPv4 address as is. The address checked is the one the request reached us from: behind NAT or a proxy, their outside address. A request from any other address answers 403 PERSONAL_TOKEN_IP.

The list of tokens shows when and from which address each token was last used. The mark may lag by up to a minute.

Reading leaves no trace

A script reading conversations isn’t a person who read them. So requests with a token:

  • don’t tell the client their message was read
  • don’t mark the ticket as read for operators
  • don’t count a knowledge base article view
  • don’t start the background translation of older messages
  • don’t show you as online to widget visitors

Writing with a token is ordinary writing. A reply goes to the client in their channel on your behalf, with every consequence of a reply from the dashboard. An explicit translation request translates the message, as the button in the dashboard does.

A token doesn’t open the operator WebSocket. For real-time events, use webhooks or long polling of the public API, with a project key.

Files

Clients’ files, meaning anything not attached to an internal note, are served by GET /api/media/{media_id} with no sign-in, as in the widget and in email. A file of an internal note goes only to a project member, and with a token only if the token also has the Files area with read.

Such a file never confirms it exists. Any refusal of the token answers 404 MEDIA_NOT_FOUND, as a missing file does: the term is over, the token is revoked, another address, no Files area, or a project outside the token’s list on the /api/w/{workspace_id}/media/… path. There is one exception: on GET /api/media/{media_id} a project member whose token leaves that project out gets 403 PERSONAL_TOKEN_PROJECT. Got a 404 for an internal file? Check the token first with GET /api/personal-tokens/current.

Signed file links in message lists, like …?exp=…&sig=…, open without a token. Treat them as secrets: anyone who gets such a link can open the file for 12 hours.

Always closed

Part of the API is closed to every token, whatever its areas. A token can’t create a new sign-in or keys, take secrets out, change sign-in data, its own or anyone else’s, grant platform roles or project ownership, handle the platform’s money, plans and licenses, or delete a project or an account. Closed are:

  • token management: everything under /api/personal-tokens, except GET /api/personal-tokens/current
  • sign-in, password, sessions, 2FA, passkeys and SSO: everything under /api/auth, except GET /api/auth/me, and accepting an invitation
  • project keys sk_…
  • secrets that would outlive the token: the signing secret for widget visitor tokens, a channel’s secret inbound email address, the BILLmanager module setup, the WhatsApp linking QR code, the project’s SSO
  • a new operator with a password, handing over project ownership, deleting a project
  • actions on clients’ BILLmanager services from a ticket, running macros, client card buttons
  • the list of payment methods and payment provider webhooks, the license, device push subscriptions, an install’s first-run setup
  • in the platform admin area: signing in as another user, editing and deleting users, their passwords, 2FA and passkeys, their roles in projects, a user’s emails in full, writing platform settings and system emails, support sessions, marking payments paid, a project’s balance and plan, editing plans, discounts and promo codes, issuing and renewing licenses, moving Telegram bots to another Bot API (it would hand every bot token on the platform to the address in the request)
  • in Enterprise Dedicated: support access, starting an update, the install’s domain and SMTP

The answer is 403 PERSONAL_TOKEN_CLOSED with closed in detail.reason. If it says unmapped, the route is new and isn’t in the token map yet: until it is, it stays closed to tokens.

Strong actions still sit in the areas, open to a token with write there. Team changes members’ admin rights, deactivates them and removes them from the project. Plan and billing, for the project owner, renews and changes the plan from the project balance and issues invoices. Platform admin blocks people, removes them from projects and ends their sessions. Give write in these areas only to scripts that need it.

Errors

Token errors come in the common envelope: error_code, error_message, detail, request_id. The format and retries are on the Limits & errors page.

ResponseWhen
400
PERSONAL_TOKEN_LIMIT
When issuing in the dashboard: you already have 25 tokens. detail: {"max": 25}
401
PERSONAL_TOKEN_INVALID
No such token: a typo, a revoked token or a deleted account
401
PERSONAL_TOKEN_EXPIRED
The token’s term is over
403
PERSONAL_TOKEN_IP
The request came from an address that isn’t allowed
403
PERSONAL_TOKEN_CLOSED
The route is closed to tokens. detail.reason: closed means always closed, unmapped means the route isn’t in the token map yet
403
PERSONAL_TOKEN_SCOPE
The token lacks the route’s area or the level it needs. detail: {"area": "tickets", "need": "write"}
403
PERSONAL_TOKEN_PROJECT
The request’s project isn’t one of the token’s projects
404
PERSONAL_TOKEN_NOT_FOUND
In the dashboard: revoking a token that’s already gone. Also GET /api/personal-tokens/current with a plain session, no token
404
MEDIA_NOT_FOUND
A file of an internal note, and the token won’t do for it (see Files)
429
RATE_LIMIT_EXCEEDED
Too many requests. The Retry-After header says in how many seconds to retry

The checks go in order: the token, its expiry, the address, the route, the area, the project. Then the route checks you yourself, as in the dashboard. A blocked account answers 403 with {"detail": "User account has been banned."}, and missing rights in a project give the same 403 and 404 as in the interface. With no Authorization header the answer is 401 with {"detail": "Could not validate credentials."}.

Rate limit

Each token has its own budget: 1,000 requests per minute across all routes. An API process remembers a token for 5 minutes after its last accepted request. The first request in each process, and the first one after such a pause, count toward the client address’s budget (also 1,000 per minute), like a request without a token. Over the limit you get 429 RATE_LIMIT_EXCEEDED with a Retry-After header.

Some routes have limits of their own, stricter than the common one, for example 20 to 30 requests per minute for assistant suggestions. They count by the client address, whatever header the request carries.

What ends a token

Revoke tokens in Profile → Personal API, one by one or all at once with Revoke all. A revoked token stops working at once and answers 401 PERSONAL_TOKEN_INVALID. It can’t be brought back, only replaced with a new one.

All your tokens are revoked by themselves when:

  • your password is reset with the link from the email
  • a platform admin clicks End all sessions, Set password or Reset link in your user card
  • the account is deleted

Changing your password in the profile and End others in your sessions leave tokens working: tokens live apart from sessions. Blocking the account stops its tokens (403), and after unblocking they work again. A platform admin sees your tokens in your user card, only their start, and can revoke any of them.

Was this page helpful?