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/jsonCode 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
| Status | Body | Recovery |
|---|---|---|
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.
