User

POST /api/v1/user/{system_uuid}/{system_user_id}

Create a new User row for the authenticated tenant. This page is the normative internal description for this endpoint per ADR 0026; the external contract is OpenAPI (operationId: createUser).

OpenAPI operationId (reference): createUser

Request path and behavior

Request enters the FastAPI stack in app/main.py (metrics, body size limit, API key + rate limit per ADR 0005, security headers, request ID), then app/api/v1/user.py. The handler validates the body against the OpenAPI schema, enforces idempotency for writes per ADR 0006, and delegates to UserService.createUserRepository (PostgreSQL). Successful writes may record an idempotency row for replay.

Success: 201 Created with the created user body (includes generated client_uuid). On idempotent replay (same key + same payload hash), the stored success response is returned with the original status code.

Idempotency

Idempotency-Key header is required: printable ASCII, length 1–128. The handler uses endpoint_path = "/api/v1/user" with the key and a SHA-256 hash of the canonical JSON body for lookup and replay. Hash mismatch → 409 IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PAYLOAD; missing header → 400 IDEMPOTENCY_KEY_REQUIRED. Full rules: Unified spec — Idempotency.

Errors

Envelope shape per ADR 0003 (code, key, message, source).

HTTP Code / key When
400 USER_101 / USER_CREATE_ALREADY_EXISTS Duplicate natural key (system_user_id, system_uuid) — logged at warning as create_user_duplicate in UserService.
400 COMMON_400 / IDEMPOTENCY_KEY_REQUIRED Write without Idempotency-Key.
409 COMMON_409 / IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PAYLOAD Same idempotency key, different body hash.
422 Validation Pydantic / OpenAPI validation — global handler logs validation_error at warning.
413 COMMON_413 Body over limit — middleware; request_body_limit_exceeded at warning.
401 / 429 API key / rate limit — before handler; see ADR 0005.
500 Uncaught: request_failed with logger.exception.

Logging

Baseline: every request completes with an info line from HTTP middleware — request_done method=… path=… status=… elapsed_ms=…. request_id is propagated from X-Request-Id per ADR 0023 (app/core/logging.py).

User router (app.api.v1.user), create path:

  • create_user_idempotent_replayinfo when replaying a stored success (key only).
  • create_user_requestedinfo, system_user_id, system_uuid.
  • create_user_succeededinfo after persist; client_uuid included.

UserService: create_user_duplicate at warning before raising; create_user_persisted at info after insert.

Metrics

http_requests_total and http_request_duration_seconds with labels including path_template=/api/v1/user, method=POST, and HTTP status. Database timing via db_operation_duration_seconds where applicable. Details: Unified spec — Metrics.

Single-file deep spec

Cross-cutting sections (layers, full error catalog context, dependencies): Unified internal spec — this operation.

← User resource hub

Page history

Date Change Author
Added Page history section (repository baseline). Ivan Boyarkin