runlot
ReferenceAPI

Deployment access control

Covers 5 API operations in the access category.

MethodPathDescription
GET/v1/orgs/{orgSlug}/projects/{projectName}/accessDeployment access policy (runlot access)
PUT/v1/orgs/{orgSlug}/projects/{projectName}/accessChanges the policy (runlot access set)
POST/v1/orgs/{orgSlug}/projects/{projectName}/access/bypassIssues a bypass key for automation (runlot access bypass --new)
DELETE/v1/orgs/{orgSlug}/projects/{projectName}/access/bypassRevoke a bypass key (runlot access bypass --revoke)
POST/v1/access/authorizeOne-time code to reach a protected deployment (docs/access.md §3.4-3)

GET /v1/orgs/{orgSlug}/projects/{projectName}/access

Who can reach this deployment (docs/access.md §3.1). A project that has never set a policy is public, not 404 — most projects are in that state.

There is no value. The password never appears on any surface, and a bypass secret is shown only once, at creation time.

viewer and above — whether this deployment is open is a fact anyone in the org needs to know.

operationId getAccess

Status codeDescriptionResponse body
200PolicyAccessPolicy
403
404
503no_core — no connection to cp-coreError

PUT /v1/orgs/{orgSlug}/projects/{projectName}/access

public keeps the current behavior, org means member or higher in the org the project belongs to, and password means one shared password per project — the moment it becomes per-user, that is a subscriber table's problem and out of scope for this API (docs/access.md §3.1).

If you don't send a password, the existing one stays. If a save that only changes the mode also clears the password, you would have to set it again when switching back, and that inconvenience buys no safety. Send a new value if you want to clear it.

Requires admin or higher. If a member could change the boundary, it would not be a boundary. Audited as access.set (the password itself is not included in the audit).

operationId setAccess

Request body: application/json · object

Status codeDescriptionResponse body
200The policy that changedAccessPolicy
400
403
404
503no_coreError

POST /v1/orgs/{orgSlug}/projects/{projectName}/access/bypass

This is the key CI uses to access a protected deployment (docs/access.md §3.6). Send it in the Runlot-Access-Bypass: <secret> request header.

The value appears only in this response. You cannot view it again, and calling this again immediately invalidates the old value — this is the only way to revoke a leaked secret.

Without this key, CI cannot access protected deployments, which may force you to turn off protection. Requires admin or higher. The audit event access.bypass.new does not record the key value.

operationId newAccessBypass

Status codeDescriptionResponse body
200Bypass key (shown only once)AccessBypass
403
404
503no_coreError

DELETE /v1/orgs/{orgSlug}/projects/{projectName}/access/bypass

Returns 204 even if there is no bypass key. The access policy itself does not change. The bypass key is managed separately from the policy. Admin or above can use this. Audited as access.bypass.revoke.

operationId revokeAccessBypass

Status codeDescriptionResponse body
204detached
403
404
503no_coreError

POST /v1/access/authorize

This sits in the middle of the login round trip. When a request to a protected deployment lands back on the dashboard's /access/authorize screen, that screen makes this call with the session and moves to the received redirect. The front end that receives that callback is what sets the cookie — the fact that it cannot be set from a different origin shaped this whole flow.

to must be a hostname we know. If it isn't, this returns 404: without this check, this endpoint would be an open redirect that sends a session-holding user to any domain, and that domain would receive the code. next also only accepts a path (//evil.example is read as an absolute address).

Someone outside the org gets 403 — even re-logging in gives the same result (§3.7). The point is viewer or above, not member or above: viewing a deployment is exactly a viewer's job.

Section §3.4-2 of the docs describes this flow as the browser's GET /access/authorize. Because the dashboard stores the session token in localStorage, the server cannot see the session on that GET. So only who does the moving switched from server to browser, and the verdict stays the same.

operationId authorizeAccess

Request body: application/json · object

Status codeDescriptionResponse body
200Callback URLAccessAuthorize
400
403
404
503no_coreError

On this page