Read both the HTTP status and response body. Log the operation ID, resource ID, HTTP status, and `requestId`. Redact tokens and patient data.

## JSON error shape

```json
{
  "error": {
    "code": "revision_conflict",
    "message": "The package revision has changed.",
    "issues": [{ "field": "revision", "message": "Read the current revision." }]
  },
  "requestId": "synthetic-request-id"
}
```

This is a synthetic shape example, not an actual response. Match stable `error.code` values, not translated messages. Field details can appear in `error.field` and `error.issues[].field`.

| HTTP                | What to do                                                                  |
| ------------------- | --------------------------------------------------------------------------- |
| `400`               | Correct malformed JSON, path, query, or request shape                       |
| `401`               | Verify the token and host; obtain valid authentication                      |
| `403`               | Check scope, workspace selection, membership, and role                      |
| `404`               | Verify the resource ID and current workspace                                |
| `409`               | Read current state before deciding the next action                          |
| `413`               | Reduce the request to the documented body limit                             |
| `415`               | Use the expected content type                                               |
| `422`               | Correct business or data validation issues                                  |
| `428`               | Supply the required revision or idempotency precondition                    |
| `500`, `502`, `503` | Preserve evidence; determine if an external action occurred before retrying |

An edge or network failure can return HTML rather than `ApiError`. Capture status and safe response headers, including `X-Request-ID` when present. Do not assume every error body is JSON.

## Optimistic concurrency

Read a package and retain its `ETag`. Use the exact value in `If-Match` for a revision. If the server returns `revision_conflict`, read the package again and resolve the change. Never overwrite another revision with a guessed ETag.

Validation belongs to one revision and context. An edit invalidates it. A previously valid package can require a new check if its referenced document or context changes.

## One key per intent

Use `Idempotency-Key` where required by the operation. The key must contain 8–128 allowed characters. UUID strings are suitable.

Store the key **before** you send the request, alongside the exact request bytes and target operation. For a retry, keep the same key, actor, path parameters, and body. With raw JSON input, keep the same bytes.

| Situation                                                  | Safe action                                                                        |
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Response received and intent complete                      | Store the result; do not create another intent                                     |
| Lost response                                              | Read the resource; retry only the same intent with the same key when state permits |
| `idempotency_key_reused`                                   | Stop: the key was used for different content                                       |
| `idempotency_in_progress`                                  | Wait and inspect state                                                             |
| `submission_outcome_unknown` or `provider_outcome_unknown` | Reconcile with external evidence                                                   |
| New, deliberately different business intent                | Create a new key                                                                   |

The contract does not publish an expiry window for reusing confirmed receipts. Never assume an old key becomes available. A new key cannot bypass an `Unknown` package or provider state.

## Unknown does not mean failed

A timeout can occur after the external system received a request. Keep `Unknown` blocked until reconciliation. For RIPS, provide a known CUV and attempt ID. For health documents, use the reconciliation operation described in that document's reference.

Use bounded backoff for reads. The current contract does not promise webhooks or a fixed polling interval. Honor `429` and `Retry-After` if they occur. These are technical controls; they do not create per-request charges.
