Developers
Tokens and scopes
The REST API takes one credential: a workspace API token that holds scopes. Each scope opens part of the API, so a partner gets only what it needs.
Make a token
- A workspace owner or admin opens Settings, then Integrations, then API tokens for developers.
- They name the token, tick the scopes it needs, and optionally choose when it expires.
- The token is shown once. Production Engine keeps only a SHA-256 fingerprint of it, so copy it then. It starts with
pe_.
The same token also works with the MCP server. A token made with no scopes works only there: every REST route answers it with 403.
Send it
curl https://production-engine.com/api/v1/projects \
-H "Authorization: Bearer pe_EXAMPLE_replace_with_your_token"The token decides the workspace; nothing in the request can choose another one. An access token from connecting Claude or ChatGPT by sign-in is not a REST credential and answers 401.
Scopes
| Scope | Opens | Endpoints |
|---|---|---|
projects:read | Read projects and their external links. | GET /projects, GET /projects/{id}, GET /projects/{id}/links |
projects:write | Create and update projects. | POST /projects, PATCH /projects/{id} |
schedule:read | Read shoot days, with call sheet status. | GET /projects/{id}/shoot-days, GET /shoot-days/{id} |
deliverables:read | Read deliverables, their versions and client-visible review notes. | GET /projects/{id}/deliverables, GET /deliverables/{id}, GET /deliverables/{id}/versions, GET /versions/{id}/notes |
deliverables:write | Create and update deliverables, add link versions and review notes. | POST /projects/{id}/deliverables, PATCH /deliverables/{id}, POST /deliverables/{id}/versions, POST /versions/{id}/notes |
contacts:read | Read clients and client contacts, including contact email and phone. | GET /clients, GET /clients/{id}, GET /contacts, GET /contacts/{id} |
crew:read | Read project crew and shoot-day crew calls: name, role, booking status and call time only. | GET /shoot-days/{id} (adds crew calls), GET /projects/{id}/crew |
locations:read | Read a project's locations. | GET /projects/{id}/locations |
media:read | Read a project's media drives. | GET /projects/{id}/media-drives |
links:write | Create, update and delete external links. | POST /projects/{id}/links, PATCH /links/{id}, DELETE /links/{id} |
webhooks:manage | Manage webhook endpoints created by this token, for events whose records the token can read. | GET /webhook-endpoints, POST /webhook-endpoints, GET /webhook-endpoints/{id}, PATCH /webhook-endpoints/{id}, DELETE /webhook-endpoints/{id}, POST /webhook-endpoints/{id}/test |
GET /me needs a token holding at least one scope, whichever it is. A route that needs a scope the token lacks answers 403 with insufficient_scope, and error.param names the scope.
When a token stops working
A token answers 401 from the first request after any of these:
- An admin revokes it in Settings.
- Its expiry passes.
- The admin who made it is removed, suspended, or no longer an owner or admin. The token acts as them, and that is checked on every request.
If the workspace's plan is not active, every request answers 402 with plan_inactive until an owner chooses a plan. Existing tokens keep working once it is active again.
Rate limit
Each token may make 120 requests per 60 seconds. The next one answers 429 with rate_limited and a Retry-After header in seconds. Tokens are counted separately, and a refused request does not count.
Writes (POST, PATCH and DELETE) also count against a burst of 20 per 10 seconds, so a retry loop cannot flood a job with records. Reads are not in the burst.
Requests refused before the limit is checked (401, 402 and 403) do not count either.