DEJA developer portal
The public DEJA HTTP API: base URL, authentication, every endpoint, request and response shapes, limits, and the OpenAPI document agents can load directly.
Everything DEJA's own tracker, CLI and install agent do, they do over this API. There is no private surface they use and you cannot. The OpenAPI document at /openapi.json describes every operation below with typed parameters, response schemas and a unique operation id, so it can be loaded straight into a function-calling tool loop.
Base URL
https://deja.design/api/v1That base is the branded front door. It rewrites onto the deployment at https://clever-mole-378.convex.site, which installed trackers and installed CLIs address directly. Both hostnames serve the same API; new integrations should use the branded one.
Quickstart
No credential is needed to check that the API is up, or to read the install documents:
curl -s https://deja.design/api/v1/health
# {"ok":true,"service":"deja","version":"..."}
curl -s https://deja.design/api/v1/release | head
curl -s https://deja.design/api/v1/install.md | headTo do anything with a project you need an API key. Install the CLI, log in with the key from the dashboard, and list what the key can see:
mkdir -p ~/.deja/bin && \
curl -fsSL https://deja.design/api/v1/cli.mjs -o ~/.deja/bin/deja.mjs && \
chmod +x ~/.deja/bin/deja.mjs
node ~/.deja/bin/deja.mjs login --key deja_sk_...
node ~/.deja/bin/deja.mjs projects list --jsonAuthentication
Three credential kinds, deliberately unequal. Which one an endpoint takes is part of its contract — none of them is interchangeable with another.
| Credential | Sent as | Reaches |
|---|---|---|
deja_pk_… (project public key) | ?pk= query parameter | Write-only ingest. It is embedded in your page source and is not a secret. |
deja_sk_… (API key) | Authorization: Bearer | Every project in the organisation that issued it. Treat as a secret. |
deja_st_… (setup token) | Authorization: Bearer | One project, one field, and it expires. Issued for an install so the agent doing it can finish the job without holding a real key. |
Endpoints
Public — no credential
| Operation | Endpoint | Returns |
|---|---|---|
getHealth | GET /health | JSON liveness probe with the deployed tracker version. |
getRelease | GET /release | JSON release manifest: current CLI and skill versions, plus a sha256 per artifact. |
getTrackerBundle | GET /tracker.js | The recorder, as JavaScript. |
getCliBundle | GET /cli.mjs | The CLI, as a single ESM file. |
getInstallGuide | GET /install.md | The end-user install document, as Markdown. |
getAgentSkill | GET /skill.md | The agent install skill, as Markdown. |
Ingest — project public key
| Operation | Endpoint | Body |
|---|---|---|
ingestSessionChunk | POST /ingest?pk=… | An opaque recording chunk, gzip or JSON, up to 1,000,000 bytes. The chunk's metadata travels in the X-Deja-Meta header. |
ingestSignalsBatch | POST /signals?pk=… | JSON batch of page views, clicks and errors, up to 256,000 bytes. Malformed entries are dropped individually rather than failing the batch. |
Projects — API key
| Operation | Endpoint | Does |
|---|---|---|
getApiKeyIdentity | POST /cli/whoami | Reports the organisation, user and label behind the key. |
listProjects | POST /cli/projects.list | Lists projects, each with its public key and session count. |
createProject | POST /cli/projects.create | Creates a project and returns its public key. |
setProjectPathRules | POST /cli/projects.path-rules | Sets the URL patterns that decide how pages are grouped. |
countProjectSessions | POST /cli/sessions.count | Counts recorded sessions for one project. |
Install — setup token
| Operation | Endpoint | Does |
|---|---|---|
getSetupTokenProject | POST /setup/whoami | Reports the one project the token is scoped to. |
setSetupPathRules | POST /setup/path-rules | Sets that project's path rules. Path rules have to be right before the first session lands, so this exists to let the installing agent finish without a human opening Settings. |
Errors
Every error is JSON, never an HTML page. error is the human message and code is the stable identifier to branch on — never parse the message. Where there is something useful to do next, a hint says what.
{
"error": "invalid api key",
"code": "unauthorized",
"hint": "The key is unknown or revoked. Issue a new one in the dashboard.",
"documentation_url": "https://deja.design/developers"
}| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The body or parameters did not validate. |
| 401 | unauthorized | Missing, malformed, invalid or expired credential. |
| 404 | not_found | No such endpoint, project, or resource. |
| 406 | not_acceptable | No representation matches the Accept header. |
| 413 | payload_too_large | The body exceeded the endpoint's byte cap. |
Limits
- Recording chunks: 1,000,000 bytes per request.
- Signal batches: 256,000 bytes per request, and at most 200 views, 2,000 clicks and 100 errors.
- Ingest never returns a server error at the tracker. A payload it cannot use is dropped and answered
200— a recorder must never be able to break the page it is recording. - Every endpoint sends CORS headers and answers preflight.
Content negotiation
Every public page on this site — this one included — is also served as Markdown. Ask for it:
curl -H "Accept: text/markdown" https://deja.design/developersThose responses carry Vary: Accept, Accept-Encoding, so a cache in front of the site can never hand the HTML variant to a client that asked for Markdown.