Skip to content

Roles, permissions & API keys

Every request to justcrawl carries an org context and a permission set. Routes check requirePermission(...) before they run — if your token’s permissions don’t include what the route needs, you get 403 Insufficient permissions with the missing permission name in the body. This page covers how to read that gate, the four built-in roles, how to build a custom role, how to invite teammates, and when to use an API key vs a session JWT.

Every protected action maps to one of these strings. They’re stored verbatim in the roles.permissions array and checked by the gateway.

Permission What it gates
workflows:read List workflows, fetch one by ID, see the DAG.
workflows:write Create or edit a workflow.
workflows:publish Mint a new published version that schedules and live traffic start using.
workflows:delete Soft-delete a workflow.
urls:read List URLs in the library, fetch one.
urls:write Upload a CSV, add a URL, edit tags.
urls:delete Remove a URL from the library.
jobs:read List jobs, fetch a job + its result.
jobs:submit POST /api/v1/jobs — scrape a URL. The credit-charging permission: a credit is taken at submit and returned if the crawl fails at every provider.
schedules:read List schedules, fetch one.
schedules:write Create or edit a schedule.
schedules:delete Remove a schedule.
bi:read Discover the BI schema and inspect query, result, saved-query, schedule, and export state.
bi:write Submit or cancel BI queries and create, edit, or delete saved queries, schedules, and exports.
org:manage The “admin” permission. Edit org settings, manage members, create custom roles, rotate API keys.

These are the only 15 strings the gateway will recognize. Adding an unknown permission to a role is silently accepted on write but is never granted on a check.

Every org is seeded with four system roles at creation time. They cannot be edited or deleted.

Role Permissions When to use
Owner All 15 The org creator. Identical to Admin in permissions, but protected against demotion when sole owner.
Admin All 15 Trusted operators who manage settings, members, and roles.
Editor All 15 except org:manage Builders. Can create workflows, schedules, jobs, URLs, and BI queries but cannot manage members or rotate keys.
Viewer workflows:read, urls:read, jobs:read, schedules:read, bi:read Read-only. Can inspect BI state but cannot submit queries or change BI resources.

Owner and Admin have identical permissions today — the difference is the sole-owner guard: the gateway refuses to demote or remove the last Owner of an org, so you can’t accidentally lock everyone out.

If the four system roles don’t fit (e.g., “build workflows but never publish them to live traffic”), build your own. Requires org:manage.

Terminal window
curl -X POST https://api.justcrawl.io/api/v1/roles \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Draft Builder",
"permissions": [
"workflows:read", "workflows:write",
"urls:read", "urls:write",
"jobs:read", "schedules:read"
]
}'

Same surface available in Settings → Roles in the dashboard.

Rules:

  • Existing custom roles are not changed when new permissions are introduced. Add bi:read and/or bi:write explicitly where those roles need BI access.
  • The role name is unique per org. Reusing a name returns 409 A role with that name already exists.
  • permissions must be a non-empty array. The gateway does not validate that each string is a known permission — unknowns are stored but never granted.
  • PUT /api/v1/roles/{id} edits the name or permissions of a custom role. System roles cannot be edited — PUT on Owner / Admin / Editor / Viewer returns 404 Role not found or is a system role.
  • DELETE /api/v1/roles/{id} removes a custom role. The endpoint refuses with 409 if any member is currently assigned that role — reassign them first.

Adding a teammate to an org takes a single POST. Requires org:manage.

Terminal window
curl -X POST https://api.justcrawl.io/api/v1/orgs/current/members \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "teammate@example.com",
"roleId": "role-uuid-from-listing"
}'

The inviting admin must also have a verified email — the endpoint returns 403 Please verify your email before inviting team members otherwise. Check Settings → Account for the verification link.

To change a member’s role: PATCH /api/v1/orgs/current/members/{userId}/role with { "roleId": "..." }. The gateway blocks demoting the sole Owner — transfer ownership first by promoting another member, then demote the original.

To remove a member: DELETE /api/v1/orgs/current/members/{userId}. Same sole-Owner guard applies.

The same requirePermission gate runs for both auth modes. They differ in how the token is minted, how long it lives, and what carries the permission set.

API key (sr_live_…) Session JWT
Created by POST /api/v1/api-keys (any authenticated user in the org) Issued automatically on login / sign-up
Format sr_live_<32 hex chars> (40 chars total) Standard JWT — eyJ...
Lifespan No expiry by default. Optional expiresAt on create. Revoked via DELETE /api/v1/api-keys/{id}. 15-minute access token + 7-day refresh token
Permissions source The role the creating user holds right now, looked up live on every request via (api_key → org_membership → role). Demote the user and the key’s powers shrink on the very next request. The role the user holds right now, baked into the access token each time it’s minted
Returned plaintext Once, in the POST response body — never retrievable again. Lose it, rotate it. Held by the browser session; not directly accessible
Where to use CLI, scripts, CI/CD, MCP server, anything non-browser The dashboard SPA
Impersonation marker Never Admin impersonation JWTs carry impersonator_admin_id for audit
  • Building a scraping pipeline / CI job / agent integration → API key. Set it as JUSTCRAWL_API_KEY in the environment, pass it as Authorization: Bearer sr_live_….
  • Logged-in user clicking around the dashboard → session JWT. Handled for you automatically.
  • Building a server-side integration that acts on behalf of multiple orgs → one API key per org. Each key is org-scoped; there’s no super-key.
Terminal window
curl -X POST https://api.justcrawl.io/api/v1/api-keys \
-H "Authorization: Bearer YOUR_SESSION_JWT" \
-H "Content-Type: application/json" \
-d '{
"name": "CI bot — main pipeline",
"expiresAt": "2027-01-01T00:00:00Z"
}'

Response includes the full key exactly once:

{
"id": "ak_...",
"name": "CI bot — main pipeline",
"keyPrefix": "sr_live_abcd",
"expiresAt": "2027-01-01T00:00:00Z",
"key": "sr_live_abcd1234ef567890..."
}

Stash it somewhere safe (secrets manager, env var). Subsequent GET /api/v1/api-keys returns the prefix only — there is no way to recover the full key.

Create a new key, deploy it to your consumers, then DELETE the old one. Old key stops authenticating immediately on delete.

Most permission-gated routes emit:

{
"error": "Insufficient permissions",
"missing": ["workflows:publish"]
}

The missing array lists every required permission the token does not have. Use it to figure out which permission to add (or which role to switch to) — no guessing.

BI routes preserve missing but use their published structured error envelope:

{
"error": {
"code": "insufficient_permissions",
"message": "Insufficient permissions",
"docs_url": "https://docs.justcrawl.io/guides/errors/bi",
"request_id": "req_01HF3JKB9Q2W4X"
},
"missing": ["bi:write"]
}

If the token has no org context (e.g., the user hasn’t completed onboarding), the gateway returns 403 No organization. Complete onboarding first. instead.

  • API Quickstart — get a sr_live_… key and make your first authenticated request
  • Rate limits & quotas — what credit-consuming routes do when you’re out of quota
  • Job lifecycle — jobs:submit is the permission that fires the lifecycle
  • Integrations — webhook / S3 output configs require org:manage