Contracts
The Contract API imports operation-scoped schemas, preserves every version as immutable evidence, and changes analysis only after explicit activation. For an HTTP API Integration, OpenAPI imports supported JSON request and response operations from paths. For an HTTP webhook Integration, it imports supported message operations from webhooks. A standalone JSON Schema must be bound to one explicit operation selector.
The legacy browser-session POST /contracts writer is retired. Every otherwise valid, owned request returns 409 canonical_lifecycle_required without changing contracts, activation history or audit. It retains deprecation headers until removal after 30 November 2026. Use the public reviewed draft-registration and activation routes documented below. Existing collector-authenticated baseline reads remain available for historical contracts.
For a canonical Sandbox or Live connection, the legacy route returns 409 with
canonical_lifecycle_required and writes nothing. Register a draft through the
public API, review it, and activate it explicitly. The legacy route cannot
activate a contract in either canonical mode.
Authentication and scopes
Workspace automation sends a workspace API key:
Code example
Authorization: Bearer $SEAMWARD_API_KEYUse contracts:read for listing and detail, contracts:write for draft registration, and contracts:activate for activation or rollback. One key may carry multiple scopes. An ingest token cannot call these routes.
The seamward CLI may also list contracts with its browser-approved setup
OAuth token carrying seamward:setup:contracts:read. That token is bound to
one environment and an approved Integration. It cannot register or activate a
contract through these public routes. Requesting another Integration returns
integration_scope_mismatch.
Copy {integrationId} from the integration's Overview tab under Connection status, using the Integration ID code block. It is a non-secret identifier. Sandbox tooling can derive the same ID from the public Connection key, so it does not need a second configuration value.
List contracts
Code example
GET /v1/integrations/{integrationId}/contracts?limit=25&cursor={opaqueCursor}The response identifies the active version and returns immutable versions in descending createdAt, then descending id, order. limit defaults to 25 and accepts 1 through 100. When nextCursor is not null, pass it unchanged as cursor to read the next page. A cursor is scoped to one integration.
Code example
{ "integration": { "id": "int_orders", "name": "Orders" }, "activeContractVersionId": "contract_v1", "contracts": [ { "id": "contract_v1", "declaredVersion": "2026-08", "fingerprint": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "importedFrom": "json-schema", "lifecycleStatus": "active", "activatedAt": "2026-08-20T12:00:00.000Z", "supersededAt": null, "createdAt": "2026-08-20T11:55:00.000Z", "operations": [ { "id": "operation_create_order_201", "operationKey": "createOrder:response:201", "direction": "outbound", "protocol": "http-api", "method": "POST", "routeTemplate": "/orders", "eventType": null, "payloadLocation": "response", "statusSelector": "201", "fingerprint": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "schema": { "type": "object", "required": ["id"] } } ] } ], "nextCursor": null}Register a draft
A reviewed automation may include expectedConnectionRevision, the opaque
string from connection metadata.
The API checks this epoch while holding the connection row lock and returns
connectionRevision from the committed draft transaction. Any intervening
configuration change returns connection_configuration_changed (409) without
writing a draft. Keep the returned epoch for the next reviewed mutation.
If a response is lost or the epoch no longer matches, inspect the current
state and review again. Do not replace the expected epoch with an arbitrary
fresh read to revive an old approval.
Code example
POST /v1/integrations/{integrationId}/contractsContent-Type: application/jsonOpenAPI example:
Code example
{ "declaredVersion": "2026-08", "expectedScope": { "direction": "outbound", "protocol": "http-api" }, "format": "openapi", "document": { "openapi": "3.1.0", "info": { "title": "Orders", "version": "2026-08" }, "paths": { "/orders": { "post": { "operationId": "createOrder", "responses": { "201": { "content": { "application/json": { "schema": { "type": "object", "required": ["id"] } } } } } } } } }}JSON Schema example:
Code example
{ "declaredVersion": "2026-08", "expectedScope": { "direction": "outbound", "protocol": "http-api" }, "format": "json-schema", "document": { "type": "object", "required": ["id"] }, "operation": { "method": "POST", "routeTemplate": "/orders", "payloadLocation": "response", "statusSelector": "201" }}Registration returns 201 with lifecycleStatus: "draft". It does not enqueue analysis or replace the active version.
expectedScope is an optimistic safety guard. Seamward returns 409 integration_scope_mismatch before writing a version or audit event when the target Integration's direction or protocol differs. The current CLI and console always send it. The field remains optional only so existing Contract API clients can migrate without a breaking change; new automation should always include it.
Code example
{ "contract": { "id": "contract_v2", "declaredVersion": "2026-09", "fingerprint": "sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc", "importedFrom": "json-schema", "lifecycleStatus": "draft", "operationCount": 1, "createdAt": "2026-08-21T09:30:00.000Z" }}Registration is safe to retry only while the existing version is still a draft and the retry is identical. If the same integration and declaredVersion already have a draft with the same document fingerprint, import format, operation count, and every operation selector and schema fingerprint, the API returns 201 with that existing draft. It does not create another version or audit event. A changed document, format, selector, or schema returns 409 contract_version_already_exists. An already active or superseded version is not treated as an identical draft retry.
Supported OpenAPI methods are GET, POST, PUT, PATCH, and DELETE. OpenAPI can be registered only against an HTTP API or HTTP webhook Integration. For an HTTP API Integration, Seamward imports paths operations. For an HTTP webhook Integration, it imports webhooks operations. Queue and scheduled-feed Integrations use an operation-bound JSON Schema. Imports read JSON and application/*+json schemas, local references, and the supported JSON Schema subset: type, properties, required, items, enum, and additionalProperties. Remote references, composition keywords such as allOf, oneOf, and anyOf, and non-JSON media types are not imported.
Get one version
Code example
GET /v1/integrations/{integrationId}/contracts/{contractVersionId}The response contains contract with the same immutable version shape returned by the list endpoint. A version id from another integration or workspace returns contract_not_found or integration_not_found without revealing foreign records.
Code example
{ "contract": { "id": "contract_v1", "declaredVersion": "2026-08", "fingerprint": "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "importedFrom": "json-schema", "lifecycleStatus": "active", "activatedAt": "2026-08-20T12:00:00.000Z", "supersededAt": null, "createdAt": "2026-08-20T11:55:00.000Z", "operations": [ { "id": "operation_create_order_201", "operationKey": "createOrder:response:201", "direction": "outbound", "protocol": "http-api", "method": "POST", "routeTemplate": "/orders", "eventType": null, "payloadLocation": "response", "statusSelector": "201", "fingerprint": "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", "schema": { "type": "object", "required": ["id"] } } ] }}Activate a version
Code example
POST /v1/integrations/{integrationId}/contracts/{contractVersionId}/activateContent-Type: application/jsonCode example
{ "expectedActiveContractVersionId": "contract_v1", "reason": "promote"}Reviewed automation may also include expectedConnectionRevision. It checks
all connection configuration alongside expectedActiveContractVersionId and
returns the committed connectionRevision. Stale configuration returns
connection_configuration_changed (409), with no activation, audit or enqueue.
This opaque connection epoch is separate from the integer analysis revision.
Use null when the integration has no active version. Use reason: "rollback" when reactivating a prior immutable version. Activation supersedes the previous version, records a new revision and audit event, and enqueues reanalysis in one database transaction.
Code example
{ "activationId": "activation_contract_v2_revision_8", "activeContractVersionId": "contract_v2", "previousActiveContractVersionId": "contract_v1", "revision": 8}If another deployment changed the active version first, the request returns 409:
Code example
{ "error": "active_contract_changed", "activeContractVersionId": "contract_v3"}Read the current version, review it, and retry with a new explicit precondition. Do not retry blindly.
Errors
| Status | Body | Meaning |
|---|---|---|
400 | {"error":"invalid_contract"} | The document, selector, or supported schema subset is invalid |
400 | {"error":"invalid_contract_query"} | A list limit or query parameter is invalid |
400 | {"error":"invalid_contract_cursor"} | The cursor is malformed or belongs to another integration |
400 | {"error":"invalid_contract_activation"} | The activation body or reason is invalid |
401 | {"error":"unauthenticated"} | The key is missing, malformed, expired, or revoked |
403 | {"error":"insufficient_scope"} | The key lacks the endpoint scope |
403 | {"error":"integration_scope_mismatch"} | Setup OAuth is missing the requested Integration |
404 | {"error":"integration_not_found"} | The integration is absent from this workspace |
404 | {"error":"contract_not_found"} | The version is absent from this integration |
409 | {"error":"contract_version_already_exists"} | The declared version exists with different content or selectors |
409 | {"error":"integration_scope_mismatch"} | The expected direction or protocol differs from the Integration |
409 | {"error":"active_contract_changed"} | The optimistic concurrency precondition is stale |
409 | {"error":"contract_not_activatable"} | The lifecycle transition is invalid |
503 | {"error":"service_unavailable"} | Reanalysis is unavailable, so activation was not attempted |
Next steps
- Authentication: create and scope a workspace API key.
- Workspace API: available and planned workspace automation.
