API keys and authentication boundary
Automation keys use the nfy_ prefix and are intentionally recognized only by eligible build (/api/v1/builds/** and legacy /api/v1/compile/**), hosted-artifact, project, saved-processor, analytics, and self-disable operations. Source-input download remains JWT-only. Send the key only as Authorization: Bearer … to the HTTPS API origin. Do not put it in query strings, files, logs, artifacts, browser code, or workflow outputs, and do not follow a redirect that changes origin.
The eight independent scopes are build:read, build:write, artifact:read, project:read, project:write, processor:read, processor:write, and analytics:read. Write never implies read, and artifact:read grants completed output retrieval rather than source-input access. Listing project builds needs both project:read and build:read. Submitting a project build needs both project:write and build:write. A direct build or draft additionally needs project:write when it associates a project, including a stored draft project inherited at start, and any supplied processorId additionally needs processor:read.
Dashboard sessions use a different authentication boundary. Authentication, account, billing, administration, and API-key management operations remain JWT-only; the only key-management exception is that a key may disable itself. An automation key is not a substitute for a dashboard session. A human should create the scoped key and copy the one-time secret directly into the CI provider; do not paste it into chat.
Hosted artifact retrieval accepts a dashboard JWT or an automation key carrying artifact:read, then authorizes the exact task. A project task requires effective artifact:output:read; a legacy or ad-hoc task uses its exact-owner fallback. Anonymous requests, paths outside that resource boundary, and absent paths receive the same not-found result; knowing or guessing a hostingPath never grants access. Hosted responses use Cache-Control: private, no-store.