# Remora API contract semantics OpenAPI `3.2.0` describes HTTP operations and JSON Schema Draft 2020-12 describes WebSocket frames. The API contract version is `1.0.0`; additive fields require clients to tolerate unknown response fields only after a contract version that permits them, while incompatible changes use a new major path or media type. ## Idempotency Every public mutation requires `Idempotency-Key`. The identity is caller subject, operation, and key. The server canonicalizes the semantic request, including the authorization target, with RFC 8785 and stores its SHA-256 fingerprint and complete response atomically with the mutation. The default retention is 24 hours and is never shorter than the advertised `Idempotency-Expires-At`. The same identity and fingerprint replays the original status, resource, and body after duplicate or ambiguous delivery. A different fingerprint returns `idempotency_conflict` and cannot affect either request. Naturally idempotent deletion and cancellation semantics still use this request identity. Internal claim uses job ID plus `operation_id` because no attempt exists yet. Renew, progress, and outcome use attempt ID, fencing token, worker incarnation, and `operation_id`. Each operation identity retains a request fingerprint. An exact replay has the original effect; a changed worker, fence, payload, stale, or non-authoritative tuple returns `precondition_failed` without mutation. First observation is accepted only when the corresponding `JobProtocol` action is enabled. Later eligibility changes do not alter a stored response, while a fresh operation is evaluated against current eligibility. [`RemoraApiMutation`](../../protocol/RemoraApiMutation.tla) models this durable operation ledger around an abstract successful `JobProtocol` effect. It does not redefine job phases or attempt-authority transitions. ## Authorization and errors Public operations apply the authorization rules in the architecture. List is filtered before pagination. A missing and an unauthorized resource both return `not_found`; callers cannot probe job existence. Cursors and event replay are bound to the authenticated subject and filter. `ApiError` fields are an allowlist. Messages and field diagnostics never carry credential values, bearer material, URL userinfo, raw upstream response bodies, internal stack traces, or unredacted configured endpoints. ## Jobs, pages, and results `Job.phase` uses the exact `JobProtocol` phase vocabulary; this contract does not define phase transitions. `lifecycle`, current authoritative attempt, current matching progress, reusable-checkpoint projection, result, and failure are separate fields. Progress is present only when its attempt ID and fence match the authoritative attempt in the same projection. Job pages order by `(created_at DESC, id DESC)`. The first request fixes a membership snapshot, authorization/filter fingerprint, and high-water mark. `next_cursor` is an opaque integrity-protected keyset cursor for that snapshot; new jobs do not enter later pages. Invalid, altered, cross-subject, or cross-filter cursors return the corresponding structured cursor error. A result endpoint returns only a visible `CommittedResult`. Tombstoned, unauthorized, absent, and never-committed results are not exposed. The hard delete endpoint returns the durable deletion resource established by the architecture. Worker failure outcomes accept only attempt-reportable runtime classes and do not accept a caller-supplied retryable flag. The service derives the durable disposition from the class mapping in the architecture. Validation, cancellation, and lost-attempt projection use their dedicated boundaries. ## WebSocket replay An HTTP job or list snapshot carries `event_cursor` at its consistency watermark. The client connects to `/v1/events` with that cursor. Server frames are ordered by increasing `sequence` for a connection and carry an opaque next cursor. Delivery is at least once; clients deduplicate event IDs and ignore a job revision no newer than the applied revision. Replay applies current authorization before delivery. If a cursor is absent, invalid, expired, authorization-incompatible, or no longer available after coordination loss, the server sends `resync_required`; the client obtains a new HTTP snapshot and reconnects from its cursor. Progress frames for a stale attempt/fence are suppressed before emission and ignored defensively by the client.