# Refactor: idempotent POST contract for caller-supplied GUIDs

**Task**: AZ-362_refactor_idempotent_post_contract
**Name**: Return existing resource on duplicate POST instead of 500
**Description**: On `INSERT` conflict for a known caller-supplied `Id` in `/api/satellite/request` and `/api/satellite/route`, return the existing resource (200 OK) instead of bubbling a 500.
**Complexity**: 3 points
**Dependencies**: AZ-353 (C03) — so the new path doesn't traverse the leaky 500 handler.
**Component**: Api + Services.RegionProcessing + Services.RouteManagement
**Tracker**: AZ-362
**Epic**: AZ-350

## Problem

`SatelliteProvider.Api/Program.cs:187-211` (`RequestRegion`) and `Program.cs:233-250` (`CreateRoute`) accept `request.Id` from the caller and `INSERT` blindly. A retried POST hits a unique-key conflict at the DB and surfaces as 500 with a leaky message (pre-C03). The client cannot determine whether their first POST succeeded.

## Outcome

- Two consecutive POSTs with the same `Id` to either endpoint return 200 OK with the existing resource state.
- No duplicate background processing is triggered for retried POSTs.
- OpenAPI documents the idempotency contract.
- 37 unit + 5 smoke tests stay green.

## Scope

### Included
- In `RegionService` (and `RouteService`), detect the unique-key violation on insert; if present, fetch and return the existing row instead of throwing.
- Update the API handlers to translate the "existing resource" outcome to 200 OK.
- Update OpenAPI / Swagger annotations to document the idempotency.
- Add a unit/integration test that POSTs the same `Id` twice and asserts on 200 OK both times.

### Excluded
- Changing the `Id` source (caller-supplied stays).
- Adding a separate idempotency-key header.

## Acceptance Criteria

**AC-1: Region POST is idempotent**
Given a `POST /api/satellite/request` that creates a region with `Id = X`
When the same payload is POSTed again
Then both calls return 200 OK with the same region resource and only one background processing job is queued.

**AC-2: Route POST is idempotent**
Given a `POST /api/satellite/route` that creates a route with `Id = X`
When the same payload is POSTed again
Then both calls return 200 OK with the same route resource.

**AC-3: OpenAPI documents the contract**
Given the generated `swagger.json`
When inspected
Then the two endpoints describe the idempotency behavior (200 on duplicate `Id`).

**AC-4: Tests stay green**
Given the post-refactor build
When `scripts/run-tests.sh --smoke` runs
Then all 37 unit + 5 smoke scenarios pass.

## Constraints

- HTTP shape preserved on success (200 OK with the same resource body).
- 500-on-duplicate becomes 200-on-duplicate (a strict improvement).
- No DB schema change.

## Risks & Mitigation

**Risk 1: differing payloads with the same `Id`**
- *Risk*: a caller may POST the same `Id` with a *different* body. We must define behavior.
- *Mitigation*: for this task, return the existing resource (treating the second POST as a retry). A future ticket can add 409-on-mismatch detection.

Full change entry: `_docs/04_refactoring/03-code-quality-refactoring/list-of-changes.md` (C09).
