Creating a token
- Sign in to the app and open AI and Automation → API Tokens.
- Press Create token. Give it a name that says what will use it, for example Claude lead-qualifier or Zapier import.
- For each resource choose None, Read or Read & write. The All resources row is a shortcut that overrides the individual rows.
- Pick an expiry: never, 30 days, 90 days or 1 year.
- Press Create token. The full token is shown once. Copy it now — for security the app never displays it again. Copy JSON gives a ready-made block for an AI agent:
{
"apiToken": "djc_2hYM7Iix…",
"baseUrl": "https://api2.djcsystem.com/v1",
"scopes": ["leads:write", "ai-flows:write", "appointments:read"],
"firstCall": "GET /me",
"catalog": "GET /resources"
}Lost the token?
It cannot be recovered. Revoke it on the token page and create a new one.
Sending it
Send the token on every request. Tokens always start with djc_. There are no unauthenticated routes apart from /health.
# Either form works
curl https://api2.djcsystem.com/v1/me -H "Authorization: Bearer djc_2hYM7Iix…"
curl https://api2.djcsystem.com/v1/me -H "X-API-Key: djc_2hYM7Iix…"Scopes: read vs read & write
Internally a scope is written <resource>:<level>. What you tick on the token page becomes that list, and GET /me reports it back.
| Level | What it allows |
|---|---|
Read x:read | Every GET: list, read one, list children. Nothing is created, changed or deleted. |
Read & write x:write | Everything read allows, plus POST, PATCH and DELETE. Write includes read. |
All resources *:read / *:write | The same level on every resource, including ones added after the token was created. |
Resources that can appear in a scope:
leadschatroomsai-flowsknowledge-recordscatalogsworkflowsappointmentsevent-typesblast-campaignsscheduled-tasksforms
A call to a resource the token was not given at all returns the same 403 as an insufficient level.
Whose data a token sees
A token acts as the user who created it. Every request is limited to that user's workspace on the server — the API adds the restriction itself, and there is no parameter that widens it. Records that belong to another workspace are indistinguishable from records that do not exist (404).
Some resources are shared across a workspace's team (chatrooms, appointments, AI flows, event types, scheduled messages); others are per user, matching what that user sees in the app (leads, knowledge, catalogs, workflows, campaigns, forms). Anything the token creates is recorded as created by its owner, so the audit trail stays truthful.
If the user is removed from the workspace, or the workspace changes, the token stops working immediately.
Expiry, revocation, renaming
- Expiry is optional. An expired token answers
401and shows as Expired on the token page. - Revoke from the token page; the token stops working on its next request. Revoking cannot be undone.
- Rename freely; the name is for you.
- Up to 25 active tokens per user. The page shows when each token was last used, which helps find the ones to retire.
What a rejected call looks like
HTTP/1.1 403 Forbidden
Content-Type: application/json
{
"success": false,
"error": "This token lacks the leads:write scope.",
"code": "INSUFFICIENT_SCOPE",
"details": { "required": "leads:write", "scopes": ["leads:read", "appointments:read"] }
}details.required names the scope the call needs — useful for agents, which can tell the user exactly which box to tick.
HTTP/1.1 401 Unauthorized
{ "success": false, "error": "This token has been revoked.", "code": "UNAUTHORIZED" }Hygiene
- One token per integration, named after it, so revoking one thing never breaks another.
- Read only unless the integration genuinely writes. Add write later if it turns out to be needed.
- Set an expiry for anything experimental; revoke on the day a project ends.
- Keep tokens in environment variables or a secrets manager — never in a repository, a spreadsheet or a chat message.