Setup authorization

seamward login uses the OAuth device authorization grant to authorize one workspace environment. seamward setup then reuses that authorization entirely in the terminal. Users do not create or copy a workspace API key or Integration ID for this flow.

Authorization flow

The login command dynamically registers a public device client, opens the hosted Seamward consent screen, and requests only the setup scopes needed to inspect the selected environment, create or reuse reviewed Integrations, connect contracts, verify first observations, and explicitly delete an Integration after a separate impact preview and confirmation. After approval, the browser stays on Seamward and displays completion while the terminal securely polls for the grant. There is no browser redirect to a local callback address. The approval binds the user, workspace, tenant, workspace environment, and setup audience. No other seamward command opens a browser.

The installer stores the short-lived access token, refresh grant, and public routing identifiers outside the repository with owner-only file permissions. Project MCP configuration contains only an opaque credential reference.

Get setup context

Code example

GET /v1/setup/contextAuthorization: Bearer <setup-access-token>

The access token must include seamward:setup:integrations:read and must still match an active consent, workspace membership, and workspace environment grant.

Code example

{  "environment": {    "id": "env_sandbox",    "name": "Sandbox",    "slug": "sandbox"  },  "integrations": [],  "workspace": {    "slug": "seamward-demo"  }}

integrations may be empty before the first setup. After an approved apply, it lists every Integration in the selected environment with its public Connection key, Integration key, direction, protocol, name, and provider. The response contains no ingest token, workspace API key, or refresh token.

New setup clients send X-Seamward-Setup-Context: 2 to request an additional authorizationFingerprint. This opaque grant binding remains stable when an access token refreshes with unchanged authorization. It binds the current user, tenant, workspace, client, consent, environment, authorized Integration set, scopes, and setup audience. Changes to the grant or membership invalidate the binding, including changes later restored to their previous values. Clients must obtain a fresh review before applying with a different binding.

Requests without this header retain the original response shape. Unsupported versions return 409 setup_context_unavailable. An older server may omit the fingerprint; upgraded clients then bind their review to the current access token and require a new review after refresh. The local MCP keeps the fingerprint in memory and does not add it to the shared version 5 credential file.

The server refuses ambiguous legacy consent rows rather than choosing one as the grant binding. A persistent 401 unauthenticated after reauthorization requires support to review the duplicate grants; clients must not keep retrying an old reviewed operation.

Preview Integration changes

Code example

POST /v1/setup/integrations/previewAuthorization: Bearer <setup-access-token>Content-Type: application/json

Code example

{  "proposals": [    {      "direction": "inbound",      "ingestTokenHash": "74959815c80616dcd8ee4a38292e2c378ed57cbe49950ef24628960cb4470e6b",      "name": "Candidate ATS",      "protocol": "http-webhook",      "provider": "Candidate provider",      "setupId": "candidate-4d217910"    }  ]}

The token needs seamward:setup:integrations:write. The response records a five-minute approval preview and reports whether each boundary will be created or reused. Send preferredIntegrationId only when the reviewer explicitly chooses an existing Integration with the same environment and scope.

Preview creation is limited to 30 requests per client or workspace environment per minute. Expired, unapplied previews are retained for one day for reconciliation and audit troubleshooting, then removed when the environment creates another preview. Completed apply results remain durable.

Code example

{  "effects": [    {      "action": "create",      "direction": "inbound",      "integrationId": "int_setup_36e88b86d225f0cfbbc33c91",      "name": "Candidate ATS",      "protocol": "http-webhook",      "provider": "Candidate provider",      "setupId": "candidate-4d217910"    }  ],  "expiresAt": "2026-08-27T14:05:00.000Z",  "fingerprint": "sha256:3e3a48b005d086436ea09aa137f386487ea5dbe0cbde96982a83b39b7a4ab651",  "operationId": "setupop_01k3q2pk3e9h1m5fjk45dxv4mm"}

Apply the reviewed preview

Code example

POST /v1/setup/integrations/applyAuthorization: Bearer <setup-access-token>Content-Type: application/jsonIdempotency-Key: <stable-key-for-this-preview>

Code example

{  "fingerprint": "sha256:3e3a48b005d086436ea09aa137f386487ea5dbe0cbde96982a83b39b7a4ab651",  "operationId": "setupop_01k3q2pk3e9h1m5fjk45dxv4mm"}

The operation runs atomically. Repeating the same operation with the same idempotency key returns the same result. Reusing that key for a different preview returns idempotency_conflict. A stale, expired, cross-client, or cross-environment preview is rejected before any Integration is created.

Read operation status

Code example

GET /v1/setup/integrations/operations/{operationId}Authorization: Bearer <setup-access-token>

Status is previewed until an approved synchronous apply commits, then it is completed. The result is available only to the OAuth client and tenant that created it. Apply is atomic and cannot be cancelled midway. Update, detach, and retire operations are not available to setup clients in this alpha.

Delete an Integration

Deletion uses seamward:setup:integrations:delete and three calls:

Code example

POST /v1/setup/integrations/{integrationId}/deletion-previewDELETE /v1/setup/integrations/{integrationId}GET /v1/setup/integrations/{integrationId}/deletions/{operationId}

The preview returns an expiring operation ID, approval fingerprint, exact-name challenge, and record counts. The DELETE request must send those reviewed values, the returned confirmation challenge, and a stable Idempotency-Key. A stale or expired preview is rejected without deleting data. Status provides durable recovery when a client loses the response.

