Finish making the author GitHub App with what GitHub returned
The console's callback page sends the code and state from GitHub's redirect. The state is spent first: unknown, spent, expired, or issued to another caller or workspace answers 400 BAD_REQUEST state_invalid. The code is exchanged once with POST /app-manifests/{code}/conversions (400 code_rejected when GitHub refuses it, 503 github_unreachable when it cannot be reached; never retried). The App is stored as another author connection, with its private key, in one transaction with a set_github_app receipt (source: manifest); its OAuth client secret is kept only when it becomes the connection people link their accounts with (the workspace had no author connection), and the webhook secret is not kept. If storing fails, 503 store_failed, naming the App made on GitHub in details.app_id and details.html_url. No key, secret or code is in any response, receipt or log, and every answer carries Referrer-Policy: no-referrer. API keys are refused.
Auth: cookie session only; no scope. Idempotency-Key: ignored.
cookieSession__Host-werft_session<token>For the web console: the browser session cookie set by POST /v1/auth/login. It holds the scopes of the user's workspace role (owner: all; admin and member: all but sessions:recover). Requests other than GET must also send X-Requested-With: werft-web.
id*stringThe workspace's id.
^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidX-Requested-With?"werft-web"Required with a cookie session: the literal werft-web.
"werft-web"application/json- body
code*string^[A-Za-z0-9_-]{1,512}$state*string^[A-Za-z0-9_-]{1,512}$Stored; the App, where it is installed next, and the receipt.
application/json- response
github_app*install_url*stringurireceipt_id*string^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$uuidcurl -X POST 'https://example.com/v1/workspaces/497f6eca-6276-4993-bfeb-53cbbbba6f08/github-apps/author/manifest/complete' \ -H 'Content-Type: application/json' \ -d '{ "code": "code-from-the-redirect", "state": "state-from-the-redirect"}'{ "github_app": { "role": "author", "app_id": 234568, "slug": "werft-acme-1a2b", "name": "werft-acme-1a2b", "html_url": "https://github.com/apps/werft-acme-1a2b", "private_key": { "updated_at": "2026-11-01T09:05:00Z", "updated_by": { "user_id": "3e7f1a90-5c2d-4b8e-8f61-2a9d0c4b7e35", "display_name": "Jae" } }, "created_at": "2026-11-01T09:05:00Z", "updated_at": "2026-11-01T09:05:00Z", "permissions": { "contents": "write", "metadata": "read", "pull_requests": "write" }, "permissions_checked_at": "2026-11-01T09:05:00Z", "warnings": [] }, "install_url": "https://github.com/apps/werft-acme-1a2b/installations/new", "receipt_id": "c4a1e9d2-7b3f-4e8a-a6c5-1d2e3f4a5b6c"}