Setup deletion delegates to the canonical reviewed transaction. Every configured connection must already belong to the selected one-mode grant. A Sandbox grant cannot remove a Live connection. When the workflow has both modes configured, use the workspace-key whole deletion API. integration_scope_mismatch refuses this scope expansion. Active background work or a cross-connection reference also refuses deletion without a partial change. A changed grant or configuration requires a new review. The original OAuth client can recover the completion receipt with its refreshed authorization; another client owned by the same person cannot read that receipt.

Example preview response:

Code example

{  "operationId": "intdel_01k3s5dyq2pk3e9h1m5fjk45dx",  "approvalFingerprint": "sha256:8ef4c6194a5cff12e4df19d13bb26b352bc36d3097a8a4fd086e4d6047e60f7c",  "confirmationChallenge": "Candidate ATS (all connections)",  "expiresAt": "2026-08-30T14:10:00.000Z",  "integration": {    "id": "wint_candidate",    "name": "Candidate ATS",    "provider": "HireFlow",    "environmentId": "all"  },  "counts": {    "contracts": 1,    "observations": 15,    "findings": 3,    "incidents": 2,    "expectedOutcomes": 1,    "replayDatasets": 0,    "replayRuns": 0,    "repairProposals": 0,    "repairArtifacts": 0,    "repairDeliveries": 0,    "contractActivations": 1,    "approvalRecords": 0,    "validationReports": 0,    "webhookDeliveries": 0,    "repositoryMappings": 0,    "credentialOperations": 1,    "setupProvisioningItems": 1,    "oauthBindings": 1  }}

Send the reviewed values and exact name to the delete endpoint:

Code example

DELETE /v1/setup/integrations/wint_candidateAuthorization: Bearer <setup-access-token>Content-Type: application/jsonIdempotency-Key: delete-candidate-ats-20260830
{  "operationId": "intdel_01k3s5dyq2pk3e9h1m5fjk45dx",  "approvalFingerprint": "sha256:8ef4c6194a5cff12e4df19d13bb26b352bc36d3097a8a4fd086e4d6047e60f7c",  "confirmationName": "Candidate ATS (all connections)"}

A completed delete, or the durable status read after a lost response, returns:

Code example

{  "operationId": "intdel_01k3s5dyq2pk3e9h1m5fjk45dx",  "status": "completed",  "integrationId": "wint_candidate",  "name": "Candidate ATS",  "counts": {    "contracts": 1,    "observations": 15,    "findings": 3,    "incidents": 2,    "expectedOutcomes": 1,    "replayDatasets": 0,    "replayRuns": 0,    "repairProposals": 0,    "repairArtifacts": 0,    "repairDeliveries": 0,    "contractActivations": 1,    "approvalRecords": 0,    "validationReports": 0,    "webhookDeliveries": 0,    "repositoryMappings": 0,    "credentialOperations": 1,    "setupProvisioningItems": 1,    "oauthBindings": 1  }}

Deletion removes the Integration's operational records and runtime credential. Historical audit records and workspace usage totals remain. Workspace state, GitHub installations and grants, external GitHub artifacts, and unrelated Integrations remain. The API never changes customer source or environment files. Existing grants need a new seamward login approval before they contain the delete scope.

Errors and recovery

StatusBodyRecovery
400{"error":"invalid_setup_preview"}Correct duplicate or invalid proposals, then preview again.
400{"error":"idempotency_key_required"}Send a stable printable Idempotency-Key with the approved apply.
400{"error":"invalid_setup_apply"}Use the operation identifier and fingerprint returned by the current preview.
400{"error":"invalid_integration_deletion"}Use the operation identifier and fingerprint returned by the deletion preview.
400{"error":"confirmation_mismatch"}Type the exact case-sensitive Integration name from the deletion preview.
401{"error":"unauthenticated"}Run seamward login, approve the current workspace environment, then retry the command.
403{"error":"insufficient_scope"}Reauthorize so the grant includes the operation's setup scope.
404{"error":"not_found"}Confirm the environment or operation still exists in the approved workspace.
404{"error":"workspace_environment_not_found"}Select a workspace environment that still exists, then authorize setup again.
409{"error":"setup_context_unavailable"}Reauthorize a complete workspace environment target.
409{"error":"setup_preview_stale"}Preview the current repository and remote state again.
409{"error":"deletion_preview_expired"}Create and review a new deletion preview.
409{"error":"deletion_preview_stale"}Review the current Integration-owned record counts in a new preview.
409{"error":"idempotency_conflict"}Use the original request for that key or create a new key for the new preview.
409{"error":"operation_mismatch"}Use the deletion operation created for this exact Integration, actor, client, and workspace.
409{"error":"integration_mapping_required"}Choose one existing semantic match, then preview again.
409{"error":"integration_scope_mismatch"}Choose an Integration in the approved environment with the reviewed direction and protocol.
409{"error":"apply_in_progress"}Wait for the active apply, then read operation status or retry the same request.
409{"error":"setup_operation_unavailable"}Preview again because the stored operation result cannot be reconciled safely.
429{"error":"rate_limited"}Wait 60 seconds before creating another deletion preview for this workspace actor.

Changing membership, revoking consent, or deleting the workspace environment causes a later request to fail even when the JWT has not yet expired.

Security boundary

  • The OAuth client is public and uses the device authorization grant without a redirect URI or client secret.
  • Access is audience-bound to the setup API.
  • Setup authorization cannot write observations.
  • Runtime observation delivery still requires the Integration's secret ingest token.
  • Integration provisioning plus contract registration and activation remain reviewed MCP writes.
  • Workspace API keys remain an advanced option for headless automation and CI.