openapi: 3.0.0 info: title: Aura Historia API Reference description: "## Overview\n\nThe Aura Historia API powers the Aura Historia antiques platform and its partner-facing integrations.\n\ It exposes product discovery, listing source discovery, personalization, partner onboarding, and integration\nworkflows\ \ on top of Aura Historia's serverless AWS platform.\n\nThis reference is designed for two audiences:\n- **Internal developers**\ \ building Aura Historia product-listings, operational tooling, and backoffice workflows\n- **External partners and integrators**\ \ synchronizing inventory, consuming platform data, or\n integrating with delegated access\n\n## Core API domains\n\n\ - **ProductListings and listing-sources** — search, retrieve, and explore product-listings, related product-listings,\ \ product\n history, and listing source data\n- **Personalization** — manage watchlists, saved search filters, and user\ \ notifications\n- **Partner workflows** — submit partnership applications, maintain partnerships, ingest catalog data\n\ \ in batches, and receive WooCommerce webhooks\n- **Identity and access** — manage user accounts, Aura Historia access\ \ tokens, and OAuth 2.0\n clients and token flows\n- **Billing** — create Stripe checkout, billing-portal, and subscription-management\ \ sessions\n\n## Authentication model\n\nThe API uses more than one authentication scheme depending on the route:\n\n\ - **`BearerAuth`** — Cognito **access** JWT bearer tokens for authenticated user, admin, and\n partner-user workflows.\ \ Cognito ID tokens are not accepted as API credentials.\n- **`AccessTokenAuth`** — Aura Historia opaque bearer access\ \ tokens created via\n `/api/v1/me/access-tokens` or OAuth 2.0, primarily intended for partner automations and server-to-server\n\ \ ingestion scenarios.\n\nWhen a route lists multiple security schemes, any one of the listed schemes may be accepted.\n\ Some read endpoints can also be called without authentication, while a valid user token may enrich\nthe response with\ \ personalized state such as watchlist, notification, or search-filter metadata.\n\n## Data and response conventions\n\ \n- **PATCH:** an omitted request member remains unchanged. A documented nullable PATCH member\n accepts `null` to clear\ \ its value; `null` for any other member returns `400 BAD_BODY_VALUE`.\n Use `[]`, not `null`, to replace a non-null\ \ collection with an empty collection. Empty HTTP\n bodies are invalid, `{}` is a valid object-PATCH no-op, and the partner-product\ \ PATCH `[]`\n remains a valid empty batch. Responses may omit absent optional values.\n- Product detail, watchlist,\ \ and saved-search match reads accept optional **`language`** and\n **`currency`** parameters. Currency defaults to `EUR`.\ \ Detail pricing contains the seller's source\n amounts, converted display amounts, and FX valuation metadata. Active\ \ product-listings use the latest persisted\n snapshot; sold product-listings use their immutable sale snapshot. Product\ \ history remains immutable, source-only,\n and is not localized.\n- Errors are returned as **`application/problem+json`**\ \ using a consistent RFC 9457-style structure\n with `status`, `title`, `error`, optional `source`, and optional `detail`\n\ - Many list and search endpoints use **cursor-based pagination** via `searchAfter`\n- Every canonical API response includes\ \ server-generated `X-Request-Id` and `X-Correlation-Id` headers. Clients may send `X-Correlation-Id` to preserve a trace\ \ only when it is a 1–128-character ASCII value containing letters, digits, `.`, `_`, or `-`; invalid values are replaced.\ \ Both headers are CORS-exposed.\n- Requests are limited to 1 MiB and time out after 30 seconds.\n- `GET /health` is a\ \ process liveness check. `GET /ready` returns `204` only when the configured PostgreSQL and OpenSearch dependencies are\ \ reachable; it otherwise returns `503`.\n- WooCommerce webhook ingestion responds with **`204 No Content`** after\ \ signed validation. Events that map to raw capture are durably captured; `product.created` and `product.updated` events with missing or unsupported status are acknowledged as ignored. A capture `204` acknowledges a changed, unchanged, duplicate, or stale outcome—not necessarily a new raw revision; canonical normalization is asynchronous, while partner Product batch writes remain synchronous\n\n## Environment\ \ endpoints\n\n- **Development:** `https://api.dev.aura-historia.com`\n- **Production:** `https://api.aura-historia.com`\n\ \n## Notes for integrators\n\n- Treat Aura Historia identifiers such as `listingSourceId`, `productListingId`, `eventId`,\ \ `userSearchFilterId`,\n and `partnershipApplicationId` as opaque values\n- For partner product ingestion, `sourceListingId`\ \ is the partner-controlled identifier and should\n remain stable within a listing source\n- Endpoints marked `deprecated`\ \ or `x-disabled` are retained for reference only and should not be\n used for new integrations\n" version: 1.0.0 contact: name: Aura-Historia Team url: https://aura-historia.com/ license: name: License url: https://github.com/aura-historia/backend/blob/develop/LICENSE servers: - url: https://api.dev.aura-historia.com description: Development environment - url: https://api.aura-historia.com description: Production environment paths: /api/v1/listing-sources/{listingSourceId}/product-listings: post: summary: Batch create product-listings (Partner API) description: "Creates one or more product-listings for a listing source using bearer authentication. This endpoint is intended for partnerships and accepts: - a Cognito bearer token for the partner user linked to the listing source, or - an Aura Historia access token owned by that partner user. Aura Historia access tokens on this endpoint must include the `product-listings:write` scope. Allowed callers are Aura Historia admins and users partnered with the target listing source. The request body is an array of `CreateProductListingData` objects. Each entry is created synchronously in its own PostgreSQL transaction. The response returns HTTP 200 with failures as `{ listingSourceId, sourceListingId, error }` objects when one or more entries succeed. `error` is the stable API error key for that entry. If every non-empty entry fails, the first failure is returned as a problem response. An empty array is accepted and returns `[]`. " operationId: postPartnerProductListings tags: - ProductListings parameters: - name: listingSourceId in: path required: true description: Unique identifier of the target ListingSource schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Array of at most 100 product-listings to create synchronously. May be empty. content: application/json: schema: type: array maxItems: 100 items: $ref: "#/components/schemas/CreateProductListingData" example: - sourceListingId: baroque-violin-001 title: text: Baroque Violin language: en description: text: A beautiful 18th-century baroque violin in excellent condition. language: en price: currency: EUR amount: 4500 availability: IN_STOCK url: https://my-listing-source.com/product-listings/baroque-violin images: - https://my-listing-source.com/images/violin-1.jpg - https://my-listing-source.com/images/violin-2.jpg responses: "200": description: "Batch create completed synchronously. This response is returned when all entries succeed or when at least one entry succeeds; it lists only failed entries. " headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/PartnerProductListingBatchFailuresResponse" examples: all_succeeded: summary: All product-listings were created value: [] partial_failure: summary: Some product-listings failed while at least one product was created value: - listingSourceId: 550e8400-e29b-41d4-a716-446655440000 sourceListingId: baroque-violin-002 error: CONFLICT "400": description: Bad request — request body is missing, empty, or contains invalid JSON content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Request body is absent or empty value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty. invalid_json: summary: Request body is not valid JSON value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: expected value at line 1 column 1 "401": description: "Unauthorized — the bearer token is missing, invalid, expired, or the referenced Aura Historia access token no longer exists. " content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_authorization: summary: Authorization header is missing value: status: 401 title: Unauthorized error: INVALID_CREDENTIALS source: field: Authorization type: header access_token_not_found: summary: Aura Historia access token is unknown or expired value: status: 401 title: Unauthorized error: ACCESS_TOKEN_NOT_FOUND "403": description: Forbidden — the authenticated caller is not allowed to ingest product-listings for this listing source content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: Not found — the specified listing source does not exist content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: LISTING_SOURCE_NOT_FOUND "503": description: Service unavailable — all entries encountered a temporary product-write failure content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PRODUCT_LISTING_TEMPORARILY_UNAVAILABLE "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PRODUCT_LISTING_INTERNAL_ERROR patch: summary: Batch update product-listings (Partner API) description: "Updates one or more existing product-listings for a listing source using bearer authentication. This endpoint is intended for partnerships and accepts: - a Cognito bearer token for the partner user linked to the listing source, or - an Aura Historia access token owned by that partner user. Aura Historia access tokens on this endpoint must include the `product-listings:write` scope. Allowed callers are Aura Historia admins and users partnered with the target listing source. The request body is an array of `UpdateProductListingData` objects. Omitted fields are unchanged; `null` clears availability, price, and auction fields. `null` for `url` or `images` returns `400 BAD_BODY_VALUE`; send `[]` to remove all images. The complete batch contract is validated before any entry is applied synchronously in its own PostgreSQL transaction. The response returns HTTP 200 with failures as `{ listingSourceId, sourceListingId, error }` objects when one or more entries succeed. `error` is the stable API error key for that entry. If every non-empty entry fails, the first failure is returned as a problem response. An empty array is accepted and returns `[]`. " operationId: patchPartnerProductListings tags: - ProductListings parameters: - name: listingSourceId in: path required: true description: Unique identifier of the target ListingSource schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Array of at most 100 product updates to apply synchronously. May be empty. content: application/json: schema: type: array maxItems: 100 items: $ref: "#/components/schemas/UpdateProductListingData" example: - sourceListingId: baroque-violin-001 availability: null - sourceListingId: baroque-violin-002 price: currency: EUR amount: 5000 availability: IN_STOCK responses: "200": description: "Batch update completed synchronously. This response is returned when all entries succeed or when at least one entry succeeds; it lists only failed entries. " headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/PartnerProductListingBatchFailuresResponse" examples: all_succeeded: summary: All product-listings were updated value: [] partial_failure: summary: Some product-listings failed while at least one product was updated value: - listingSourceId: 550e8400-e29b-41d4-a716-446655440000 sourceListingId: baroque-violin-002 error: PRODUCT_LISTING_NOT_FOUND "400": description: Bad request — request body is missing, empty, or contains invalid JSON content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Request body is absent or empty value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty. invalid_json: summary: Request body is not valid JSON value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: expected value at line 1 column 1 "401": description: "Unauthorized — the bearer token is missing, invalid, expired, or the referenced Aura Historia access token no longer exists. " content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_authorization: summary: Authorization header is missing value: status: 401 title: Unauthorized error: INVALID_CREDENTIALS source: field: Authorization type: header access_token_not_found: summary: Aura Historia access token is unknown or expired value: status: 401 title: Unauthorized error: ACCESS_TOKEN_NOT_FOUND "403": description: Forbidden — the authenticated caller is not allowed to ingest product-listings for this listing source content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: Not found — the specified listing source does not exist content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: LISTING_SOURCE_NOT_FOUND "503": description: Service unavailable — all entries encountered a temporary product-write failure content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PRODUCT_LISTING_TEMPORARILY_UNAVAILABLE "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PRODUCT_LISTING_INTERNAL_ERROR put: summary: Batch upsert product-listings (Partner API) description: "Creates new product-listings or updates existing ones for a listing source in a single batch call,\nusing\ \ bearer authentication. This endpoint is intended for partnerships and accepts:\n- a Cognito bearer token for the\ \ partner user linked to the listing source, or\n- an Aura Historia access token owned by that partner user.\n\nAura\ \ Historia access tokens on this endpoint must include the `product-listings:write` scope.\nAllowed callers are Aura\ \ Historia admins and users partnered with the target listing source.\n\nThe request body is an array of `UpsertProductListingData`\ \ objects. Each entry is upserted\nsynchronously in its own PostgreSQL transaction. For each entry:\n- **New listing**\ \ — omitted or `null` availability, price, estimates, and auction timestamps\n create no value; a concrete value\ \ sets it.\n- **Existing listing** — availability, price, each price estimate, and each auction timestamp\n are tri-state:\ \ omitted preserves, `null` clears, and a concrete value sets. A withdrawn\n listing is restored before current facts\ \ apply.\n- `title` and `description` apply only on creation. For an\n existing\ \ listing they preserve current state and emit no current-state history event.\n- `images` is separate: omit to preserve,\ \ send `[]` to clear, or send an array to replace.\n `null` is invalid. `url` is non-clearable: omit or send `null`\ \ to preserve it; send a URL\n value to replace it.\n\nThe response returns HTTP 200 with failures as `{ listingSourceId,\ \ sourceListingId, error }` objects when\none or more entries succeed. `error` is the stable API error key for that\ \ entry. If every non-empty entry fails, the first failure is returned\nas a problem response. An empty array is accepted\ \ and returns `[]`.\n" operationId: putPartnerProductListings tags: - ProductListings parameters: - name: listingSourceId in: path required: true description: Unique identifier of the target ListingSource schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Array of at most 100 product-listings to upsert synchronously. May be empty. content: application/json: schema: type: array maxItems: 100 items: $ref: "#/components/schemas/UpsertProductListingData" examples: create_new: summary: Create a new product value: - sourceListingId: baroque-violin-001 title: text: Baroque Violin language: en description: text: A beautiful 18th-century baroque violin in excellent condition. language: en price: currency: EUR amount: 4500 availability: IN_STOCK url: https://my-listing-source.com/product-listings/baroque-violin images: - https://my-listing-source.com/images/violin-1.jpg update_existing: summary: Update an existing product while leaving the current price unchanged value: - sourceListingId: baroque-violin-001 availability: SOLD_OUT priceEstimateMin: currency: EUR amount: 4200 priceEstimateMax: currency: EUR amount: 4800 url: https://my-listing-source.com/product-listings/baroque-violin?status=sold images: - https://my-listing-source.com/images/violin-1.jpg - https://my-listing-source.com/images/violin-detail.jpg auctionEnd: "2025-05-10T12:30:00Z" clear_images: summary: Clear an existing product's images value: - sourceListingId: baroque-violin-001 images: [] responses: "200": description: "Batch upsert completed synchronously. This response is returned when all entries succeed or when at least one entry succeeds; it lists only failed entries. " headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/PartnerProductListingBatchFailuresResponse" examples: all_succeeded: summary: All product-listings were upserted value: [] partial_failure: summary: Some product-listings failed while at least one product was upserted value: - listingSourceId: 550e8400-e29b-41d4-a716-446655440000 sourceListingId: baroque-violin-002 error: BAD_BODY_VALUE "400": description: Bad request — request body is missing, empty, or contains invalid JSON content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Request body is absent or empty value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty. invalid_json: summary: Request body is not valid JSON value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: expected value at line 1 column 1 "401": description: "Unauthorized — the bearer token is missing, invalid, expired, or the referenced Aura Historia access token no longer exists. " content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_authorization: summary: Authorization header is missing value: status: 401 title: Unauthorized error: INVALID_CREDENTIALS source: field: Authorization type: header access_token_not_found: summary: Aura Historia access token is unknown or expired value: status: 401 title: Unauthorized error: ACCESS_TOKEN_NOT_FOUND "403": description: Forbidden — the authenticated caller is not allowed to ingest product-listings for this listing source content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: Not found — the specified listing source does not exist content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: LISTING_SOURCE_NOT_FOUND "503": description: Service unavailable — all entries encountered a temporary product-write failure content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PRODUCT_LISTING_TEMPORARILY_UNAVAILABLE "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PRODUCT_LISTING_INTERNAL_ERROR delete: summary: Batch delete product-listings (Partner API) description: "Soft-deletes product-listings for a listing source. Each entry writes its product lifecycle delete event and materialized PostgreSQL state synchronously in its own transaction. Allowed callers are Aura Historia admins and users partnered with the target listing source. Aura Historia access tokens must include the `product-listings:write` scope. The request body is an array of product identifiers. The response returns HTTP 200 with failures as `{ listingSourceId, sourceListingId, error }` objects when one or more entries succeed. `error` is the stable API error key for that entry. If every non-empty entry fails, the first failure is returned as a problem response. An empty array is accepted and returns `[]`. " operationId: deletePartnerProductListings tags: - ProductListings parameters: - name: listingSourceId in: path required: true description: Unique identifier of the target ListingSource schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Array of at most 100 product-listings to delete synchronously. May be empty. content: application/json: schema: type: array maxItems: 100 items: $ref: "#/components/schemas/WithdrawProductListingData" example: - sourceListingId: baroque-violin-001 responses: "200": description: "Batch delete completed synchronously. This response is returned when all entries succeed or when at least one entry succeeds; it lists only failed entries. " content: application/json: schema: $ref: "#/components/schemas/PartnerProductListingBatchFailuresResponse" examples: all_succeeded: value: [] partial_failure: value: - listingSourceId: 550e8400-e29b-41d4-a716-446655440000 sourceListingId: baroque-violin-002 error: PRODUCT_LISTING_NOT_FOUND "400": description: Bad request — request body is missing, empty, or contains invalid JSON content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Unauthorized — the bearer token is missing, invalid, expired, or unknown. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Forbidden — caller is neither admin nor partnered with this listing source, or the access token lacks `product-listings:write`. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: Not found — the specified listing source or product does not exist. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Service unavailable — all entries encountered a temporary product-write failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" /api/v1/webhooks/woocommerce/{listingSourceId}: post: summary: Ingest a WooCommerce product webhook (Partner API) description: "Accepts a single WooCommerce product webhook event for a partnership using bearer authentication. This endpoint is intended for partnerships and accepts: - a Cognito bearer token for the partner user linked to the listing source, or - an Aura Historia access token owned by that partner user. The caller must provide: - the bearer token in the `Authorization` header, - the WooCommerce topic in `x-wc-webhook-topic`, and - the base64-encoded HMAC-SHA256 signature of the raw request body in `x-wc-webhook-signature`, and - optionally, the WooCommerce delivery identifier in `x-wc-webhook-delivery-id`. A supplied delivery identifier applies only when an event maps to raw capture. Its provider receipt retains only a canonical semantic source-payload SHA-256 evidence digest for 90 days. A captured-observation retry with the same digest receives `204`; reuse with different evidence returns `409 WOOCOMMERCE_PROVIDER_RECEIPT_DIGEST_CONFLICT`. Logical receipt expiry permits a new receipt but never removes or rewrites a captured raw revision or its provenance. A timestamp-free DELETE establishes a restore barrier. A later UPSERT without a provably newer `date_modified_gmt` returns `409 WOOCOMMERCE_PROVIDER_SOURCE_ORDER_AMBIGUOUS`; it creates neither a receipt nor a raw revision, so the original request remains recoverable for reconciliation and retry. Authorized ignored create/update events persist no receipt, even when they include a delivery identifier. Topic-specific payload requirements: - every topic requires `id` - `product.created` and `product.updated` with `status: publish` capture an UPSERT and require nonblank `name` and `permalink` - `product.created` and `product.updated` with `status: trash`, `draft`, `pending`, or `private` capture a DELETE and require only `id`; missing or unsupported statuses are authorized ignored events and also require only `id` - `product.deleted` captures a DELETE and requires only `id` The listing source identified by `listingSourceId` must have a stored `woocommerceWebhookSecret` and a configured `woocommerceLanguage`. A nonblank `price` maps to a `SET` and requires the listing source to have `woocommerceCurrency` configured. " operationId: postWoocommerceWebhook tags: - ProductListings security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listingSourceId in: path required: true description: Unique identifier of the target ListingSource that should receive the WooCommerce webhook schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: x-wc-webhook-topic in: header required: true description: "WooCommerce webhook topic that determines how the payload is interpreted. Only the listed topic values are accepted. " schema: type: string enum: - product.created - product.updated - product.deleted example: product.created - name: x-wc-webhook-signature in: header required: true description: "Base64-encoded HMAC-SHA256 signature of the raw HTTP request body, calculated with the listing source's configured `woocommerceWebhookSecret`. " schema: type: string example: wqW/6B0S9myGxVQ8l3PPX2f6s4HWtY6q9KAr4m+Jv3E= - name: x-wc-webhook-delivery-id in: header required: false description: "Optional WooCommerce delivery identifier. For an event that maps to raw capture, its receipt retains only a canonical semantic source-payload SHA-256 evidence digest for 90 days and deduplicates retries. Reusing the identifier with a different digest returns `409 WOOCOMMERCE_PROVIDER_RECEIPT_DIGEST_CONFLICT`. Logical receipt expiry permits a new receipt but never removes or rewrites a captured raw revision or its provenance. Authorized ignored create/update status events persist no receipt and receive no receipt-based retry-deduplication, even when this header is supplied. When absent, no provider receipt or receipt-based retry-deduplication guarantee is made." schema: type: string minLength: 1 maxLength: 512 example: 4d1f0a18-ec5e-4e97-8cf8-ff4edc8b2e31 requestBody: required: true description: "Topic-specific WooCommerce product payload. Every topic requires `id`. For `product.created` and `product.updated`, `status: publish` captures an UPSERT and requires nonblank `name` and `permalink`; `status: trash`, `draft`, `pending`, or `private` captures a DELETE and requires only `id`. Missing or unsupported create/update statuses are authorized ignored events and require only `id`. `product.deleted` captures a DELETE and requires only `id`; its other fields are optional and ignored when absent. When present on an event that maps to raw capture, WooCommerce `date_modified_gmt` is the source ordering timestamp. It accepts a no-offset GMT value interpreted as UTC, RFC3339 `Z`, or RFC3339 `+00:00`; malformed or nonzero-offset values return `400 BAD_BODY_VALUE`. `product.created` and `product.updated` events with missing or unsupported status are ignored only after capability and source authorization, then acknowledge `204` without parsing `date_modified_gmt` or constructing a provider receipt; a denied ignored event returns `403 FORBIDDEN`. An authorized ignored event persists no provider receipt even if a delivery ID is supplied. Omit it when source order is unavailable; an omitted timestamp provides no source-ordering guarantee. " content: application/json: schema: anyOf: - $ref: "#/components/schemas/WoocommerceProductWebhookUpsertData" - $ref: "#/components/schemas/WoocommerceProductWebhookDeleteData" examples: created: summary: Product created webhook value: id: 17 name: Test Produkt Titel permalink: http://aura-historia-test.local/product/test-produkt-titel/ description: "

Hayde yallah test beschreibung

" short_description: "

Hayde yallah kurze test beschreibung

" price: "42.69" status: publish stock_status: instock images: [] updated: summary: Product updated webhook value: id: 17 name: Test Produkt Titel permalink: http://aura-historia-test.local/product/test-produkt-titel/ description: "

Hayde yallah test beschreibung

" short_description: "

Hayde yallah kurze test beschreibung

" price: "123.45" status: publish stock_status: instock images: [] deleted: summary: Product deleted webhook value: id: 17 responses: "204": description: "Webhook capability, signature, and source authorization are validated. A `204` acknowledges an authorized ignored create/update event with missing or unsupported status, or a durably captured changed, unchanged, duplicate, or stale outcome; it does not mean a new raw revision was written. Ignored events do not parse `date_modified_gmt` or construct a provider receipt. A provider receipt exists only for an event that maps to raw capture and carries a delivery ID; authorized ignored events persist no receipt even when a delivery ID is supplied. Source ordering is evaluated only for an event that maps to raw capture with a valid `date_modified_gmt`. When a delivery ID or source timestamp is omitted, no corresponding receipt or source-ordering guarantee is made. Canonical ProductListing normalization runs asynchronously. The backend returns no response body on success. " headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" "400": description: "Bad request — invalid or missing listing source ID, missing body, malformed JSON, unsupported WooCommerce topic, an invalid `date_modified_gmt` source timestamp (including a nonzero offset) for an event that maps to raw capture, or a missing field required by the selected capture operation. " content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_listing_source_id: summary: Missing listing source ID path parameter value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: listingSourceId sourceType: path detail: Missing field 'listingSourceId'. invalid_listing_source_id: summary: Invalid listing source ID format value: status: 400 title: Bad Request error: INVALID_UUID source: field: listingSourceId sourceType: path detail: Invalid UUID format missing_body: summary: Request body is absent or empty value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty. invalid_json: summary: Request body is not valid JSON value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: expected value at line 1 column 1 missing_topic: summary: WooCommerce topic header is missing value: status: 400 title: Bad Request error: BAD_HEADER_VALUE source: field: x-wc-webhook-topic sourceType: header "401": description: "Unauthorized — missing or invalid bearer credentials use `INVALID_CREDENTIALS`. A missing or invalid WooCommerce signature uses `BAD_HEADER_VALUE`. " content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_authorization: summary: Authorization header is missing value: status: 401 title: Unauthorized error: INVALID_CREDENTIALS source: field: Authorization sourceType: header invalid_access_token: summary: Aura Historia access token is unknown or expired value: status: 401 title: Unauthorized error: INVALID_CREDENTIALS missing_signature: summary: WooCommerce signature header is missing value: status: 401 title: Unauthorized error: BAD_HEADER_VALUE source: field: x-wc-webhook-signature sourceType: header detail: WooCommerce signature header is required. signature_mismatch: summary: WooCommerce signature does not match the request body value: status: 401 title: Unauthorized error: BAD_HEADER_VALUE source: field: x-wc-webhook-signature sourceType: header detail: WooCommerce signature is invalid. "403": description: Forbidden — the caller lacks the required ProductListing write capability or exact source partnership grant, including for ignored status events content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: Not found — the specified listing source does not exist content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: LISTING_SOURCE_NOT_FOUND "409": description: Conflict — a delivery receipt conflicts with its retained evidence digest (`WOOCOMMERCE_PROVIDER_RECEIPT_DIGEST_CONFLICT`), distinct evidence has the same `date_modified_gmt` timestamp (`WOOCOMMERCE_PROVIDER_SOURCE_ORDER_CONFLICT`), or an UPSERT cannot safely cross a delete ordering boundary (`WOOCOMMERCE_PROVIDER_SOURCE_ORDER_AMBIGUOUS`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: receipt_digest_conflict: summary: Delivery ID conflicts with its retained evidence digest value: status: 409 title: Conflict error: WOOCOMMERCE_PROVIDER_RECEIPT_DIGEST_CONFLICT source_order_conflict: summary: Source timestamp conflicts with distinct prior evidence value: status: 409 title: Conflict error: WOOCOMMERCE_PROVIDER_SOURCE_ORDER_CONFLICT source_order_ambiguous: summary: Source timestamp cannot prove a post-delete restore is newer value: status: 409 title: Conflict error: WOOCOMMERCE_PROVIDER_SOURCE_ORDER_AMBIGUOUS "503": description: Service unavailable — this route emits only `AUTH_TEMPORARILY_UNAVAILABLE`, `LISTING_SOURCE_TEMPORARILY_UNAVAILABLE`, or `PRODUCT_LISTING_TEMPORARILY_UNAVAILABLE`. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: auth_temporarily_unavailable: summary: Authentication is temporarily unavailable value: status: 503 title: Service Unavailable error: AUTH_TEMPORARILY_UNAVAILABLE listing_source_temporarily_unavailable: summary: ListingSource access is temporarily unavailable value: status: 503 title: Service Unavailable error: LISTING_SOURCE_TEMPORARILY_UNAVAILABLE product_listing_temporarily_unavailable: summary: Raw ProductListing capture is temporarily unavailable value: status: 503 title: Service Unavailable error: PRODUCT_LISTING_TEMPORARILY_UNAVAILABLE "500": description: Internal server error — this route emits only `AUTH_INTERNAL_ERROR`, `LISTING_SOURCE_INTERNAL_ERROR`, or `PRODUCT_LISTING_INTERNAL_ERROR`. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: auth_internal_error: summary: Authentication failed internally value: status: 500 title: Internal Server Error error: AUTH_INTERNAL_ERROR listing_source_internal_error: summary: ListingSource access failed internally value: status: 500 title: Internal Server Error error: LISTING_SOURCE_INTERNAL_ERROR product_listing_internal_error: summary: Raw ProductListing capture failed internally value: status: 500 title: Internal Server Error error: PRODUCT_LISTING_INTERNAL_ERROR /api/v1/product-listings/by-slug/{productListingTitleSlugId}: get: summary: Get a single product by title slug description: >- Retrieves an active ProductListing by its immutable Aura-owned `productListingTitleSlugId`, derived from its title. Withdrawn listings return `404 PRODUCT_LISTING_NOT_FOUND`; this endpoint never returns `410`. operationId: getProductListingByTitleSlug tags: - ProductListings parameters: - name: productListingTitleSlugId in: path required: true description: Immutable Aura-owned public locator derived from the listing title schema: type: string minLength: 8 maxLength: 120 pattern: "^[a-z0-9]+(-[a-z0-9]+)*-[0-9a-f]{6}$" example: museum-cabinet-18-19-century-a1b2c3 - name: language in: query required: false schema: $ref: "#/components/schemas/LanguageData" - name: currency in: query required: false schema: $ref: "#/components/schemas/CurrencyData" security: - BearerAuth: [] - {} responses: "200": description: ProductListing found and returned successfully content: application/json: schema: $ref: "#/components/schemas/PersonalizedProductListingDetailsData" "400": description: Invalid path or query parameter content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: ProductListing not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Product detail temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" /api/v1/product-listings/{productListingId}: get: summary: Get a single product description: "Retrieves a single product by its `productListingId`. Withdrawn listings return `404 PRODUCT_LISTING_NOT_FOUND`; this endpoint never returns `410`. `pricing.source` preserves seller-supplied source amounts and currencies. `pricing.display` contains HalfUp-converted amounts in the requested currency, and `pricing.valuation` identifies the persisted snapshot used. A sold Product may have no main price; then display prices are omitted while its immutable `SALE` valuation remains present. Currency defaults to `EUR`. Anonymous responses use freshness caching only: no `ETag` or `Last-Modified` validator is emitted because current display pricing may change when the selected persisted FX snapshot changes. Responses always contain `item`, the product data. A valid user or delegated-user bearer token adds optional top-level `userState`; authenticated responses are not cached. " operationId: getProductListing tags: - ProductListings parameters: - name: productListingId in: path required: true description: Unique identifier of the ProductListing schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: language in: query required: false description: Requested current title and description language. Defaults to `en`; unavailable translations fall back to stored content. schema: $ref: "#/components/schemas/LanguageData" - name: currency in: query required: false description: Display currency. Defaults to `EUR`. schema: $ref: "#/components/schemas/CurrencyData" security: - BearerAuth: [] - {} responses: "200": description: ProductListing found and returned successfully content: application/json: schema: $ref: "#/components/schemas/PersonalizedProductListingDetailsData" headers: Content-Language: description: The language of the returned content schema: $ref: "#/components/schemas/LanguageData" example: de Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" "400": description: Bad request - invalid parameters content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: ProductListing not found, including a withdrawn listing content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: PRODUCT_LISTING_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PRODUCT_LISTING_INTERNAL_ERROR "503": description: Product detail or notification state is temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PRODUCT_LISTING_TEMPORARILY_UNAVAILABLE /api/v1/product-listings/{productListingId}/history: get: summary: Get ProductListing history description: "Retrieves immutable domain history for one ProductListing by `productListingId`. Each array item is one committed `PRODUCT_LISTING_DISCOVERED` or `PRODUCT_LISTING_CHANGED` event, ordered by occurrence time and event ID. A changed item contains a deterministic ordered `changes` array; it is never split into synthetic entries. Discovery exposes image count, never source image URLs. Price snapshots preserve stored source amounts and currencies. Sale observations retain their immutable source provenance. This endpoint performs no currency conversion. " operationId: getProductListingHistory tags: - ProductListings parameters: - name: productListingId in: path required: true description: Unique ProductListing identifier schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 responses: "200": description: Product history retrieved successfully headers: Cache-Control: schema: type: string example: public, max-age=180, s-maxage=900 content: application/json: schema: type: array items: $ref: "#/components/schemas/ProductListingHistoryEntryData" examples: product_history: summary: Example ProductListing history value: - eventType: PRODUCT_LISTING_DISCOVERED productListingId: 550e8400-e29b-41d4-a716-446655440000 eventId: 650e8400-e29b-41d4-a716-446655440001 payload: listingSourceId: 550e8400-e29b-41d4-a716-446655440010 sourceListingId: source-6ba7b810 pricing: price: currency: EUR amount: 3000 availability: IN_STOCK url: https://listing-source.example/product-listings/6ba7b810 imageCount: 3 auction: start: null end: null timestamp: "2026-09-01T10:00:00Z" - eventType: PRODUCT_LISTING_CHANGED productListingId: 550e8400-e29b-41d4-a716-446655440000 eventId: 650e8400-e29b-41d4-a716-446655440003 payload: changes: - type: MAIN_PRICE_CHANGED previous: currency: EUR amount: 3000 current: currency: EUR amount: 2999 - type: AVAILABILITY_CHANGED previous: IN_STOCK current: SOLD_OUT timestamp: "2026-09-01T10:15:00Z" "400": description: Bad request - Invalid parameters content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: Product not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: PRODUCT_LISTING_NOT_FOUND "503": description: ProductListing history is temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PRODUCT_LISTING_TEMPORARILY_UNAVAILABLE "500": description: ProductListing history contains invalid stored event data content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PRODUCT_LISTING_INTERNAL_ERROR /api/v1/product-listings/{productListingId}/similar: get: summary: Get similar product-listings description: "Retrieves product-listings similar to the specified product using KNN search over text embeddings. When the product embedding is ready, the endpoint returns matching product-listings. If it is absent, the endpoint returns `202 Accepted` with a polling location. Ready entries return `displayPrice` in the requested currency and `priceValuation` metadata. Active ProductListings use one persisted snapshot pinned for the request; sold ProductListings use immutable sale-time values. Every ready entry contains `item`; a valid user or delegated-user bearer token adds `userState`. Personalized KNN results use `Cache-Control: no-store`. " operationId: getSimilarProductListings tags: - ProductListings parameters: - name: productListingId in: path required: true description: Unique identifier of the product schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: language in: query required: false description: Language used for localized KNN result titles. Defaults to `en`. schema: $ref: "#/components/schemas/LanguageData" - name: currency in: query required: false description: Display currency for KNN result prices. Defaults to `EUR`. schema: $ref: "#/components/schemas/CurrencyData" security: - BearerAuth: [] - {} responses: "200": description: Similar product-listings found and returned successfully content: application/json: schema: type: array items: $ref: "#/components/schemas/PersonalizedProductListingSummaryData" "202": description: Product embedding is not available yet. Poll the Location URL for KNN results. headers: Location: description: URL to poll for similar product-listings once the embedding is available schema: type: string format: uri example: /api/v1/product-listings/550e8400-e29b-41d4-a716-446655440000/similar "400": description: Bad request - invalid parameters content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: Product not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: PRODUCT_LISTING_NOT_FOUND detail: ProductListing with ListingSourceId '550e8400-e29b-41d4-a716-446655440000' and SourceListingId '6ba7b810' not found. "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/product-listings: get: summary: Simple product search via query parameters description: "Performs canonical ProductListing search with query parameters. Text queries combine BM25 and embedding KNN retrieval when embedding generation succeeds; otherwise the backend falls back to BM25. Explicit non-score sorts use BM25. A valid bearer token adds user state and makes the response `Cache-Control: no-store`. `language` defaults to `en` and `currency` defaults to `EUR`." operationId: simpleSearchProductListings tags: - ProductListings parameters: - name: language in: query required: false description: Language used for search matching and localized response fields; defaults to `en` schema: $ref: "#/components/schemas/LanguageData" example: de - name: currency in: query required: false description: Currency used to select the indexed price field; defaults to `EUR`; no request-time currency conversion occurs schema: $ref: "#/components/schemas/CurrencyData" example: EUR - name: productQuery in: query required: false description: Optional text queries for product-listings. Multiple entries are ORed together. style: form explode: true schema: type: array items: type: string minLength: 1 example: - test - example - name: enhancedSearchDescription in: query required: false description: Optional natural-language description used to refine matching. schema: type: string minLength: 1 maxLength: 1000 - name: excludeProductId in: query required: false description: Optional product IDs to exclude from results. style: form explode: true schema: type: array items: type: string format: uuid uniqueItems: true example: - 550e8400-e29b-41d4-a716-446655440000 - name: listingSourceId in: query required: false description: Optional ListingSource IDs to include. A result must belong to one of the supplied sources. style: form explode: true schema: type: array items: type: string format: uuid uniqueItems: true - name: excludeListingSourceId in: query required: false description: Optional ListingSource IDs to exclude from results. style: form explode: true schema: type: array items: type: string format: uuid uniqueItems: true - name: price in: query required: false description: "Optional price range filter in minor currency units (e.g. cents for most supported currencies, whole yen for JPY). Use `price[min]` and/or `price[max]` to specify the range bounds. " style: deepObject explode: true schema: type: object properties: min: type: integer minimum: 0 description: Minimum price (inclusive) in minor currency units for the selected indexed currency example: 1000 max: type: integer minimum: 0 description: Maximum price (inclusive) in minor currency units for the selected indexed currency example: 50000 - name: availability in: query required: false description: Optional exact availability assertions. Values are ORed. style: form explode: true schema: type: array items: $ref: "#/components/schemas/ListingAvailabilityData" uniqueItems: true example: - IN_STOCK - LIMITED_AVAILABILITY - name: created in: query required: false description: "Optional filter by product creation date range (RFC3339 format). Use `created[min]` and/or `created[max]` to specify the datetime bounds. " style: deepObject explode: true schema: type: object properties: min: type: string format: date-time description: Minimum creation datetime (inclusive, RFC3339 format) example: "2024-01-01T00:00:00Z" max: type: string format: date-time description: Maximum creation datetime (inclusive, RFC3339 format) example: "2024-12-31T23:59:59Z" - name: updated in: query required: false description: "Optional filter by product last-updated date range (RFC3339 format). Use `updated[min]` and/or `updated[max]` to specify the datetime bounds. " style: deepObject explode: true schema: type: object properties: min: type: string format: date-time description: Minimum last-updated datetime (inclusive, RFC3339 format) example: "2024-01-01T00:00:00Z" max: type: string format: date-time description: Maximum last-updated datetime (inclusive, RFC3339 format) example: "2024-12-31T23:59:59Z" - name: auctionStart in: query required: false description: "Optional filter by auction start datetime range (RFC3339 format). Use `auctionStart[min]` and/or `auctionStart[max]` to specify the bounds. Only matches product-listings that have an auction start time set. " style: deepObject explode: true schema: type: object properties: min: type: string format: date-time description: Minimum auction start datetime (inclusive, RFC3339 format) example: "2025-05-01T00:00:00Z" max: type: string format: date-time description: Maximum auction start datetime (inclusive, RFC3339 format) example: "2025-05-31T23:59:59Z" - name: auctionEnd in: query required: false description: "Optional filter by auction end datetime range (RFC3339 format). Use `auctionEnd[min]` and/or `auctionEnd[max]` to specify the bounds. Only matches product-listings that have an auction end time set. " style: deepObject explode: true schema: type: object properties: min: type: string format: date-time description: Minimum auction end datetime (inclusive, RFC3339 format) example: "2025-05-01T00:00:00Z" max: type: string format: date-time description: Maximum auction end datetime (inclusive, RFC3339 format) example: "2025-05-31T23:59:59Z" - name: sort in: query required: false description: Field to sort results by schema: $ref: "#/components/schemas/SortProductListingFieldData" example: created - name: order in: query required: false description: Sort order (only valid when sort is specified) schema: type: string enum: - asc - desc example: desc - name: searchAfter in: query required: false description: "Opaque ProductListing cursor for pagination. Pass the complete JSON object returned as `searchAfter` in the previous response as one query value. It pins the FX snapshot for the full cursor chain. " schema: $ref: "#/components/schemas/ProductListingSearchCursorData" example: '{"fxRateId":"6ba7b810-9dad-11d1-80b4-00c04fd430c8","searchAfter":[2999,"550e8400-e29b-41d4-a716-446655440000"]}' - name: size in: query required: false description: Number of product-listings to return per page schema: type: integer minimum: 1 maximum: 100 default: 21 example: 5 security: - BearerAuth: [] - {} responses: "200": description: Simple search results returned successfully headers: Cache-Control: description: Shared/public cache directives for GET simple-search responses schema: type: string example: public, max-age=60, s-maxage=300 Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/ProductListingSearchResultData" "400": description: Bad request - invalid query parameters content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_query: summary: Invalid query parameter payload value: status: 400 title: Bad Request error: BAD_BODY_VALUE invalid_sort_field: summary: Invalid sort field value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: sort sourceType: query detail: "Expected any of: 'score', 'price', 'updated', 'created'. Got: 'invalid'" invalid_order: summary: Invalid sort order value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: order sourceType: query detail: "Expected any of: 'asc' or 'desc'. Got: 'invalid'" "503": description: Product search or required personalized state is temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PRODUCT_LISTING_TEMPORARILY_UNAVAILABLE "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/search-filters: get: summary: List user search filters description: "Retrieves all search filters for the authenticated user. Uses the service's fixed collection ordering. Requires a Cognito JWT or an Aura access token with `search-filters:write`. " operationId: getUserSearchFilters tags: - Search Filters security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: Search filters retrieved successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/UserSearchFilterCollectionData" example: items: - userId: 550e8400-e29b-41d4-a716-446655440000 userSearchFilterId: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 name: My Tech Store Search notifications: true state: ACTIVE search: language: en currency: USD productQuery: - antique clock - vintage clock price: min: 1000 max: 5000 availability: - IN_STOCK listingSourceId: - 550e8400-e29b-41d4-a716-446655440000 created: "2024-01-01T10:00:00Z" updated: "2024-01-01T12:00:00Z" from: 0 size: 1 total: 1 "401": description: Unauthorized - missing or invalid Cognito JWT or Aura access token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - Aura access token lacks the required `search-filters:write` scope content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR post: summary: Create a new search filter description: "Creates a new search filter for the authenticated user. The search filter configuration is provided in the request body. Returns the created search filter with generated ID and metadata. Requires a Cognito JWT or an Aura access token with `search-filters:write`. The number of allowed search filters and the set of usable search filter fields depend on the user's tier: - `FREE`: Up to 1 search filter. Allowed fields are `productQuery`, `price`, `state`, `excludeProductId`, and `lifecycle`. Other fields are forbidden and return `SEARCH_FILTER_RESTRICTED_FEATURE`. - `PRO`: Up to 5 search filters. All filter fields are allowed. - `ULTIMATE`: Unlimited search filters. All filter fields are allowed. " operationId: createUserSearchFilter tags: - Search Filters security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Search filter configuration with name and filter criteria content: application/json: schema: $ref: "#/components/schemas/PostUserSearchFilterData" example: name: My Tech Store Search search: language: en currency: USD productQuery: - antique clock - vintage clock price: min: 1000 max: 5000 availability: - IN_STOCK created: min: "2024-01-01T00:00:00Z" max: "2024-12-31T23:59:59Z" listingSourceId: - 550e8400-e29b-41d4-a716-446655440000 responses: "201": description: Search filter created successfully headers: Location: description: URL of the created search filter schema: type: string format: uri example: https://api.aura-historia.com/api/v1/me/search-filters/6ba7b810-9dad-11d1-80b4-00c04fd430c8 Content-Language: description: The language of the returned content schema: $ref: "#/components/schemas/LanguageData" example: en Last-Modified: description: When the search filter was created/updated schema: type: string format: http-date example: Wed, 01 Jan 2024 12:00:00 GMT Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/UserSearchFilterData" example: userId: 550e8400-e29b-41d4-a716-446655440000 userSearchFilterId: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 name: My Tech Store Search notifications: true state: ACTIVE search: language: en currency: USD productQuery: - antique clock - vintage clock price: min: 1000 max: 5000 availability: - IN_STOCK listingSourceId: - 550e8400-e29b-41d4-a716-446655440000 created: "2024-01-01T12:00:00Z" updated: "2024-01-01T12:00:00Z" "400": description: Bad request - invalid request body content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Missing request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_json: summary: Invalid JSON format value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Invalid JSON format "401": description: Unauthorized - missing or invalid Cognito JWT or Aura access token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - Aura access token lacks the required `search-filters:write` scope content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: User not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "422": description: Unprocessable Entity - search filter quota exceeded or restricted feature used content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: quota_exceeded: summary: Search filter quota exceeded value: status: 422 title: Unprocessable Content error: SEARCH_FILTER_QUOTA_EXCEEDED detail: Exceeded the maximum amount of search filters. There are already 1/1 search filters occupied. restricted_feature: summary: Search filter contains a field not available for the user's tier value: status: 422 title: Unprocessable Content error: SEARCH_FILTER_RESTRICTED_FEATURE detail: Search filter contains forbidden search field 'listingSourceName' which requires a higher user tier. "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/search-filters/{userSearchFilterId}: get: summary: Get a specific search filter description: "Retrieves a specific search filter by its ID for the authenticated user. Returns the complete search filter configuration and metadata. Requires a Cognito JWT or an Aura access token with `search-filters:write`. " operationId: getUserSearchFilter tags: - Search Filters parameters: - name: userSearchFilterId in: path required: true description: Unique identifier of the search filter schema: type: string format: uuid example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: Search filter found and returned successfully headers: Content-Language: description: The language of the returned content schema: $ref: "#/components/schemas/LanguageData" example: en Last-Modified: description: When the search filter was last updated schema: type: string format: http-date example: Wed, 01 Jan 2024 12:00:00 GMT Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/UserSearchFilterData" example: userId: 550e8400-e29b-41d4-a716-446655440000 userSearchFilterId: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 name: My Tech Store Search notifications: true state: ACTIVE search: language: en currency: USD productQuery: - antique clock - vintage clock price: min: 1000 max: 5000 availability: - IN_STOCK listingSourceId: - 550e8400-e29b-41d4-a716-446655440000 created: "2024-01-01T10:00:00Z" updated: "2024-01-01T12:00:00Z" "400": description: Bad request - invalid parameters content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_search_filter_id: summary: Missing search filter ID value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: userSearchFilterId sourceType: path invalid_uuid: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_UUID source: field: userSearchFilterId sourceType: path detail: Invalid UUID format "401": description: Unauthorized - missing or invalid Cognito JWT or Aura access token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - Aura access token lacks the required `search-filters:write` scope content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: Search filter not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: SEARCH_FILTER_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR delete: summary: Delete a search filter description: "Deletes a specific search filter by its ID for the authenticated user. The search filter must exist and belong to the authenticated user. Requires a Cognito JWT or an Aura access token with `search-filters:write`. " operationId: deleteUserSearchFilter tags: - Search Filters parameters: - name: userSearchFilterId in: path required: true description: Unique identifier of the search filter to delete schema: type: string format: uuid example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "204": description: Search filter deleted successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" "400": description: Bad request - invalid parameters content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_search_filter_id: summary: Missing search filter ID value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: userSearchFilterId sourceType: path invalid_uuid: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_UUID source: field: userSearchFilterId sourceType: path detail: Invalid UUID format "401": description: Unauthorized - missing or invalid Cognito JWT or Aura access token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - Aura access token lacks the required `search-filters:write` scope content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: Search filter not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: SEARCH_FILTER_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR patch: summary: Update a search filter description: "Updates a specific search filter by its ID for the authenticated user. Allows partial updates: omitted members remain unchanged, documented nullable criteria accept `null` to clear, and `null` for required scalars or collections returns `400 BAD_BODY_VALUE`. `{}` returns the existing search filter unchanged; an empty HTTP body is invalid. Requires a Cognito JWT or an Aura access token with `search-filters:write`. The set of usable search filter fields depends on the user's tier: - `FREE`: Allowed fields are `productQuery`, `price`, `state`, `excludeProductId`, and `lifecycle`. Other fields are forbidden and return `SEARCH_FILTER_RESTRICTED_FEATURE`. - `PRO`: All filter fields are allowed. - `ULTIMATE`: All filter fields are allowed. " operationId: updateUserSearchFilter tags: - Search Filters parameters: - name: userSearchFilterId in: path required: true description: Unique identifier of the search filter to update schema: type: string format: uuid example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: "Partial search filter update data. `{}` is a no-op; an empty HTTP body is invalid. " content: application/json: schema: $ref: "#/components/schemas/PatchUserSearchFilterData" examples: partial_update: summary: Update specific fields value: name: Updated Filter Name search: price: min: 2000 max: 8000 listingSourceId: - 550e8400-e29b-41d4-a716-446655440000 update_name_only: summary: Update only the name value: name: New Filter Name disable_notifications: summary: Disable notifications for this search filter value: notifications: false enable_notifications: summary: Enable notifications for this search filter value: notifications: true deactivate_filter: summary: Deactivate this search filter manually value: state: INACTIVE_BY_USER reactivate_filter: summary: Reactivate this search filter value: state: ACTIVE update_filter_only: summary: Update only the filter criteria value: search: language: de currency: EUR listingSourceId: - 550e8400-e29b-41d4-a716-446655440000 empty_update: summary: No changes (returns existing filter) value: {} responses: "200": description: Search filter updated successfully headers: Content-Language: description: The language of the returned content schema: $ref: "#/components/schemas/LanguageData" example: en Last-Modified: description: When the search filter was last updated schema: type: string format: http-date example: Wed, 01 Jan 2024 12:30:00 GMT Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/UserSearchFilterData" example: userId: 550e8400-e29b-41d4-a716-446655440000 userSearchFilterId: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 name: Updated Filter Name notifications: true state: ACTIVE search: language: en currency: USD productQuery: - antique clock - vintage clock price: min: 2000 max: 8000 availability: - IN_STOCK listingSourceId: - 550e8400-e29b-41d4-a716-446655440000 created: "2024-01-01T10:00:00Z" updated: "2024-01-01T12:30:00Z" "400": description: Bad request - invalid parameters or body content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_search_filter_id: summary: Missing search filter ID value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: userSearchFilterId sourceType: path invalid_uuid: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_UUID source: field: userSearchFilterId sourceType: path detail: Invalid UUID format invalid_json: summary: Invalid JSON format value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Invalid JSON format "401": description: Unauthorized - missing or invalid Cognito JWT or Aura access token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - Aura access token lacks the required `search-filters:write` scope content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: Search filter or user not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: search_filter_not_found: summary: Search filter not found value: status: 404 title: Not Found error: SEARCH_FILTER_NOT_FOUND user_not_found: summary: User not found value: status: 404 title: Not Found error: USER_NOT_FOUND "422": description: Unprocessable Entity - restricted feature used for the user's tier or quota exceeded during reactivation content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: restricted_feature: summary: Reactivation blocked by tier-restricted features value: status: 422 title: Unprocessable Content error: SEARCH_FILTER_RESTRICTED_FEATURE detail: Search filter contains forbidden search field 'listingSourceName' which requires a higher user tier. quota_exceeded: summary: Reactivation blocked by search-filter quota value: status: 422 title: Unprocessable Content error: SEARCH_FILTER_QUOTA_EXCEEDED detail: Exceeded the maximum amount of search filters. There are already 1/1 search filters occupied. "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/search-filters/{userSearchFilterId}/product-listings: get: summary: Preview product matches for a saved search filter description: "Previews product-listings that would match the authenticated user's saved search filter using the same percolator-style query semantics used by search-filter product-match processing. The request only controls response localization (`language`, `currency`). Client-driven pagination is not supported. The endpoint always returns a fixed preview of up to 10 product-listings and rejects `size` or `searchAfter` query parameters with `BAD_QUERY_PARAMETER_VALUE`. When the saved search filter contains `search.enhancedSearchDescription` and enhanced matching is available, the backend re-evaluates each candidate product for this specific filter. ProductListings rejected by enhanced matching are omitted from the preview; accepted product-listings expose the live result in `userState.searchFilter`: - `matched` (`true`) - `hidden` (always `false` in this flow) - optional `matchReason` In that enhanced flow, `userSearchFilterId`, `userSearchFilterName`, and `matchFeedback` are omitted from `userState.searchFilter`. Requires valid Cognito JWT authentication. " operationId: getSearchFilterPreviewProductListings tags: - Search Filters parameters: - name: userSearchFilterId in: path required: true description: Unique identifier of the search filter schema: type: string format: uuid example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 - name: currency in: query required: false description: Currency used to select indexed search prices; no request-time currency conversion occurs schema: $ref: "#/components/schemas/CurrencyData" example: EUR - name: language in: query required: false description: "Preferred language for localized content. Defaults to `en` when omitted. " schema: $ref: "#/components/schemas/LanguageData" example: de security: - BearerAuth: [] responses: "200": description: Product match preview retrieved successfully headers: Cache-Control: description: Cache control directive — always set to `no-store` schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/SearchFilterMatchProductCollectionData" example: items: - item: productListingId: 550e8400-e29b-41d4-a716-446655440000 productListingTitleSlugId: golden-cufflinks-a1b2c3 eventId: 550e8400-e29b-41d4-a716-446655440001 source: listingSourceId: 550e8400-e29b-41d4-a716-446655440000 name: Curated Antiques slugId: curated-antiques sourceListingId: 6ba7b810 title: text: Pair of Golden Cufflinks language: en price: currency: EUR amount: 2999 availability: IN_STOCK url: https://curated-antiques.example/product-listings/golden-cufflinks viewUrl: https://curated-antiques.example/product-listings/golden-cufflinks?utm_source=aura_historia&utm_medium=referral images: - url: https://curated-antiques.example/images/golden-cufflinks-1.jpg contentPolicy: decision: ALLOWED created: "2024-01-01T10:00:00Z" updated: "2024-01-01T12:00:00Z" userState: watchlist: watching: false notifications: false contentVisibility: showUnassessedOrSensitiveContent: false notification: unseenNotificationIds: [] searchFilter: matched: true hidden: false matchReason: The cufflinks match the saved enhanced description for vintage golden accessories. size: 1 total: 1 searchAfter: null "400": description: Bad request - invalid parameters content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: search_after_not_supported: summary: Client pagination cursor is not supported value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: searchAfter sourceType: query detail: Remove pagination parameters; this endpoint always returns a fixed-size preview. size_not_supported: summary: Client pagination size is not supported value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: size sourceType: query detail: Remove pagination parameters; this endpoint always returns a fixed-size preview. missing_search_filter_id: summary: Missing search filter ID value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: userSearchFilterId sourceType: path invalid_uuid: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_UUID source: field: userSearchFilterId sourceType: path detail: Invalid UUID format "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: Search filter not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: SEARCH_FILTER_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/search-filters/{userSearchFilterId}/matches: get: summary: List hydrated persisted matches for a search filter description: "Retrieves full localized personalized Product details for persisted matches of the given saved search filter and authenticated user. Results use fixed ascending match-creation order and are paginated with the tie-safe RFC3339 match-creation timestamp plus product UUID `searchAfter` cursor. Requires a Cognito JWT or an Aura access token with `search-filters:write`. " operationId: getSearchFilterMatches tags: - Search Filters parameters: - name: userSearchFilterId in: path required: true description: Unique identifier of the search filter schema: type: string format: uuid example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 - name: language in: query required: false description: Preferred language for localized product content. Defaults to `en`. schema: $ref: "#/components/schemas/LanguageData" example: de - name: currency in: query required: false description: Display currency. Defaults to `EUR`. schema: $ref: "#/components/schemas/CurrencyData" - name: searchAfter in: query required: false description: JSON-encoded `[RFC3339 match creation timestamp, product UUID]` cursor from the preceding page. schema: type: string example: '["2024-01-01T10:00:00Z","550e8400-e29b-41d4-a716-446655440111"]' - name: size in: query required: false description: Number of matched product-listings to return per page schema: type: integer minimum: 1 maximum: 100 default: 21 example: 21 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: Hydrated search-filter matched product-listings retrieved successfully headers: Cache-Control: description: Cache control directive — always set to `no-store` schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/SearchFilterMatchProductCollectionData" example: items: - item: productListingId: 550e8400-e29b-41d4-a716-446655440111 productListingTitleSlugId: golden-accessory-a1b2c3 eventId: 550e8400-e29b-41d4-a716-446655440222 source: listingSourceId: 550e8400-e29b-41d4-a716-446655440333 name: Golden Listings slugId: golden-listings sourceListingId: accessory-1 pricing: source: price: amount: 12500 currency: GBP display: price: amount: 14780 currency: EUR valuation: type: CURRENT fxRateId: 550e8400-e29b-41d4-a716-446655440444 capturedAt: "2026-08-15T10:00:00Z" availability: IN_STOCK lifecycle: ACTIVE url: https://example.test/accessory-1 viewUrl: https://example.test/accessory-1 images: [] auction: {} created: "2024-01-01T10:00:00Z" updated: "2024-01-01T12:00:00Z" userState: watchlist: watching: false notifications: false contentVisibility: showUnassessedOrSensitiveContent: false notification: unseenNotificationIds: [] searchFilter: matched: true hidden: false size: 1 total: null searchAfter: - "2024-01-01T10:00:00Z" - 550e8400-e29b-41d4-a716-446655440111 "400": description: Bad request - invalid parameters content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_search_after_timestamp: summary: Invalid searchAfter timestamp value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: searchAfter sourceType: query detail: Invalid RFC3339 timestamp format missing_search_filter_id: summary: Missing search filter ID value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: userSearchFilterId sourceType: path invalid_uuid: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_UUID source: field: userSearchFilterId sourceType: path detail: Invalid UUID format "401": description: Unauthorized - missing or invalid Cognito JWT or Aura access token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - Aura access token lacks the required `search-filters:write` scope content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: Search filter not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: SEARCH_FILTER_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR "503": description: Product details, persisted FX snapshots, or notification state are temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: SEARCH_FILTER_TEMPORARILY_UNAVAILABLE /api/v1/me/search-filters/{userSearchFilterId}/matches/{productListingId}: patch: summary: Update feedback for a search-filter match description: 'Updates the authenticated user''s feedback for a specific product match created by one of their saved search filters. The match is addressed by the search filter ID and canonical product ID. Requires a Cognito JWT or an Aura access token with `search-filters:write`. The request body itself is required and must be a JSON object, but the `feedback` field is optional: - `{"feedback": true}` marks the match as relevant. - `{"feedback": false}` marks the match as not relevant. - `{}` performs a no-op update and returns the existing stored match unchanged. ' operationId: updateSearchFilterMatchFeedback tags: - Search Filters parameters: - name: userSearchFilterId in: path required: true description: Unique identifier of the saved search filter that produced the match schema: type: string format: uuid example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 - name: productListingId in: path required: true description: Canonical identifier of the matched product schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440111 security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: "Feedback patch for an existing search-filter product match. An empty JSON object `{}` is valid. Omitting `feedback` inside the JSON object leaves the stored feedback unchanged. " content: application/json: schema: $ref: "#/components/schemas/PatchUserSearchFilterMatchData" examples: mark_match_as_relevant: summary: Mark the match as relevant value: feedback: true mark_match_as_not_relevant: summary: Mark the match as not relevant value: feedback: false no_op_patch: summary: No-op patch returning the existing match unchanged value: {} responses: "200": description: Search-filter product match updated successfully headers: Last-Modified: description: When the search-filter product match was last updated schema: type: string format: http-date example: Wed, 01 Jan 2024 12:30:00 GMT Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/SearchFilterProductMatchData" examples: feedback_set_to_true: summary: Match feedback stored as relevant value: userId: 550e8400-e29b-41d4-a716-446655440000 userSearchFilterId: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 productListingId: 550e8400-e29b-41d4-a716-446655440111 originEventId: 550e8400-e29b-41d4-a716-446655440222 feedback: true created: "2024-01-01T10:00:00Z" updated: "2024-01-01T12:30:00Z" no_op_patch: summary: Empty patch returns the existing feedback state value: userId: 550e8400-e29b-41d4-a716-446655440000 userSearchFilterId: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 productListingId: 550e8400-e29b-41d4-a716-446655440111 originEventId: 550e8400-e29b-41d4-a716-446655440222 feedback: true created: "2024-01-01T10:00:00Z" updated: "2024-01-01T10:00:00Z" "400": description: Bad request - invalid parameters or body content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_search_filter_id: summary: Missing search filter ID value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: userSearchFilterId sourceType: path missing_product_id: summary: Missing product ID value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: productListingId sourceType: path invalid_search_filter_id: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_UUID source: field: userSearchFilterId sourceType: path detail: Invalid UUID format invalid_product_id: summary: Invalid product ID format value: status: 400 title: Bad Request error: INVALID_UUID source: field: productListingId sourceType: path detail: Invalid UUID format missing_body: summary: Missing request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_body: summary: Invalid request body format value: status: 400 title: Bad Request error: BAD_BODY_VALUE "401": description: Unauthorized - missing or invalid Cognito JWT or Aura access token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - Aura access token lacks the required `search-filters:write` scope content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: Search-filter product match not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: SEARCH_FILTER_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/watchlist: get: summary: List user's watchlist product-listings description: "Retrieves all product-listings in the authenticated user's watchlist in watchlist creation order. Each result contains current product data and complete user-specific state. Accepts Cognito JWT authentication or an Aura Historia access token with `watchlist:read`. " operationId: getWatchlistProductListings tags: - Watchlist parameters: - name: language in: query required: false description: Preferred language for localized content. Defaults to `en` when omitted. schema: $ref: "#/components/schemas/LanguageData" example: de - name: currency in: query required: false description: Display currency. Defaults to `EUR`. schema: $ref: "#/components/schemas/CurrencyData" - name: size in: query required: false description: ProductListings per page. Defaults to 21; values are clamped to 1..100. schema: type: integer minimum: 1 maximum: 100 default: 21 - name: searchAfter in: query required: false description: "JSON array cursor returned by the preceding page: [RFC3339 timestamp, product UUID]." schema: type: string example: '["2026-08-04T12:34:56Z", "550e8400-e29b-41d4-a716-446655440000"]' security: - BearerAuth: [] responses: "200": description: Watchlist product-listings retrieved successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: type: object required: - items - size properties: items: type: array items: $ref: "#/components/schemas/PersonalizedProductListingDetailsData" size: type: integer minimum: 1 maximum: 100 searchAfter: type: array items: type: string minItems: 2 maxItems: 2 nullable: true example: items: - item: productListingId: 550e8400-e29b-41d4-a716-446655440000 productListingTitleSlugId: smartphone-case-a1b2c3 eventId: 550e8400-e29b-41d4-a716-446655440001 listingSourceId: 550e8400-e29b-41d4-a716-446655440000 sourceListingId: 6ba7b810 listingSourceName: Tech Store title: text: Smartphone Case language: en availability: IN_STOCK lifecycle: ACTIVE url: https://tech-store.com/smartphone-case viewUrl: https://tech-store.com/smartphone-case?utm_source=aura_historia&utm_medium=referral images: - url: https://tech-store.com/images/case-1.jpg contentPolicy: decision: ALLOWED created: "2024-01-01T10:00:00Z" updated: "2024-01-01T12:00:00Z" userState: watchlist: watching: true notifications: false contentVisibility: showUnassessedOrSensitiveContent: false notification: unseenNotificationIds: [] searchFilter: matched: false hidden: false size: 21 searchAfter: null "400": description: Bad request - invalid parameters content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE detail: Query parameters are invalid. "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR "503": description: Product details, persisted FX snapshots, or notification state are temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: WATCHLIST_TEMPORARILY_UNAVAILABLE post: summary: Add product to watchlist description: "Adds a product to the authenticated user's watchlist. The request body requires canonical `productListingId` and may set `notifications`; notifications default to `true`. Active-entry quota depends on the user's tier: Free users may have 20, Pro users 100, and Ultimate users have no active-entry limit. Adding beyond the active quota returns a 422 Unprocessable Entity error. Returns the created `WatchlistEntryData`. Requires a Cognito JWT or an Aura access token with `watchlist:write`. " operationId: addWatchlistProduct tags: - Watchlist security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Canonical product identifier and optional notification preference. content: application/json: schema: $ref: "#/components/schemas/PostWatchlistData" example: productListingId: 550e8400-e29b-41d4-a716-446655440000 notifications: true responses: "201": description: Watchlist entry created successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/WatchlistEntryData" example: userId: 550e8400-e29b-41d4-a716-446655440000 productListingId: 550e8400-e29b-41d4-a716-446655440000 notifications: true state: ACTIVE "400": description: Bad request - invalid request body content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Missing request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_json: summary: Invalid JSON format value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Invalid JSON format "401": description: Unauthorized - missing or invalid Cognito JWT or Aura access token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - Aura access token lacks the required `watchlist:write` scope content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: User or ProductListing not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: user_not_found: value: status: 404 title: Not Found error: USER_NOT_FOUND product_listing_not_found: value: status: 404 title: Not Found error: PRODUCT_LISTING_NOT_FOUND "409": description: Watchlist entry already exists, or the ProductListing is withdrawn and cannot be watched content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: already_exists: value: status: 409 title: Conflict error: CONFLICT detail: Watchlist entry already exists. product_listing_unavailable: value: status: 409 title: Conflict error: PRODUCT_LISTING_UNAVAILABLE detail: ProductListing is unavailable. "422": description: Unprocessable Entity - watchlist quota exceeded content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 422 title: Unprocessable Content error: WATCHLIST_QUOTA_EXCEEDED detail: Exceeded the maximum amount of watchlist entries. There are already 20/20 active watchlist entries occupied. "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/admin/listing-sources: post: summary: Create a ListingSource as admin description: | Creates a ListingSource for an authenticated administrator. `operator` accepts either an existing Party reference (`EXISTING` with `partyId`) or a new Party with name and optional contact details (`NEW`). Ingestion configuration, provider uniqueness, presentation, and referral validation remain service-owned. `woocommerceWebhookSecret` is accepted only when WooCommerce ingestion is configured and is never returned or logged. operationId: adminCreateListingSource tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: ListingSource creation payload. content: application/json: schema: $ref: "#/components/schemas/CreateListingSourceData" responses: "201": description: ListingSource created successfully. headers: Location: description: URL of the created admin ListingSource detail resource. schema: type: string format: uri example: /api/v1/admin/listing-sources/550e8400-e29b-41d4-a716-446655440000 content: application/json: schema: $ref: "#/components/schemas/ListingSourceReferenceData" "400": description: Bad request - missing, malformed, or invalid ListingSource body. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Unauthorized - missing or invalid bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Forbidden - this endpoint requires the ADMIN role. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: Referenced operator Party was not found. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "409": description: ListingSource or provider configuration conflicts with current state. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Internal ListingSource creation failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: ListingSource creation is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" get: summary: Search ListingSources as admin description: | Searches ListingSource records for authenticated administrators. `query` performs a case-insensitive substring search across the ListingSource name and stable slug, and also the operator Party name and slug. `name` restricts the substring match to the ListingSource name. `listingSourceId` and `listingSourceSlugId` are exact identity filters. `operatorPartyId` and `ingestionMethod` are exact filters. Results use deterministic cursor pagination. The default sort is `name` ascending; override it by providing both `sort` and `order`. Valid sort fields are `name`, `slug`, `created`, and `updated`; ListingSource ID is always the final deterministic tie-breaker. Page sizes are clamped to 1–100, with a default of 21. The returned UUID `searchAfter` cursor is omitted on the terminal page and should be sent with the same filters and sort. Responses always use `Cache-Control: no-store`. The result contains only safe business-resource fields: ListingSource identity and name, operator Party reference, active ingestion methods, presentation URLs, and referral summary. Provider credentials, webhook secrets, and crawler-local configuration are never returned. operationId: adminSearchListingSources tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: query in: query required: false description: Optional case-insensitive substring query across source and operator Party text. schema: type: string example: antiques - name: name in: query required: false description: Optional case-insensitive substring filter for the ListingSource name. schema: type: string example: antiques - name: listingSourceId in: query required: false description: Optional exact ListingSource UUID filter. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: listingSourceSlugId in: query required: false description: Optional exact immutable ListingSource slug filter. schema: type: string example: antiques-and-more-550e8400-e29b-41d4-a716-446655440000 - name: operatorPartyId in: query required: false description: Optional exact operator Party UUID filter. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440001 - name: ingestionMethod in: query required: false description: Optional exact active ingestion-method filter. schema: $ref: "#/components/schemas/ListingIngestionMethodData" example: PARTNER_API - name: sort in: query required: false description: "Sort field. Valid values are `name`, `slug`, `created`, and `updated`. The default is `name` ascending." schema: $ref: "#/components/schemas/SortListingSourceFieldData" example: name - name: order in: query required: false description: "Sort direction for `sort`. Provide both `sort` and `order` to override the default." schema: type: string enum: - asc - desc example: asc - name: searchAfter in: query required: false description: Opaque ListingSource UUID cursor returned by the previous page. It is omitted when no next page exists. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: size in: query required: false description: Number of ListingSource summaries requested per page. The server clamps values to the inclusive range 1–100. schema: type: integer minimum: 1 maximum: 100 default: 21 example: 21 responses: "200": description: ListingSource summaries returned successfully. headers: Cache-Control: description: Responses are never stored because this is an administrator read. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header. schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/ListingSourceSearchCollectionData" example: items: - listingSourceId: 550e8400-e29b-41d4-a716-446655440000 listingSourceSlugId: antiques-and-more-550e8400-e29b-41d4-a716-446655440000 name: Antiques and More operator: partyId: 550e8400-e29b-41d4-a716-446655440001 partySlugId: antiques-operator-550e8400-e29b-41d4-a716-446655440001 name: Antiques Operator ingestionMethods: - PARTNER_API presentation: url: https://antiques.example/ image: https://antiques.example/logo.png referralConfiguration: type: PARTNERIZE camref: campaign123 created: "2026-01-01T10:00:00Z" updated: "2026-01-02T10:00:00Z" size: 21 searchAfter: 550e8400-e29b-41d4-a716-446655440000 "400": description: Bad request - invalid ListingSource search parameters. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_sort: summary: Invalid sort field value: status: 400 title: Bad Request error: BAD_SORT_VALUE source: field: sort type: QUERY invalid_query: summary: Invalid UUID, ingestion method, page size, or cursor value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: searchAfter type: QUERY "401": description: Unauthorized - invalid or missing bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the ADMIN role. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "500": description: Internal authentication or ListingSource search failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: LISTING_SOURCE_INTERNAL_ERROR "503": description: Authentication, authorization, or ListingSource search data is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: LISTING_SOURCE_TEMPORARILY_UNAVAILABLE /api/v1/admin/parties: post: summary: Create a Party as admin description: | Creates a Party for an authenticated administrator. The request accepts only the Party name and optional phone/email contact information. Party names trim outer Unicode whitespace, reject blank values, and reject values over 255 UTF-8 bytes. The Party slug is generated once from the canonical name and stable Party ID; it remains immutable if the Party is renamed later. operationId: adminCreateParty tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Party creation payload. content: application/json: schema: $ref: "#/components/schemas/CreatePartyData" example: name: Antiques and More phone: "+49 30 123456" email: contact@example.com responses: "201": description: Party created successfully. headers: Location: description: URL of the created admin Party detail resource. schema: type: string format: uri example: /api/v1/admin/parties/550e8400-e29b-41d4-a716-446655440000 Cache-Control: description: Responses are never stored because contact data is returned. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/PartyData" example: partyId: 550e8400-e29b-41d4-a716-446655440000 partySlugId: antiques-and-more-550e8400-e29b-41d4-a716-446655440000 name: Antiques and More contact: phone: "+49 30 123456" email: contact@example.com created: "2026-01-01T10:00:00Z" updated: "2026-01-01T10:00:00Z" "400": description: Bad request - missing, empty, malformed, or invalid Party body. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_name: summary: Invalid Party name value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: name must be nonblank and at most 255 UTF-8 bytes. invalid_contact_email: summary: Invalid Party email value: status: 400 title: Bad Request error: BAD_BODY_VALUE "401": description: Unauthorized - missing or invalid bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the ADMIN role. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "409": description: Conflict - the generated Party slug conflicts with current state. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT detail: Party conflicts with current state. "503": description: Authentication, authorization, or Party persistence is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTY_TEMPORARILY_UNAVAILABLE "500": description: Internal authentication or Party persistence failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PARTY_INTERNAL_ERROR get: summary: Search parties as admin description: | Searches Party records for authenticated administrators. `query` performs a case-insensitive substring search across Party name, phone, and email. `name`, `phone`, and `email` restrict the substring match to one contact/name field. `created` and `updated` accept inclusive RFC3339 ranges using `min` and `max` query members. Results use deterministic cursor pagination. The default sort is `name` ascending; override it by providing both `sort` and `order`. Valid sort fields are `name`, `email`, `phone`, `created`, and `updated`; Party ID is always the final deterministic tie-breaker. Page sizes are clamped to 1–100, with a default of 21. The returned UUID `searchAfter` cursor is omitted on the terminal page and should be sent with the same filters and sort. Responses always use `Cache-Control: no-store` because contact data is returned. operationId: adminSearchParties tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: query in: query required: false description: Optional case-insensitive substring query across Party name, phone, and email. schema: type: string example: antiques - name: name in: query required: false description: Optional case-insensitive substring filter for the Party name. schema: type: string example: antiques - name: phone in: query required: false description: Optional case-insensitive substring filter for the Party phone number. schema: type: string example: "+49 30" - name: email in: query required: false description: Optional case-insensitive substring filter for the Party email address. schema: type: string example: contact@example.com - name: "created[min]" in: query required: false description: Optional inclusive lower bound for the Party creation timestamp (RFC3339). schema: type: string format: date-time example: "2026-01-01T00:00:00Z" - name: "created[max]" in: query required: false description: Optional inclusive upper bound for the Party creation timestamp (RFC3339). schema: type: string format: date-time example: "2026-12-31T23:59:59Z" - name: "updated[min]" in: query required: false description: Optional inclusive lower bound for the Party update timestamp (RFC3339). schema: type: string format: date-time example: "2026-01-01T00:00:00Z" - name: "updated[max]" in: query required: false description: Optional inclusive upper bound for the Party update timestamp (RFC3339). schema: type: string format: date-time example: "2026-12-31T23:59:59Z" - name: sort in: query required: false description: "Sort field. Valid values are `name`, `email`, `phone`, `created`, and `updated`. The default is `name` ascending." schema: $ref: "#/components/schemas/SortPartyFieldData" example: name - name: order in: query required: false description: "Sort direction for `sort`. Provide both `sort` and `order` to override the default." schema: type: string enum: - asc - desc example: asc - name: searchAfter in: query required: false description: Opaque Party UUID cursor returned by the previous page. It is omitted when no next page exists. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: size in: query required: false description: Number of Party summaries requested per page. The server clamps values to the inclusive range 1–100. schema: type: integer minimum: 1 maximum: 100 default: 21 example: 21 responses: "200": description: Party summaries returned successfully. headers: Cache-Control: description: Responses are never stored because this administrator read contains contact data. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/PartyCollectionData" example: items: - partyId: 550e8400-e29b-41d4-a716-446655440000 partySlugId: antiques-and-more-550e8400-e29b-41d4-a716-446655440000 name: Antiques and More contact: phone: "+49 30 123456" email: contact@example.com created: "2026-01-01T10:00:00Z" updated: "2026-01-02T10:00:00Z" size: 21 searchAfter: 550e8400-e29b-41d4-a716-446655440000 "400": description: Bad request - invalid query parameters. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_sort_field: summary: Invalid sort field value: status: 400 title: Bad Request error: BAD_SORT_VALUE source: field: sort type: QUERY invalid_order: summary: Invalid sort order value: status: 400 title: Bad Request error: BAD_ORDER_VALUE source: field: order type: QUERY invalid_search_after: summary: Invalid searchAfter cursor value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: searchAfter type: QUERY detail: "searchAfter must contain a party UUID." invalid_size: summary: Invalid page size value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: size type: QUERY "401": description: Unauthorized - invalid or missing bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the ADMIN role. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "503": description: Authentication, authorization, or Party search data is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTY_TEMPORARILY_UNAVAILABLE "500": description: Internal authentication or Party search failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PARTY_INTERNAL_ERROR /api/v1/admin/parties/{partyId}: patch: summary: Update a Party as admin description: | Updates the name and/or contact information for a Party for an authenticated administrator. Only `name`, `phone`, and `email` are supported. Omitted members remain unchanged; `null` clears `phone` or `email`; `name` is not nullable and `name: null` is rejected. Party names trim outer Unicode whitespace, reject blank values, and reject values over 255 UTF-8 bytes. Renaming a Party never changes its immutable slug. The response is the resulting Party representation and uses `Cache-Control: no-store` because contact data is returned. An empty object is a valid no-op PATCH; an empty HTTP body is invalid. operationId: adminUpdateParty tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: partyId in: path required: true description: Stable Party identifier. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 requestBody: required: true description: Party name/contact patch. Omit unchanged members; null clears phone/email only. content: application/json: schema: $ref: "#/components/schemas/UpdatePartyData" examples: rename: summary: Rename without changing the Party slug value: name: Antiques and More set_contact: summary: Set optional contact values value: phone: "+49 30 123456" email: contact@example.com clear_contact: summary: Clear optional contact values value: phone: null email: null no_op_patch: summary: Leave the Party unchanged value: {} responses: "200": description: Party updated successfully. headers: Cache-Control: description: Responses are never stored because contact data is returned. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/PartyData" example: partyId: 550e8400-e29b-41d4-a716-446655440000 partySlugId: antiques-and-more-550e8400-e29b-41d4-a716-446655440000 name: Antiques and More contact: phone: "+49 30 123456" email: contact@example.com created: "2026-01-01T10:00:00Z" updated: "2026-01-02T10:00:00Z" "400": description: Bad request - invalid Party UUID, empty/malformed body, or invalid Party value. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_party_id: summary: Invalid Party UUID value: status: 400 title: Bad Request error: INVALID_UUID source: field: partyId type: PATH detail: Path parameter 'partyId' must be a UUID. invalid_name: summary: Invalid Party name value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: name must be nonblank and at most 255 UTF-8 bytes. invalid_email: summary: Invalid Party email value: status: 400 title: Bad Request error: BAD_BODY_VALUE nullable_name: summary: Required Party name cannot be cleared value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body field 'name' must not be null. "401": description: Unauthorized - missing or invalid bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the ADMIN role. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: Party was not found. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: PARTY_NOT_FOUND detail: Party was not found. "409": description: Conflict - the Party was changed concurrently. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT detail: Party conflicts with current state. "503": description: Authentication, authorization, or Party persistence is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTY_TEMPORARILY_UNAVAILABLE "500": description: Internal authentication or Party persistence failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PARTY_INTERNAL_ERROR get: summary: Get a Party as admin description: | Gets one Party for an authenticated administrator. `partyId` must be a UUID. The response contains the Party identity, immutable slug, name, optional phone/email contact information, and creation/update timestamps. Responses always use `Cache-Control: no-store` because contact data is returned. operationId: adminGetParty tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: partyId in: path required: true description: Stable Party identifier. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 responses: "200": description: Party returned successfully. headers: Cache-Control: description: Responses are never stored because contact data is returned. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/PartyData" example: partyId: 550e8400-e29b-41d4-a716-446655440000 partySlugId: antiques-and-more-550e8400-e29b-41d4-a716-446655440000 name: Antiques and More contact: phone: "+49 30 123456" email: contact@example.com created: "2026-01-01T10:00:00Z" updated: "2026-01-02T10:00:00Z" "400": description: Bad request - invalid Party UUID. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_UUID source: field: partyId type: PATH detail: Path parameter 'partyId' must be a UUID. "401": description: Unauthorized - missing or invalid bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the ADMIN role. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: Party was not found. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: PARTY_NOT_FOUND detail: Party was not found. "503": description: Authentication or Party persistence is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTY_TEMPORARILY_UNAVAILABLE "500": description: Internal authentication or Party persistence failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PARTY_INTERNAL_ERROR /api/v1/admin/users: get: summary: Search users as admin description: "Searches users across all accounts for authenticated administrators. Text filters use case-insensitive substring matching. `query` searches email, first name, and last name; `email`, `firstName`, and `lastName` restrict the match to one field. `tier` and `role` filters use exact values. Results use deterministic keyset pagination. The default sort is `name` ascending; override it by providing both `sort` and `order`. Every sort uses `userId` as a deterministic tie-breaker. This response always uses `Cache-Control: no-store`. " operationId: adminSearchUsers tags: - User Account security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: query in: query required: false description: Optional case-insensitive substring query across email, first name, and last name. schema: type: string example: ada - name: email in: query required: false description: Optional case-insensitive substring filter for the email address. schema: type: string example: ada@example.com - name: firstName in: query required: false description: Optional case-insensitive substring filter for the first name. schema: type: string example: Ada - name: lastName in: query required: false description: Optional case-insensitive substring filter for the last name. schema: type: string example: Lovelace - name: tier in: query required: false description: Optional filter by one or more user subscription tiers. style: form explode: true schema: type: array items: $ref: "#/components/schemas/UserTierData" uniqueItems: true example: - PRO - ULTIMATE - name: role in: query required: false description: Optional filter by one or more user roles. style: form explode: true schema: type: array items: $ref: "#/components/schemas/UserRoleData" uniqueItems: true example: - ADMIN - name: created[min] in: query required: false description: Optional lower bound for the user's `created` timestamp (RFC3339). schema: type: string format: date-time example: "2026-01-01T00:00:00Z" - name: created[max] in: query required: false description: Optional upper bound for the user's `created` timestamp (RFC3339). schema: type: string format: date-time example: "2026-12-31T23:59:59Z" - name: updated[min] in: query required: false description: Optional lower bound for the user's `updated` timestamp (RFC3339). schema: type: string format: date-time example: "2026-04-01T00:00:00Z" - name: updated[max] in: query required: false description: Optional upper bound for the user's `updated` timestamp (RFC3339). schema: type: string format: date-time example: "2026-04-25T23:59:59Z" - name: sort in: query required: false description: "Field used for ordering results. Valid fields are `name`, `email`, `firstName`, `lastName`, `tier`, `role`, `created`, and `updated`. The default is `name` ascending. To override it, provide both `sort` and `order`. " schema: $ref: "#/components/schemas/SortUserFieldData" example: email - name: order in: query required: false description: "Sort direction for `sort`. Ignored unless `sort` is also provided; when both are absent, results use `name` ascending. " schema: type: string enum: - asc - desc example: asc - name: searchAfter in: query required: false description: "Opaque UUID cursor for keyset pagination. Pass the `searchAfter` string returned by the previous response to fetch the next page. Requests may also provide the legacy JSON array form when its final element is the user UUID. The cursor is valid only with the same filters and sort used for the previous page. " schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: size in: query required: false description: Number of users requested per page. The server clamps values to the inclusive range 1–100. schema: type: integer minimum: 1 maximum: 100 default: 21 example: 21 responses: "200": description: Users returned successfully. headers: Cache-Control: description: Responses are never stored because this is an administrator-only user read. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/UserCollectionData" example: items: - userId: 550e8400-e29b-41d4-a716-446655440000 email: ada.admin@example.com firstName: Ada lastName: Lovelace tier: ULTIMATE role: ADMIN stripeCustomerId: cus_admin_550e8400e29b41d4a716446655440000 - userId: 7f1c2a88-4e18-4c3a-bf0d-6a0fd5e6d901 email: grace.user@example.com firstName: Grace lastName: Hopper tier: PRO role: USER size: 2 searchAfter: 7f1c2a88-4e18-4c3a-bf0d-6a0fd5e6d901 "400": description: Bad request - invalid query parameters. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_query: summary: Invalid query parameter value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE invalid_sort_field: summary: Invalid sort field value: status: 400 title: Bad Request error: BAD_SORT_VALUE source: field: sort sourceType: query detail: "Expected any of: 'name', 'email', 'firstName', 'lastName', 'tier', 'role', 'updated', 'created'. Got: 'invalid'" invalid_order: summary: Invalid sort order value: status: 400 title: Bad Request error: BAD_ORDER_VALUE source: field: order sourceType: query detail: "Expected any of: 'asc', 'desc'. Got: 'invalid'" invalid_search_after: summary: Invalid searchAfter cursor value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: searchAfter sourceType: query detail: "searchAfter must contain a user UUID." invalid_size: summary: Invalid page size value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: size sourceType: query detail: invalid digit found in string "401": description: Unauthorized - invalid or missing bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the `ADMIN` role and `users:read` capability when delegated. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "503": description: Authentication, authorization, or user search data is temporarily unavailable (`AUTH_TEMPORARILY_UNAVAILABLE` or `USER_TEMPORARILY_UNAVAILABLE`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: USER_TEMPORARILY_UNAVAILABLE "500": description: Internal authentication or user search failure (`AUTH_INTERNAL_ERROR` or `USER_INTERNAL_ERROR`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: USER_INTERNAL_ERROR /api/v1/admin/users/{userId}: get: summary: Get a user as admin description: "Retrieves a single user account by user ID for authenticated administrators. Cognito JWTs and Aura Historia access tokens are accepted; delegated access tokens require `users:read`. The response always sends `Cache-Control: no-store`. " operationId: adminGetUser tags: - User Account parameters: - name: userId in: path required: true description: Unique identifier (UUID) of the user to retrieve. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: User returned successfully. headers: Cache-Control: description: Caching directive. Always set to `no-store`. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/AdminUserAccountData" example: userId: 550e8400-e29b-41d4-a716-446655440000 email: ada.admin@example.com firstName: Ada lastName: Lovelace language: en currency: EUR showUnassessedOrSensitiveContent: true tier: ULTIMATE role: ADMIN stripeCustomerId: cus_admin_550e8400e29b41d4a716446655440000 "401": description: Unauthorized - invalid or missing bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the `ADMIN` role and `users:read` capability when delegated. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: User not found. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND detail: User was not found. "500": description: Internal server error. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: USER_INTERNAL_ERROR patch: summary: Update a user as admin description: "Partially updates a user account for authenticated administrators. Cognito JWTs and Aura Historia access tokens are accepted; delegated access tokens require `users:write`. The caller's persisted user role must be `ADMIN`; this authorization is enforced in the service layer. All request fields are optional, but the request body itself must be present and non-empty. Send only one logical change category per request: profile/preferences fields, `role`, or `tier`. An empty JSON object (`{}`) is accepted and returns the existing user unchanged. Demoting the last active administrator is rejected with `409 CONFLICT`. " operationId: adminPatchUser tags: - User Account parameters: - name: userId in: path required: true description: Unique identifier (UUID) of the user to update. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Partial admin-only user update payload. content: application/json: schema: $ref: "#/components/schemas/PatchAdminUserData" examples: update_role: summary: Promote a user value: role: ADMIN update_tier: summary: Upgrade the tier value: tier: ULTIMATE update_profile: summary: Update profile settings value: firstName: Ada lastName: Lovelace language: en currency: EUR showUnassessedOrSensitiveContent: true responses: "200": description: User updated successfully. headers: Cache-Control: description: Responses are never stored because this is an administrator-only user mutation. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/AdminUserAccountData" example: userId: 550e8400-e29b-41d4-a716-446655440000 email: ada.admin@example.com firstName: Ada lastName: Lovelace language: en currency: EUR showUnassessedOrSensitiveContent: true tier: ULTIMATE role: ADMIN stripeCustomerId: cus_admin_550e8400e29b41d4a716446655440000 "400": description: Bad request - missing or invalid request body. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Missing request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_body: summary: Invalid request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE "401": description: Unauthorized - invalid or missing bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the `ADMIN` role; delegated access tokens also require `users:write`. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: This action requires the 'ADMIN' role. "404": description: User not found. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND detail: User with UserId '550e8400-e29b-41d4-a716-446655440000' not found. "409": description: Conflict - the last active administrator cannot be demoted. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT detail: At least one active administrator must remain. "500": description: Internal server error. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR delete: summary: Delete a user as admin description: "Deletes a user account for authenticated administrators. Cognito JWTs and Aura Historia access tokens are accepted; delegated access tokens require `users:write`. The caller's persisted user role must be `ADMIN`; this authorization is enforced in the User service. PostgreSQL synchronously cascades user-owned access tokens, OAuth authorization codes, watchlist entries, saved-search filters and matches, notifications, partnership memberships, and partnership applications. The final active administrator cannot be deleted, including through a self-targeted admin request. " operationId: adminDeleteUser tags: - User Account parameters: - name: userId in: path required: true description: Unique identifier (UUID) of the user to delete. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "204": description: User deleted successfully. headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" "400": description: Bad request - the user ID is not a UUID. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_UUID "401": description: Unauthorized - invalid or missing bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the `ADMIN` role; delegated access tokens also require `users:write`. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: User not found. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND detail: User was not found. "409": description: Conflict - the target state prevents deletion, including removal of the final active administrator. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT detail: At least one active administrator must remain. "503": description: User authorization or persistence is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: USER_TEMPORARILY_UNAVAILABLE "500": description: Internal server error. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: USER_INTERNAL_ERROR /api/v1/admin/users/{userId}/suspension: put: summary: Suspend a user as admin description: | Suspends the explicit target user with an administrator-supplied reason. Cognito JWTs and Aura Historia access tokens are accepted. The caller's persisted user role must be `ADMIN`; delegated Aura Historia access tokens must also have `users:write`. Authorization is enforced in the User service, not only at the HTTP route. `userId` must be a UUID. `reason` is required, must not be empty or whitespace-only, and is limited to 1,000 bytes. It is emitted to structured operational logs, so it must not contain tokens, passwords, credentials, or other secrets. Common credential markers are rejected. Suspension is idempotent: a repeated request returns the target's suspended state. The final active administrator cannot be suspended. Once committed, a suspended user is rejected as `401 INVALID_CREDENTIALS` by all later Cognito JWT and Aura access-token authentication. Every response sends `Cache-Control: no-store`. operationId: adminSuspendUser tags: - User Account parameters: - name: userId in: path required: true description: Unique identifier (UUID) of the user to suspend. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/SuspendUserData" example: reason: Repeated policy violations responses: "200": description: User is suspended, including after an idempotent repeat. headers: Cache-Control: description: Responses are never stored because this is an administrator-only user mutation. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/SuspendUserResponseData" example: userId: 550e8400-e29b-41d4-a716-446655440000 suspended: true "400": description: Invalid target UUID or missing, empty, oversized, or secret-bearing suspension reason. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_user_id: value: status: 400 title: Bad Request error: INVALID_UUID source: field: userId sourceType: path missing_reason: value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Suspension reason is required. invalid_reason: value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Suspension reason is invalid. "401": description: Invalid or missing bearer credentials, including a credential for a suspended user. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: The caller is not an administrator or a delegated credential lacks users:write. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: Target user was not found. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "409": description: The target is the final active administrator. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT detail: At least one active administrator must remain. "503": description: Authentication, authorization, or user persistence is temporarily unavailable. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: authentication: value: status: 503 title: Service Unavailable error: AUTH_TEMPORARILY_UNAVAILABLE suspension: value: status: 503 title: Service Unavailable error: USER_TEMPORARILY_UNAVAILABLE "500": description: Internal authentication or user persistence failure. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: authentication: value: status: 500 title: Internal Server Error error: AUTH_INTERNAL_ERROR suspension: value: status: 500 title: Internal Server Error error: USER_INTERNAL_ERROR delete: summary: Reactivate a user as admin description: | Removes the durable suspension state for the explicit target user. No request body is accepted. Cognito JWTs and Aura Historia access tokens are accepted. The caller's persisted user role must be `ADMIN`; delegated Aura Historia access tokens must also have `users:write`. Authorization is enforced in the User service, not only at the HTTP route. `userId` must be a UUID and the target user must exist. Reactivation is idempotent: a repeated request for an active user returns its active state without another write. The operation changes only suspension state; it preserves the user's profile, tier, role, credentials, and partnership relationships. Once committed, valid existing credentials can authenticate normally again. Every response sends `Cache-Control: no-store`. operationId: adminUnsuspendUser tags: - User Account parameters: - name: userId in: path required: true description: Unique identifier (UUID) of the user to reactivate. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: User is active, including after an idempotent repeat. headers: Cache-Control: description: Responses are never stored because this is an administrator-only user mutation. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/UnsuspendUserResponseData" example: userId: 550e8400-e29b-41d4-a716-446655440000 suspended: false "400": description: Invalid target UUID or a nonempty request body. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_UUID source: field: userId sourceType: path "401": description: Invalid or missing bearer credentials, including a credential for a suspended user. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: The caller is not an administrator or a delegated credential lacks users:write. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: Target user was not found. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "409": description: User reactivation conflicts with concurrent state. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT "503": description: Authentication, authorization, or user persistence is temporarily unavailable. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: USER_TEMPORARILY_UNAVAILABLE "500": description: Internal authentication or user persistence failure. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: USER_INTERNAL_ERROR /api/v1/admin/users/{userId}/sessions/revoke: post: summary: Revoke a user's Cognito sessions as admin description: | Invalidates the explicit target user's Cognito sessions with Cognito global sign-out. No request body is accepted. Cognito JWTs and Aura Historia access tokens are accepted. The caller's persisted user role must be `ADMIN`; delegated Aura Historia access tokens must also have `users:write`. Authorization and the target-user check are enforced in the User service, not only at the HTTP route. The PostgreSQL authorization check completes before the external Cognito call. `userId` must be a UUID and the target user must exist. Cognito global sign-out is safe to retry: it succeeds when there are no active sessions. The operation does not change the Aura user's profile, tier, role, suspension state, Aura access tokens, or partnerships. Every response sends `Cache-Control: no-store`. operationId: adminRevokeUserSessions tags: - User Account parameters: - name: userId in: path required: true description: Unique identifier (UUID) of the user whose Cognito sessions are revoked. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "204": description: Cognito sessions were invalidated, including after a retry with no active sessions. headers: Cache-Control: description: Responses are never stored because this is an administrator-only security operation. schema: type: string example: no-store "400": description: Invalid target UUID. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_UUID source: field: userId sourceType: path "401": description: Invalid or missing bearer credentials. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: The caller is not an administrator or a delegated credential lacks users:write. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: The target user or corresponding Cognito identity was not found. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "503": description: User authorization, persistence, or Cognito is temporarily unavailable. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: USER_TEMPORARILY_UNAVAILABLE "500": description: Internal user-session revocation failure. headers: Cache-Control: schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: USER_INTERNAL_ERROR /api/v1/admin/users/{userId}/access-tokens: get: summary: List a user's Aura Historia access-token metadata as admin description: | Lists non-secret Aura Historia access-token metadata for the explicit target user. Cognito JWTs and Aura Historia access tokens are accepted. The caller's persisted user role must be `ADMIN`; delegated Aura Historia access tokens must also have `access-tokens:read`. Authorization is enforced in the User service, not only at the HTTP route. `userId` must be a UUID and the target user must exist. Expired and current tokens are included. Results use bounded keyset pagination in fixed `created ASC, access-token UUID ASC` order. Page sizes are clamped to 1–100, with a default of 21. `searchAfter` is a JSON-encoded `[created RFC3339 timestamp, access-token UUID]` cursor and is omitted on the terminal page. The cursor's creation timestamp is used only for continuation and is not returned as item metadata. Raw token values, token hashes, and masked token values are never returned. operationId: adminListUserAccessTokens tags: - User Account parameters: - name: userId in: path required: true description: Unique identifier (UUID) of the user whose token metadata is listed. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: size in: query required: false description: Requested page size. The server clamps it to 1–100; the default is 21. schema: type: integer format: int64 minimum: 1 maximum: 100 example: 21 - name: searchAfter in: query required: false description: JSON-encoded `[created RFC3339 timestamp, access-token UUID]` cursor from the previous page. schema: type: string example: '["2026-09-04T12:00:00Z","01970f22-2bf0-7000-8000-000000000001"]' security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: Secret-free access-token metadata for the target user. headers: Cache-Control: description: Responses are never stored because this is an administrator-only credential read. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/AdminAccessTokenCollectionData" example: items: - userId: 550e8400-e29b-41d4-a716-446655440000 accessTokenId: 01970f22-2bf0-7000-8000-000000000001 name: Incident review token scopes: - users:read origin: User expires: "2026-09-30T00:00:00Z" size: 21 searchAfter: - "2026-09-04T12:00:00Z" - 01970f22-2bf0-7000-8000-000000000001 "400": description: Bad request - invalid target user UUID, page size, or search-after cursor. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_user_id: summary: Invalid target user identifier value: status: 400 title: Bad Request error: INVALID_UUID source: field: userId sourceType: path detail: Path parameter 'userId' must be a UUID. invalid_query: summary: Invalid page query value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: searchAfter sourceType: query "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - the caller is not an administrator or a delegated credential lacks access-tokens:read. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: The target user does not exist. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND detail: User was not found. "503": description: Access-token authorization or persistence is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: ACCESS_TOKEN_TEMPORARILY_UNAVAILABLE "500": description: Internal access-token operation failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: ACCESS_TOKEN_INTERNAL_ERROR delete: summary: Revoke all of a user's Aura Historia access tokens as admin description: | Revokes all Aura Historia access tokens owned by the target user in one atomic PostgreSQL transaction. Cognito JWTs and Aura Historia access tokens are accepted. The caller's persisted user role must be `ADMIN`; delegated Aura Historia access tokens must also have `access-tokens:write`. Authorization is enforced in the User service, not only at the HTTP route. `userId` must be a UUID and the target user must exist. An existing user with no tokens and a repeated request both return `204 No Content`; a missing target user returns `404 USER_NOT_FOUND`. Deletion is scoped to the target user, leaves unrelated users' tokens untouched, and committed deletion makes the revoked credentials fail authentication. It never reveals raw token values or hashes. operationId: adminDeleteUserAccessTokens tags: - User Account parameters: - name: userId in: path required: true description: Unique identifier (UUID) of the user whose tokens are revoked. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "204": description: All access tokens revoked, or no tokens were present for the existing target user. headers: Cache-Control: description: Responses are never stored because this is an administrator-only credential mutation. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" "400": description: Bad request - the user ID is not a UUID. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_UUID source: field: userId sourceType: path detail: Path parameter 'userId' must be a UUID. "401": description: Unauthorized - invalid or missing bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - the caller is not an administrator or a delegated credential lacks access-tokens:write. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: The target user does not exist. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND detail: User was not found. "503": description: Access-token authorization or persistence is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: ACCESS_TOKEN_TEMPORARILY_UNAVAILABLE "500": description: Internal access-token operation failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: ACCESS_TOKEN_INTERNAL_ERROR /api/v1/admin/users/{userId}/access-tokens/{accessTokenId}: delete: summary: Revoke a user's Aura Historia access token as admin description: | Revokes one Aura Historia access token for the target user. Cognito JWTs and Aura Historia access tokens are accepted. The caller's persisted user role must be `ADMIN`; delegated Aura Historia access tokens must also have `access-tokens:write`. Authorization is enforced in the User service, not only at the HTTP route. Both `userId` and `accessTokenId` must be UUIDs. The database deletion is scoped by both identifiers, so an access-token ID belonging to another user cannot revoke that user's credential. The operation is idempotent: it returns `204 No Content` when the token was deleted, was already absent, or belongs to another user. It never reveals raw token values or hashes. operationId: adminDeleteUserAccessToken tags: - User Account parameters: - name: userId in: path required: true description: Unique identifier (UUID) of the user whose token is revoked. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: accessTokenId in: path required: true description: Unique identifier (UUID) of the access token to revoke. schema: type: string format: uuid example: 01970f22-2bf0-7000-8000-000000000001 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "204": description: Access token revoked, or already absent for the target user. headers: Cache-Control: description: Responses are never stored because this is an administrator-only credential mutation. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" "400": description: Bad request - the user ID or access-token ID is not a UUID. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_user_id: summary: Invalid target user identifier value: status: 400 title: Bad Request error: INVALID_UUID source: field: userId sourceType: path detail: Path parameter 'userId' must be a UUID. invalid_access_token_id: summary: Invalid access-token identifier value: status: 400 title: Bad Request error: INVALID_UUID source: field: accessTokenId sourceType: path detail: Path parameter 'accessTokenId' must be a UUID. "401": description: Unauthorized - invalid or missing bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - the caller is not an administrator or a delegated credential lacks access-tokens:write. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "503": description: Access-token authorization or persistence is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: ACCESS_TOKEN_TEMPORARILY_UNAVAILABLE "500": description: Internal access-token operation failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: ACCESS_TOKEN_INTERNAL_ERROR /api/v1/me: delete: summary: Delete authenticated user description: "Permanently deletes the authenticated user's account. The deletion is performed synchronously and the access token is immediately invalidated upon success. Cognito JWTs and Aura Historia access tokens are accepted; delegated access tokens require `users:write`. The last active administrator cannot be deleted. " operationId: deleteUser tags: - User Account security: - BearerAuth: [] - AccessTokenAuth: [] responses: "204": description: User deleted successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" "401": description: Unauthorized - invalid or missing bearer credentials content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "404": description: User not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "409": description: Conflict - the last active administrator cannot be deleted content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT detail: At least one active administrator must remain. "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/account: get: summary: Get user account data description: "Retrieves the authenticated user's account information including email, name, language and currency preferences, the `showUnassessedOrSensitiveContent` preference, the user's subscription tier, and the user's role. Requires valid Cognito JWT authentication. " operationId: getUserAccount tags: - User Account security: - BearerAuth: [] responses: "200": description: User account data retrieved successfully headers: Last-Modified: description: When the user account was last updated schema: type: string format: http-date example: Wed, 01 Jan 2024 12:00:00 GMT Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/OwnUserAccountData" example: userId: 550e8400-e29b-41d4-a716-446655440000 email: user@example.com firstName: John lastName: Doe language: en currency: EUR showUnassessedOrSensitiveContent: false tier: FREE role: USER created: "2024-01-01T10:00:00Z" updated: "2024-01-01T12:00:00Z" "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: User not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR patch: summary: Update user account data description: "Updates the authenticated user's account information. All fields in the request body are optional - only provided fields will be updated. The user's `role` is returned in the response but cannot be updated through this endpoint. Returns the updated user account data. Requires valid Cognito JWT authentication. " operationId: updateUserAccount tags: - User Account security: - BearerAuth: [] requestBody: required: true description: "Partial user account update data. All fields are optional - only provided fields will be updated. " content: application/json: schema: $ref: "#/components/schemas/PatchUserAccountData" examples: update_all: summary: Update all fields value: firstName: Jane lastName: Smith language: de currency: USD showUnassessedOrSensitiveContent: true update_name_only: summary: Update name only value: firstName: Jane lastName: Smith update_preferences_only: summary: Update language and currency only value: language: fr currency: GBP update_content_visibility_preference_only: summary: Update content visibility preference only value: showUnassessedOrSensitiveContent: true responses: "200": description: User account updated successfully headers: Last-Modified: description: When the user account was last updated schema: type: string format: http-date example: Wed, 01 Jan 2024 12:30:00 GMT Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/OwnUserAccountData" example: userId: 550e8400-e29b-41d4-a716-446655440000 email: jane.smith@example.com firstName: Jane lastName: Smith language: de currency: USD showUnassessedOrSensitiveContent: true tier: FREE role: USER created: "2024-01-01T10:00:00Z" updated: "2024-01-01T12:30:00Z" "400": description: Bad request - invalid request body content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Missing request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_json: summary: Invalid JSON format value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Invalid JSON format "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: User not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/access-tokens: post: summary: Create an Aura Historia access token description: "Creates a new Aura Historia access token for the authenticated user. The plaintext bearer token is returned only in this create response; later reads return a masked token value. Requires valid Cognito JWT authentication. " operationId: postMyAccessToken tags: - User Account security: - BearerAuth: [] requestBody: required: true description: Access-token creation payload. content: application/json: schema: $ref: "#/components/schemas/PostAccessTokenData" example: name: Partner product sync scope: - product-listings:write expiresAt: "2026-06-30T00:00:00Z" responses: "201": description: Access token created successfully headers: Cache-Control: description: Caching directive. Always set to `no-store`. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/GetAccessTokenData" example: accessTokenId: 01970f22-2bf0-7000-8000-000000000001 name: Partner product sync scope: - product-listings:write token: aurahistoria_abcdefghijk_abcdefghijklmnopqrstuvwxyz1234567 tokenType: BEARER expiresAt: "2026-06-30T00:00:00Z" expiresIn: 2851200 created: "2026-05-28T06:30:00Z" updated: "2026-05-28T06:30:00Z" "400": description: Bad request - missing, empty, or invalid JSON body content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Missing request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_scope: summary: Unsupported access-token scope value value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: "unknown scope `product-listings_write`, expected one of: product-listings:write, users:read, users:write, access-tokens:read, access-tokens:write, search-filters:write, watchlist:read, watchlist:write" "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: User not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR get: summary: List Aura Historia access tokens description: "Lists the authenticated user's non-expired Aura Historia access tokens. The `token` field is masked on this endpoint and does not reveal the plaintext bearer token again. Requires valid Cognito JWT authentication. " operationId: getMyAccessTokens tags: - User Account security: - BearerAuth: [] responses: "200": description: Access tokens retrieved successfully headers: Cache-Control: description: Caching directive. Always set to `no-store`. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: type: array items: $ref: "#/components/schemas/GetAccessTokenData" examples: one_token: summary: One non-expired access token value: - accessTokenId: 01970f22-2bf0-7000-8000-000000000001 name: Partner product sync scope: - product-listings:write token: aurahistoria_abcdefghijk_**** tokenType: BEARER expiresAt: "2026-06-30T00:00:00Z" expiresIn: 2851200 created: "2026-05-28T06:30:00Z" updated: "2026-05-28T06:30:00Z" no_tokens: summary: No non-expired access tokens exist value: [] "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: User not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR patch: summary: Update an Aura Historia access token description: "Updates metadata for one access token owned by the authenticated user. Omitted properties remain unchanged; `expires: null` clears expiry. `name` and `scopes` reject `null`; use `scopes: []` to clear scopes. Requires valid Cognito JWT authentication. " operationId: patchMyAccessToken tags: - User Account security: - BearerAuth: [] requestBody: required: true description: Partial access-token update payload. content: application/json: schema: $ref: "#/components/schemas/PatchAccessTokenData" example: accessTokenId: 01970f22-2bf0-7000-8000-000000000001 name: Renamed partner sync token scopes: - product-listings:write expires: "2026-07-31T00:00:00Z" responses: "200": description: Access token updated successfully headers: Cache-Control: description: Caching directive. Always set to `no-store`. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/GetAccessTokenData" example: accessTokenId: 01970f22-2bf0-7000-8000-000000000001 name: Renamed partner sync token scope: - product-listings:write token: aurahistoria_abcdefghijk_**** tokenType: BEARER expiresAt: "2026-07-31T00:00:00Z" expiresIn: 5529600 created: "2026-05-28T06:30:00Z" updated: "2026-05-28T07:00:00Z" "400": description: Bad request - missing, empty, or invalid JSON body content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Missing request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_access_token_id: summary: Invalid access token identifier in request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: invalid character in UUID "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: Access token not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: ACCESS_TOKEN_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/access-tokens/{accessTokenId}: get: summary: Get one Aura Historia access token description: "Retrieves one non-expired Aura Historia access token owned by the authenticated user. The `token` field is masked on this endpoint and does not reveal the plaintext bearer token again. Requires valid Cognito JWT authentication. " operationId: getMyAccessToken tags: - User Account security: - BearerAuth: [] parameters: - name: accessTokenId in: path required: true description: Unique identifier of the access token schema: type: string format: uuid example: 01970f22-2bf0-7000-8000-000000000001 responses: "200": description: Access token retrieved successfully headers: Cache-Control: description: Caching directive. Always set to `no-store`. schema: type: string example: no-store Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/GetAccessTokenData" example: accessTokenId: 01970f22-2bf0-7000-8000-000000000001 name: Partner product sync scope: - product-listings:write token: aurahistoria_abcdefghijk_**** tokenType: BEARER expiresAt: "2026-06-30T00:00:00Z" expiresIn: 2851200 created: "2026-05-28T06:30:00Z" updated: "2026-05-28T06:30:00Z" "400": description: Bad request - invalid or missing access-token path parameter content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_access_token_id: summary: Missing access-token path parameter value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: accessTokenId sourceType: path detail: Missing field 'accessTokenId'. invalid_access_token_id: summary: Invalid access-token identifier value: status: 400 title: Bad Request error: INVALID_UUID source: field: accessTokenId sourceType: path detail: invalid character in UUID "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: Access token not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: ACCESS_TOKEN_NOT_FOUND "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR delete: summary: Delete an Aura Historia access token description: "Deletes one access token owned by the authenticated user. The token to delete is identified by the required `{accessTokenId}` path parameter. Cognito JWTs and Aura Historia access tokens are accepted; delegated access tokens require `access-tokens:write`. The operation is idempotent: an already-revoked or missing token is a successful no-op and returns `204 No Content`. " operationId: deleteMyAccessToken tags: - User Account security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: accessTokenId in: path required: true description: Unique identifier of the access token to delete schema: type: string format: uuid example: 01970f22-2bf0-7000-8000-000000000001 responses: "204": description: Access token deleted successfully, or already absent for the authenticated user headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" "400": description: Bad request - invalid or missing access-token path parameter content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_access_token_id: summary: Missing access-token path parameter value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: accessTokenId sourceType: path detail: Missing field 'accessTokenId'. invalid_access_token_id: summary: Invalid access-token identifier value: status: 400 title: Bad Request error: INVALID_UUID source: field: accessTokenId sourceType: path detail: invalid character in UUID "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/billing/checkout: post: summary: Create Stripe checkout session description: "Creates a hosted Stripe Checkout session for the authenticated user to start a subscription. The request body selects the desired subscription plan and billing cycle. The backend first creates and persists a Stripe customer for the user, then creates a subscription-mode Checkout session for that customer. Stripe and PostgreSQL are separate systems; an association remains persisted if later session creation fails. This endpoint can only be used when the user does not already have a `stripe_customer_id`. Requires Cognito JWT or Aura Historia access-token authentication. Delegated access tokens require `users:read`. " operationId: postBillingCheckout tags: - Billing security: - BearerAuth: [] requestBody: required: true description: Desired subscription plan and billing cycle for the new Stripe checkout session. content: application/json: schema: $ref: "#/components/schemas/PostBillingCheckoutData" examples: pro_monthly: summary: Create a monthly Pro checkout session value: plan: PRO cycle: MONTHLY ultimate_yearly: summary: Create a yearly Ultimate checkout session value: plan: ULTIMATE cycle: YEARLY responses: "201": description: Checkout session created successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/BillingSessionUrlData" example: url: https://checkout.stripe.com/c/pay/cs_test_123 "400": description: Bad request - request body is missing, empty, malformed JSON, or contains unsupported enum values content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Request body is absent or empty value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_plan: summary: Unsupported billing plan value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: unknown variant `ENTERPRISE`, expected `PRO` or `ULTIMATE` at line 1 column 20 "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: User not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "409": description: Conflict - the authenticated user already has a Stripe customer record content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: STRIPE_CUSTOMER_ALREADY_EXISTS detail: User has already created a Stripe customer; use the customer-portal endpoint instead "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/billing/portal: post: summary: Create Stripe billing portal session description: "Creates a hosted Stripe customer-portal session for the authenticated user. This endpoint does not accept a request body. It can only be used when the user already has a persisted `stripe_customer_id`. Requires Cognito JWT or Aura Historia access-token authentication. Delegated access tokens require `users:read`. " operationId: postBillingPortal tags: - Billing security: - BearerAuth: [] responses: "201": description: Billing portal session created successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/BillingSessionUrlData" example: url: https://billing.stripe.com/p/session/test_123 "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: User not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "422": description: Unprocessable Content - the authenticated user has no Stripe customer record yet content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 422 title: Unprocessable Content error: STRIPE_CUSTOMER_DOES_NOT_EXIST detail: User has never had a Stripe subscription; no customer-portal session can be created "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/billing/manage: post: summary: Create Stripe billing management session description: "Creates a hosted Stripe billing session for the authenticated user through a single entrypoint. - Users with tier `FREE` receive a Stripe Checkout session for the requested `plan` and `cycle`. - Users with tier `PRO` or `ULTIMATE` receive a Stripe customer-portal session instead. The request body is required for all callers and must contain supported billing enum values. For free users, the backend uses the provided `plan` and `cycle` to select the configured Stripe price and creates a Stripe customer first when none exists yet. For paid users, the backend still validates that the body is present and well-formed, but returns a portal session based on the stored Stripe customer rather than purchasing a new plan. Requires Cognito JWT or Aura Historia access-token authentication. Delegated access tokens require `users:read`. " operationId: postBillingManage tags: - Billing security: - BearerAuth: [] requestBody: required: true description: Desired subscription plan and billing cycle. Required for all callers. content: application/json: schema: $ref: "#/components/schemas/PostBillingCheckoutData" examples: pro_monthly: summary: Valid request body for a free user upgrading to Pro monthly value: plan: PRO cycle: MONTHLY ultimate_yearly: summary: Valid request body for a free user upgrading to Ultimate yearly value: plan: ULTIMATE cycle: YEARLY responses: "201": description: Billing management session created successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/BillingSessionUrlData" examples: checkout_session: summary: Free user receives a Stripe Checkout session URL value: url: https://checkout.stripe.com/c/pay/cs_test_manage_free portal_session: summary: Paid user receives a Stripe customer-portal session URL value: url: https://billing.stripe.com/p/session/manage_paid "400": description: Bad request - request body is missing, empty, malformed JSON, or contains unsupported enum values content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Request body is absent or empty value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_plan: summary: Unsupported billing plan value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: unknown variant `ENTERPRISE`, expected `PRO` or `ULTIMATE` at line 1 column 20 "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: User not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: USER_NOT_FOUND "422": description: Unprocessable Content - the authenticated paid user has no Stripe customer record yet content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 422 title: Unprocessable Content error: STRIPE_CUSTOMER_DOES_NOT_EXIST detail: User has never had a Stripe subscription; no customer-portal session can be created "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/watchlist/{productListingId}: delete: summary: Remove product from watchlist description: "Removes a specific product from the authenticated user's watchlist. Returns a 204 No Content response on success. Requires valid Cognito JWT authentication. " operationId: deleteWatchlistProduct tags: - Watchlist parameters: - name: productListingId in: path required: true description: Unique identifier of the product schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] responses: "204": description: Product removed from watchlist successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" "400": description: Bad request - invalid parameters content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_product_id: summary: Missing product ID value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: productListingId sourceType: path invalid_uuid: summary: Invalid product ID format value: status: 400 title: Bad Request error: INVALID_UUID source: field: productListingId sourceType: path detail: Invalid UUID format "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: Watchlist entry not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: WATCHLIST_ENTRY_NOT_FOUND "409": description: Watchlist entry was changed concurrently content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT detail: Watchlist entry was changed concurrently. "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR patch: summary: Update watchlist product settings description: "Updates settings for a specific watchlist product (e.g., toggle notifications or manually activate/deactivate the watchlist entry). Returns the full personalized product data after applying the update. Requires valid Cognito JWT authentication. " operationId: patchWatchlistProduct tags: - Watchlist parameters: - name: productListingId in: path required: true description: Unique identifier of the product schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: language in: query required: false description: "Preferred language for localized content in the response. Defaults to `en` when omitted. " schema: $ref: "#/components/schemas/LanguageData" example: de - name: currency in: query required: false description: Currency for price display in the response. schema: $ref: "#/components/schemas/CurrencyData" example: EUR security: - BearerAuth: [] requestBody: required: true description: Patch object containing fields to update content: application/json: schema: $ref: "#/components/schemas/WatchlistProductPatch" examples: enable_notifications: summary: Enable notifications value: notifications: true disable_notifications: summary: Disable notifications value: notifications: false deactivate_watchlist_entry: summary: Deactivate this watchlist entry manually value: state: INACTIVE_BY_USER reactivate_watchlist_entry: summary: Reactivate this watchlist entry value: state: ACTIVE responses: "200": description: Watchlist product updated successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" content: application/json: schema: $ref: "#/components/schemas/PersonalizedProductListingDetailsData" example: item: productListingId: 550e8400-e29b-41d4-a716-446655440000 productListingTitleSlugId: smartphone-case-a1b2c3 eventId: 550e8400-e29b-41d4-a716-446655440001 source: listingSourceId: 550e8400-e29b-41d4-a716-446655440000 name: Tech Store slugId: tech-store sourceListingId: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 title: text: Smartphone Case language: en price: offer: currency: EUR amount: 2999 estimate: min: currency: EUR amount: 2700 max: currency: EUR amount: 3300 availability: IN_STOCK url: https://tech-store.com/smartphone-case viewUrl: https://tech-store.com/smartphone-case?utm_source=aura_historia&utm_medium=referral images: - url: https://tech-store.com/images/case-1.jpg contentPolicy: decision: ALLOWED created: "2024-01-01T10:00:00Z" updated: "2024-01-01T12:00:00Z" userState: watchlist: watching: true notifications: true contentVisibility: showUnassessedOrSensitiveContent: false notification: unseenNotificationIds: [] "400": description: Bad request - invalid parameters or body content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_product_id: summary: Missing product ID value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: productListingId sourceType: path invalid_uuid: summary: Invalid product ID format value: status: 400 title: Bad Request error: INVALID_UUID source: field: productListingId sourceType: path detail: Invalid UUID format missing_body: summary: Missing request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_body: summary: Invalid request body format value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Invalid JSON format "401": description: Unauthorized - invalid or missing JWT token content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: UNAUTHORIZED "404": description: Watchlist entry or ProductListing not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: watchlist_entry_not_found: value: status: 404 title: Not Found error: WATCHLIST_ENTRY_NOT_FOUND product_listing_not_found: value: status: 404 title: Not Found error: PRODUCT_LISTING_NOT_FOUND "422": description: Unprocessable Entity - watchlist quota exceeded during reactivation content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 422 title: Unprocessable Content error: WATCHLIST_QUOTA_EXCEEDED detail: Exceeded the maximum amount of watchlist entries. There are already 20/20 active watchlist entries occupied. "409": description: Watchlist entry was changed concurrently, or a withdrawn ProductListing cannot be reactivated content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: concurrency_conflict: value: status: 409 title: Conflict error: CONFLICT detail: Watchlist entry was changed concurrently. product_listing_unavailable: value: status: 409 title: Conflict error: PRODUCT_LISTING_UNAVAILABLE "500": description: Internal server error content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/me/notifications: get: summary: List user notifications description: "Retrieves the authenticated user’s canonical notifications in newest-first order. Watchlist price-change notifications preserve each event’s immutable source currency; no FX conversion is applied from the user’s preferences. Product image URLs are presented according to the authenticated user’s `showUnassessedOrSensitiveContent` preference. " operationId: listNotifications tags: - Notifications parameters: - name: language in: query required: false schema: $ref: "#/components/schemas/LanguageData" - name: size in: query required: false schema: type: integer minimum: 1 maximum: 100 default: 21 - name: searchAfter in: query required: false description: Opaque JSON `[created RFC3339 timestamp, notification UUID]` cursor returned by the prior page. schema: type: string security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: Notifications retrieved successfully. headers: Cache-Control: schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/NotificationCollectionData" "400": description: Bad request content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Invalid or missing bearer credential content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Notifications are temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" patch: summary: Update selected notification seen states description: Updates the seen state for the explicit canonical notification IDs in the request body. operationId: updateNotificationsSeen tags: - Notifications security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateNotificationsSeenData" responses: "204": description: Selected notifications updated. "400": description: Bad request content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Invalid or missing bearer credential content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Notifications are temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" delete: summary: Delete all notifications operationId: deleteNotifications tags: - Notifications security: - BearerAuth: [] - AccessTokenAuth: [] responses: "204": description: Notifications deleted. "401": description: Invalid or missing bearer credential content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Notifications are temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" /api/v1/me/notifications/all: patch: summary: Update all notification seen states operationId: updateAllNotificationsSeen tags: - Notifications security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateNotificationSeenData" responses: "204": description: Notifications updated. "400": description: Bad request content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Invalid or missing bearer credential content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Notifications are temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" /api/v1/me/notifications/{notificationId}: patch: summary: Update one notification seen state operationId: updateNotificationSeen tags: - Notifications security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: notificationId in: path required: true schema: type: string format: uuid requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateNotificationSeenData" responses: "204": description: Notification updated. "400": description: Bad request content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Invalid or missing bearer credential content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Notifications are temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: Notification not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" delete: summary: Delete one notification operationId: deleteNotification tags: - Notifications security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: notificationId in: path required: true schema: type: string format: uuid responses: "204": description: Notification deleted. "400": description: Bad request content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Invalid or missing bearer credential content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Notifications are temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: Notification not found content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" /api/v1/newsletter-subscriptions: put: summary: Upsert newsletter subscription description: "Subscribes an email address to the Aura Historia newsletter mailing list. The endpoint accepts anonymous requests. An optional Cognito JWT or Aura Historia access-token bearer credential may be supplied for authenticated calls. When authenticated, omitted optional profile fields (`firstName`, `lastName`, `language`, `currency`) fall back to the authenticated user's stored account values; explicitly provided request values take precedence. " operationId: putNewsletterSubscription tags: - Newsletter security: - BearerAuth: [] - {} requestBody: required: true description: Newsletter subscription data. content: application/json: schema: $ref: "#/components/schemas/PutNewsletterSubscriptionData" examples: minimal: summary: Subscribe with email only value: email: collector@example.com complete: summary: Subscribe with explicit profile values value: email: collector@example.com firstName: Ada lastName: Lovelace language: en currency: EUR responses: "204": description: Newsletter subscription synced successfully headers: Access-Control-Allow-Origin: description: CORS header schema: type: string example: "*" "400": description: "Bad request — the request body is missing, malformed, contains invalid field values, or the newsletter provider rejects the submitted email address as invalid. " content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Missing request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty invalid_body: summary: Invalid request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE invalid_email: summary: Newsletter provider rejected the email address value: status: 400 title: Bad Request error: INVALID_EMAIL "503": description: Newsletter provider temporarily unavailable content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: NEWSLETTER_TEMPORARILY_UNAVAILABLE "500": description: Internal newsletter subscription failure content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: NEWSLETTER_INTERNAL_ERROR /api/v1/admin/listing-sources/{listingSourceId}: get: summary: Get a ListingSource as admin description: | Gets one ListingSource for an authenticated administrator. `listingSourceId` must be a UUID. The response uses the canonical ListingSource detail representation and never exposes provider credentials, webhook secrets, or crawler-local configuration. operationId: adminGetListingSource tags: - ListingSources parameters: - name: listingSourceId in: path required: true schema: type: string format: uuid responses: "200": description: ListingSource returned successfully. content: application/json: schema: $ref: "#/components/schemas/ListingSourceData" "400": description: Bad request - invalid ListingSource UUID. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Unauthorized - missing or invalid bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Forbidden - this endpoint requires the ADMIN role. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: ListingSource was not found. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Internal authentication or ListingSource detail failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Authentication, authorization, or ListingSource detail is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" security: - BearerAuth: [] - AccessTokenAuth: [] patch: summary: Update a ListingSource as admin description: | Updates one ListingSource for an authenticated administrator. `listingSourceId` must be a UUID. Omitted members remain unchanged; `null` clears only `woocommerceWebhookSecret`, `url`, `image`, and `referralConfiguration`. `name` and `ingestionConfiguration` are non-nullable and cannot be cleared. The ingestion configuration methods must match the ListingSource ingestion methods. An empty object is a valid no-op PATCH; an empty HTTP body is invalid. Renaming a ListingSource never changes its immutable slug. The response contains only the stable ListingSource ID and slug. WooCommerce webhook secrets are write-only: they are never returned or logged. operationId: adminUpdateListingSource tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listingSourceId in: path required: true description: Stable ListingSource identifier. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 requestBody: required: true description: ListingSource patch. Omit unchanged members; null clears only documented nullable members. content: application/json: schema: $ref: "#/components/schemas/UpdateListingSourceData" examples: update: summary: Update name, ingestion configuration, and presentation value: name: Antiques and More ingestionConfiguration: - type: WOOCOMMERCE currency: EUR language: en url: https://antiques.example/ image: https://antiques.example/image.jpg referralConfiguration: type: PARTNERIZE camref: campaign123 set_webhook_secret: summary: Set the WooCommerce webhook secret value: woocommerceWebhookSecret: provider-secret clear_values: summary: Clear nullable ListingSource values value: woocommerceWebhookSecret: null url: null image: null referralConfiguration: null no_op_patch: summary: Leave the ListingSource unchanged value: {} responses: "200": description: ListingSource updated successfully. content: application/json: schema: $ref: "#/components/schemas/ListingSourceReferenceData" example: listingSourceId: 550e8400-e29b-41d4-a716-446655440000 listingSourceSlugId: antiques-and-more-550e8400-e29b-41d4-a716-446655440000 "400": description: Bad request - invalid ListingSource UUID, empty/malformed body, invalid values, or ingestion configuration mismatch. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_listing_source_id: summary: Invalid ListingSource UUID value: status: 400 title: Bad Request error: INVALID_UUID source: field: listingSourceId type: PATH detail: Path parameter 'listingSourceId' must be a UUID. invalid_patch: summary: Invalid patch value value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: ListingSource ingestion configuration is invalid. "401": description: Unauthorized - missing or invalid bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the ADMIN role. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: ListingSource was not found. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: LISTING_SOURCE_NOT_FOUND detail: Listing source was not found. "409": description: Conflict - the ListingSource was changed concurrently or conflicts with current provider state. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT detail: Listing source conflicts with current state. "503": description: Authentication, authorization, or ListingSource persistence is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: LISTING_SOURCE_TEMPORARILY_UNAVAILABLE "500": description: Internal authentication or ListingSource update failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: LISTING_SOURCE_INTERNAL_ERROR delete: summary: Delete an unused ListingSource as admin description: | Physically removes a ListingSource only when it has no ProductListings, raw-ingestion streams, approved PartnershipApplication reference, or retained existing-source proposal. The committed transaction explicitly removes its Partnership grants and owned ingestion configuration, including provider/webhook secrets. Enabled `WEB_CRAWL` is not a blocker: every new spider or scraper pass refreshes authoritative ListingSource scope before candidate selection, and a failed refresh skips that pass. Already-running work can race with deletion; the business raw-capture fence discards that missing-source result without recreating state. This operation does not retire or purge a used source. A repeated delete returns `404`. operationId: adminDeleteListingSource tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listingSourceId in: path required: true schema: type: string format: uuid responses: "204": description: ListingSource removed. The response body is empty. headers: Cache-Control: schema: type: string example: no-store "400": description: Invalid ListingSource UUID (`INVALID_UUID`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Missing or invalid bearer credentials. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Administrator authority is required (`FORBIDDEN`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: ListingSource was not found (`LISTING_SOURCE_NOT_FOUND`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "409": description: A protected dependency or concurrent mutation blocks deletion (`CONFLICT`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Invalid persisted state or internal deletion failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Temporary authorization or persistence failure. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" /api/v1/oauth/authorize: get: summary: OAuth2 authorization endpoint description: "Starts the OAuth2 authorization code flow (RFC 6749 §4.1). The authenticated user (resource owner) grants the requesting OAuth client access to their Aura Historia account. PKCE (RFC 7636) with `S256` is required for all requests. On success the endpoint redirects (302) to the `redirect_uri` appending `code` and, if supplied, `state` as query parameters. The single-use authorization code must be exchanged for an access token via `POST /api/v1/oauth/token` before it expires. A delegated caller must have `access-tokens:write` and every requested OAuth scope. " operationId: oauthAuthorize tags: - OAuth security: - BearerAuth: [] parameters: - name: response_type in: query required: true description: OAuth response type. Must be `code`. schema: type: string enum: - code example: code - name: client_id in: query required: true description: UUIDv7 identifier of the registered OAuth client. schema: type: string format: uuid example: 01970f22-2bf0-7000-8000-000000000010 - name: redirect_uri in: query required: true description: Redirect URI registered for the OAuth client. Must exactly match one of the client's registered redirect URIs. schema: type: string format: uri example: https://client.example/callback - name: scope in: query required: false description: Space-separated list of requested scopes. Must be a subset of the client's allowed scopes. schema: type: string example: product-listings:write - name: state in: query required: false description: Opaque client state value. Returned unchanged in the redirect to prevent CSRF attacks. schema: type: string example: xyz - name: code_challenge in: query required: true description: PKCE code challenge. Must be the base64url-encoded SHA256 hash of the `code_verifier` (S256 method). schema: type: string example: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM - name: code_challenge_method in: query required: true description: PKCE challenge method. Must be `S256`. schema: type: string enum: - S256 example: S256 responses: "302": description: "Authorization successful. The user-agent is redirected to `redirect_uri` with the one-time authorization `code` and the original `state` (if supplied) appended as query parameters, e.g. `https://client.example/callback?code=&state=`. " headers: Location: description: Redirect URL containing the authorization `code` and optional `state`. schema: type: string format: uri example: https://client.example/callback?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz Cache-Control: description: Always set to `no-store`. schema: type: string example: no-store "400": description: Bad request — a required query parameter is missing, malformed, or has an unsupported value. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_response_type: summary: Missing response_type query parameter value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: response_type sourceType: query detail: Missing query parameter response_type unsupported_response_type: summary: Unsupported response_type value value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: response_type sourceType: query detail: Unsupported response_type 'token' missing_code_challenge: summary: Missing code_challenge query parameter value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: code_challenge sourceType: query detail: Missing query parameter code_challenge unsupported_code_challenge_method: summary: Unsupported code_challenge_method value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: code_challenge_method sourceType: query detail: Unsupported code_challenge_method 'plain' invalid_scope: summary: Requested scope not allowed for client value: status: 400 title: Bad Request error: OAUTH_INVALID_SCOPE source: field: scope sourceType: query detail: Requested scope is not allowed for client. invalid_redirect_uri: summary: Redirect URI not registered for client value: status: 400 title: Bad Request error: OAUTH_INVALID_REDIRECT_URI source: field: redirect_uri sourceType: query detail: Redirect URI is not registered for client. malformed_redirect_uri: summary: Redirect URI is not a valid absolute URI value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: redirect_uri sourceType: query detail: relative URL without a base invalid_client_id: summary: Invalid OAuth client identifier value: status: 400 title: Bad Request error: INVALID_UUID source: field: client_id sourceType: query detail: invalid character in UUID "401": description: Unauthorized — the Cognito bearer token is missing, invalid, or the OAuth client was not found / credentials are invalid. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_bearer: summary: Missing or invalid bearer token value: status: 401 title: Unauthorized error: UNAUTHORIZED client_not_found: summary: OAuth client not found value: status: 401 title: Unauthorized error: OAUTH_CLIENT_NOT_FOUND detail: OAuth client not found. "500": description: Internal server error. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/oauth/token: post: summary: OAuth2 token endpoint description: "Exchanges a single-use authorization code for an Aura Historia access token (RFC 6749 §4.1.3). The request body must be `application/x-www-form-urlencoded`. Client authentication is performed via `client_id` / `client_secret` form fields. PKCE code verification (RFC 7636) is mandatory: `code_verifier` must produce the `code_challenge` supplied to the authorize endpoint. The issued access token is non-expiring by default (same lifetime as tokens created via `POST /api/v1/me/access-tokens`). Successful responses also include a short-lived `third_party_exchange_code` that can be redeemed once via `GET /api/v1/oauth/tokens/by-third-party-code/{thirdPartyCode}`. The exchange code expires after 60 seconds and is deleted on first use. " operationId: oauthToken tags: - OAuth requestBody: required: true description: Authorization code exchange payload (form-urlencoded). content: application/x-www-form-urlencoded: schema: type: object required: - grant_type - code - redirect_uri - client_id - client_secret - code_verifier properties: grant_type: type: string enum: - authorization_code description: OAuth grant type. Must be `authorization_code`. example: authorization_code code: type: string description: Single-use authorization code received from the authorize redirect. example: SplxlOBeZQQYbYS6WxSbIA redirect_uri: type: string format: uri description: Must exactly match the `redirect_uri` used in the corresponding authorization request. example: https://client.example/callback client_id: type: string format: uuid description: UUIDv7 OAuth client identifier. example: 01970f22-2bf0-7000-8000-000000000010 client_secret: type: string description: OAuth client secret. example: aurahistoria_abcdefghijk_verylongsecretvalue code_verifier: type: string description: PKCE code verifier. The SHA256 hash of this value must match the `code_challenge` from the authorization request. example: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk responses: "200": description: Access token issued successfully. headers: Cache-Control: description: Always set to `no-store`. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/OAuthTokenResponseData" example: access_token: aurahistoria_abcdefghijk_verylongtokenvalue token_type: BEARER expires_in: null scope: product-listings:write third_party_exchange_code: 01970f22-2bf0-7000-8000-000000000099 "400": description: Bad request — a required form field is missing, `client_id` is not a valid UUID, `redirect_uri` is malformed, or an authorization code error occurred (code not found, expired, mismatched client or redirect URI, or invalid PKCE verifier). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_grant_type: summary: Missing grant_type form field value: status: 400 title: Bad Request error: BAD_BODY_VALUE source: field: grant_type sourceType: body detail: Missing form field grant_type unsupported_grant_type: summary: Unsupported grant_type value: status: 400 title: Bad Request error: BAD_BODY_VALUE source: field: grant_type sourceType: body detail: Unsupported grant_type 'client_credentials' code_not_found: summary: Authorization code not found or already used value: status: 400 title: Bad Request error: OAUTH_AUTHORIZATION_CODE_NOT_FOUND detail: Authorization code not found. code_expired: summary: Authorization code expired value: status: 400 title: Bad Request error: OAUTH_AUTHORIZATION_CODE_EXPIRED detail: Authorization code expired. code_client_mismatch: summary: Authorization code belongs to a different OAuth client value: status: 400 title: Bad Request error: OAUTH_AUTHORIZATION_CODE_CLIENT_MISMATCH detail: Authorization code does not belong to client. redirect_uri_mismatch: summary: Redirect URI does not match the authorization request value: status: 400 title: Bad Request error: OAUTH_AUTHORIZATION_REDIRECT_URI_MISMATCH detail: Authorization code redirect_uri mismatch. invalid_code_verifier: summary: PKCE code verifier does not match value: status: 400 title: Bad Request error: OAUTH_INVALID_CODE_VERIFIER detail: PKCE code_verifier did not match code_challenge. malformed_redirect_uri: summary: Redirect URI is not a valid absolute URI value: status: 400 title: Bad Request error: BAD_BODY_VALUE source: field: redirect_uri sourceType: body detail: relative URL without a base invalid_client_id: summary: Invalid OAuth client identifier value: status: 400 title: Bad Request error: INVALID_UUID source: field: client_id sourceType: body detail: invalid character in UUID "401": description: Unauthorized — invalid OAuth client credentials or unknown OAuth client. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_client_secret: summary: Invalid client secret value: status: 401 title: Unauthorized error: INVALID_OAUTH_CLIENT_SECRET detail: Invalid OAuth client secret. client_not_found: summary: OAuth client not found value: status: 401 title: Unauthorized error: OAUTH_CLIENT_NOT_FOUND detail: OAuth client not found. "500": description: Internal server error. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/oauth/tokens/by-third-party-code/{thirdPartyCode}: get: summary: Exchange OAuth token by third-party code description: "Exchanges a one-time `third_party_exchange_code` returned by `POST /api/v1/oauth/token` for the same Aura Historia access token. No client credentials or bearer token are required. The exchange code expires after 60 seconds and is deleted whether the exchange succeeds or fails due to expiry. " operationId: oauthTokenByThirdPartyCode tags: - OAuth parameters: - name: thirdPartyCode in: path required: true description: UUIDv7 one-time exchange code returned by the OAuth token endpoint. schema: type: string format: uuid example: 01970f22-2bf0-7000-8000-000000000099 responses: "200": description: Access token returned successfully for a valid third-party exchange code. headers: Cache-Control: description: Always set to `no-store`. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/OAuthTokenResponseData" example: access_token: aurahistoria_abcdefghijk_verylongtokenvalue token_type: BEARER expires_in: null scope: product-listings:write "400": description: "Bad request — the path parameter is missing/invalid, or the exchange code was not found or already expired. The backend currently uses the same `OAUTH_THIRD_PARTY_EXCHANGE_CODE_NOT_FOUND` error code for both unknown and expired exchange codes; the `detail` text distinguishes the two cases. " content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_third_party_code: summary: Missing third-party exchange code path parameter value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: thirdPartyCode sourceType: path detail: Missing path parameter thirdPartyCode invalid_third_party_code: summary: Invalid third-party exchange code value: status: 400 title: Bad Request error: INVALID_UUID source: field: thirdPartyCode sourceType: path detail: invalid character in UUID exchange_code_not_found: summary: Third-party exchange code not found value: status: 400 title: Bad Request error: OAUTH_THIRD_PARTY_EXCHANGE_CODE_NOT_FOUND detail: Third-party exchange code not found. exchange_code_expired: summary: Third-party exchange code expired value: status: 400 title: Bad Request error: OAUTH_THIRD_PARTY_EXCHANGE_CODE_NOT_FOUND detail: Third-party exchange code expired. "500": description: Internal server error. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/oauth/revoke: post: summary: OAuth2 token revocation endpoint description: "Revokes an Aura Historia access token that was issued through the OAuth authorization code flow (RFC 7009). The request body must be `application/x-www-form-urlencoded`. Client authentication is performed via `client_id` / `client_secret` form fields. After revocation the token is immediately inactive and introspection will return `active: false`. Attempting to revoke an unknown or already-revoked token is not an error. " operationId: oauthRevoke tags: - OAuth requestBody: required: true description: Token revocation payload (form-urlencoded). content: application/x-www-form-urlencoded: schema: type: object required: - token - client_id - client_secret properties: token: type: string description: The access token to revoke. example: aurahistoria_abcdefghijk_verylongtokenvalue client_id: type: string format: uuid description: UUIDv7 OAuth client identifier. example: 01970f22-2bf0-7000-8000-000000000010 client_secret: type: string description: OAuth client secret. example: aurahistoria_abcdefghijk_verylongsecretvalue responses: "200": description: Token revoked successfully (or token was unknown/already inactive). Empty response body. headers: Cache-Control: description: Always set to `no-store`. schema: type: string example: no-store "400": description: Bad request — a required form field is missing, `client_id` is not a valid UUID, or the token value is invalid. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_token: summary: Missing token form field value: status: 400 title: Bad Request error: BAD_BODY_VALUE source: field: token sourceType: body detail: Missing form field token invalid_client_id: summary: Invalid OAuth client identifier value: status: 400 title: Bad Request error: INVALID_UUID source: field: client_id sourceType: body detail: invalid character in UUID "401": description: Unauthorized — invalid OAuth client credentials or unknown OAuth client. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_client_secret: summary: Invalid client secret value: status: 401 title: Unauthorized error: INVALID_OAUTH_CLIENT_SECRET detail: Invalid OAuth client secret. client_not_found: summary: OAuth client not found value: status: 401 title: Unauthorized error: OAUTH_CLIENT_NOT_FOUND detail: OAuth client not found. "500": description: Internal server error. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/oauth/introspect: post: summary: OAuth2 token introspection endpoint description: "Returns metadata about an Aura Historia access token (RFC 7662). The request body must be `application/x-www-form-urlencoded`. Client authentication is performed via `client_id` / `client_secret` form fields. If the token is unknown, expired, or revoked, `active` is `false` and all other fields are omitted. Otherwise `active` is `true` and the available token metadata is populated. " operationId: oauthIntrospect tags: - OAuth requestBody: required: true description: Token introspection payload (form-urlencoded). content: application/x-www-form-urlencoded: schema: type: object required: - token - client_id - client_secret properties: token: type: string description: The access token to introspect. example: aurahistoria_abcdefghijk_verylongtokenvalue client_id: type: string format: uuid description: UUIDv7 OAuth client identifier. example: 01970f22-2bf0-7000-8000-000000000010 client_secret: type: string description: OAuth client secret. example: aurahistoria_abcdefghijk_verylongsecretvalue responses: "200": description: "Introspection result. `active: false` means the token is unknown, expired, or revoked." headers: Cache-Control: description: Always set to `no-store`. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/OAuthIntrospectionResponseData" examples: active: summary: Active token value: active: true scope: product-listings:write client_id: 01970f22-2bf0-7000-8000-000000000010 sub: 0196580c-e4ca-723f-a7e0-1a73588380f1 token_type: Bearer exp: 1780000000 iat: 1748400000 inactive: summary: Inactive / unknown token value: active: false "400": description: Bad request — a required form field is missing, `client_id` is not a valid UUID, or the token value is invalid. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_token: summary: Missing token form field value: status: 400 title: Bad Request error: BAD_BODY_VALUE source: field: token sourceType: body detail: Missing form field token invalid_client_id: summary: Invalid OAuth client identifier value: status: 400 title: Bad Request error: INVALID_UUID source: field: client_id sourceType: body detail: invalid character in UUID "401": description: Unauthorized — invalid OAuth client credentials or unknown OAuth client. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_client_secret: summary: Invalid client secret value: status: 401 title: Unauthorized error: INVALID_OAUTH_CLIENT_SECRET detail: Invalid OAuth client secret. client_not_found: summary: OAuth client not found value: status: 401 title: Unauthorized error: OAUTH_CLIENT_NOT_FOUND detail: OAuth client not found. "500": description: Internal server error. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: INTERNAL_SERVER_ERROR /api/v1/admin/oauth-clients: get: summary: List OAuth client metadata for administration description: | Lists registered OAuth client metadata for authenticated administrators. User and delegated-user principals must have the persisted `ADMIN` business role. Delegated Aura Historia access-token callers must also have the `access-tokens:read` capability. The OAuth protocol routes remain under `/api/v1/oauth`. Results use bounded keyset pagination in fixed `created` ascending, then OAuth client UUID ascending order. Page sizes are clamped to 1–100, with a default of 21. The `searchAfter` value is a JSON-encoded `[created RFC3339 timestamp, client UUID]` cursor; it is omitted on the terminal page and must be sent with the same filters. `clientId` is an exact UUID filter. `name` is a case-insensitive substring filter. The response contains no plaintext client secret, secret hash, or masked secret value. Every success and error response uses `Cache-Control: no-store`. operationId: adminListOAuthClients tags: - OAuth security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: clientId in: query required: false description: Optional exact UUID filter for one OAuth client. schema: type: string format: uuid example: 01970f22-2bf0-7000-8000-000000000010 - name: name in: query required: false description: Optional case-insensitive substring filter for the OAuth client display name. schema: type: string example: dashboard - name: searchAfter in: query required: false description: JSON-encoded `[created RFC3339 timestamp, client UUID]` cursor returned by the previous page. schema: type: string example: '["2026-09-04T12:00:00Z","01970f22-2bf0-7000-8000-000000000010"]' - name: size in: query required: false description: Number of OAuth client summaries requested per page. The server clamps values to 1–100. schema: type: integer format: int64 minimum: 1 maximum: 100 default: 21 example: 21 responses: "200": description: OAuth client summaries returned successfully. headers: Cache-Control: description: Responses are never stored because administrator OAuth data is returned. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/OAuthClientAdminCollectionData" example: items: - client_id: 01970f22-2bf0-7000-8000-000000000010 client_name: Dashboard integration tos_uri: https://client.example/tos policy_uri: https://client.example/policy client_uri: https://client.example logo_uri: https://client.example/logo.png redirect_uris: - https://client.example/callback scope: - access-tokens:read client_id_issued_at: 1748539200 size: 21 searchAfter: - "2026-09-04T12:00:00Z" - 01970f22-2bf0-7000-8000-000000000010 "400": description: Bad request — an OAuth client filter, page size, or cursor is invalid. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: searchAfter sourceType: QUERY "401": description: Unauthorized — invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden — the caller is not an administrator or a delegated caller lacks `access-tokens:read`. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: OAuth client administration requires the ADMIN role; delegated callers also require access-tokens:read. "500": description: Internal authentication, authorization, or OAuth client read failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: OAUTH_INTERNAL_ERROR "503": description: Temporary authentication, authorization, or OAuth client read failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: OAUTH_TEMPORARILY_UNAVAILABLE post: summary: Create OAuth client metadata for administration description: | Creates a new OAuth client metadata record for an authenticated administrator. User and delegated-user principals must have the persisted `ADMIN` business role. Delegated Aura Historia access-token callers must also have the `access-tokens:write` capability. The plaintext `client_secret` is returned only in this create response and is never logged or returned by later reads. Redirect URIs must be non-empty HTTPS URLs without fragments. Requested scopes must be supported OAuth scopes. operationId: adminCreateOAuthClient tags: - OAuth security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: OAuth client metadata creation payload. content: application/json: schema: $ref: "#/components/schemas/OAuthClientMetadataRequestData" example: client_name: Acceptance OAuth client tos_uri: https://client.example/tos policy_uri: https://client.example/policy client_uri: https://client.example logo_uri: https://client.example/logo.png redirect_uris: - https://client.example/callback scope: - product-listings:write responses: "201": description: OAuth client metadata created successfully. headers: Location: description: URL of the created admin OAuth client detail resource. schema: type: string format: uri example: /api/v1/admin/oauth-clients/01970f22-2bf0-7000-8000-000000000010 Cache-Control: description: Always set to `no-store` because the response contains the raw secret. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/OAuthClientMetadataResponseData" example: client_id: 01970f22-2bf0-7000-8000-000000000010 client_secret: aurahistoria_oauth_client_secret_abcdefghijk_abcdefghijklmnopqrstuvwxyz1234567 client_name: Acceptance OAuth client tos_uri: https://client.example/tos policy_uri: https://client.example/policy client_uri: https://client.example logo_uri: https://client.example/logo.png redirect_uris: - https://client.example/callback scope: - product-listings:write client_id_issued_at: 1748539200 "400": description: Bad request — missing, empty, malformed JSON body, invalid redirect URI, or unsupported scope. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: missing_body: summary: Missing request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: Body cannot be empty empty_redirect_uris: summary: Redirect URIs are required value: status: 400 title: Bad Request error: OAUTH_INVALID_CLIENT_METADATA detail: "OAuth client metadata is invalid: OAuth client must have at least one redirect URI" insecure_redirect_uri: summary: Redirect URI must use HTTPS value: status: 400 title: Bad Request error: OAUTH_INVALID_CLIENT_METADATA detail: "OAuth client metadata is invalid: OAuth redirect URI must use HTTPS: http://client.example/callback" "401": description: Unauthorized — invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden — the caller is not an administrator or a delegated caller lacks `access-tokens:write`. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: OAuth client creation requires the ADMIN role; delegated callers also require access-tokens:write. "500": description: Internal authentication or OAuth client creation failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: OAUTH_INTERNAL_ERROR "503": description: Temporary authentication, authorization, or OAuth client persistence failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: OAUTH_TEMPORARILY_UNAVAILABLE /api/v1/admin/oauth-clients/{clientId}: get: summary: Get OAuth client metadata for administration description: | Retrieves one OAuth client metadata record for an authenticated administrator. User and delegated-user principals must have the persisted `ADMIN` business role. Delegated Aura Historia access-token callers must also have the `access-tokens:read` capability. The response contains client metadata, redirect URIs, and scopes, but never a plaintext client secret, secret hash, or masked secret value. Every success and error response uses `Cache-Control: no-store`. operationId: adminGetOAuthClient tags: - OAuth security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: clientId in: path required: true description: UUIDv7 identifier of the OAuth client. schema: type: string format: uuid example: 01970f22-2bf0-7000-8000-000000000010 responses: "200": description: OAuth client metadata retrieved successfully. headers: Cache-Control: description: Responses are never stored because administrator OAuth data is returned. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/OAuthClientAdminData" example: client_id: 01970f22-2bf0-7000-8000-000000000010 client_name: Acceptance OAuth client tos_uri: https://client.example/tos policy_uri: https://client.example/policy client_uri: https://client.example logo_uri: https://client.example/logo.png redirect_uris: - https://client.example/callback scope: - product-listings:write client_id_issued_at: 1748539200 "400": description: Bad request — the OAuth client path parameter is not a valid UUID. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_UUID source: field: clientId sourceType: PATH "401": description: Unauthorized — invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden — the caller is not an administrator or a delegated caller lacks `access-tokens:read`. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: OAuth client administration requires the ADMIN role; delegated callers also require access-tokens:read. "404": description: OAuth client was not found. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: OAUTH_CLIENT_NOT_FOUND detail: OAuth client not found. "500": description: Internal authentication, authorization, or OAuth client read failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: OAUTH_INTERNAL_ERROR "503": description: Temporary authentication, authorization, or OAuth client read failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: OAUTH_TEMPORARILY_UNAVAILABLE patch: summary: Update OAuth client metadata for administration description: | Updates one OAuth client metadata record for an authenticated administrator. Omitted properties leave existing values unchanged; every explicit `null` is invalid because OAuth metadata is non-nullable; and `{}` is a valid no-op. User and delegated-user principals must have the persisted `ADMIN` business role. Delegated Aura Historia access-token callers must also have the `access-tokens:write` capability. Redirect URIs remain non-empty HTTPS URLs without fragments, and requested scopes must be supported OAuth scopes. The response is secret-free. This operation never exposes or rotates the client secret. Every success and error response uses `Cache-Control: no-store`. operationId: adminPatchOAuthClient tags: - OAuth security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: clientId in: path required: true description: UUIDv7 identifier of the OAuth client. schema: type: string format: uuid example: 01970f22-2bf0-7000-8000-000000000010 requestBody: required: true description: Partial OAuth client metadata update payload. content: application/json: schema: $ref: "#/components/schemas/OAuthClientMetadataPatchData" example: client_name: Updated acceptance OAuth client tos_uri: https://client.example/updated-tos policy_uri: https://client.example/updated-policy client_uri: https://updated-client.example logo_uri: https://updated-client.example/logo.png redirect_uris: - https://client.example/updated scope: - access-tokens:read responses: "200": description: OAuth client metadata updated successfully without exposing a secret. headers: Cache-Control: description: Always set to `no-store`. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/OAuthClientAdminData" example: client_id: 01970f22-2bf0-7000-8000-000000000010 client_name: Updated acceptance OAuth client tos_uri: https://client.example/updated-tos policy_uri: https://client.example/updated-policy client_uri: https://updated-client.example logo_uri: https://updated-client.example/logo.png redirect_uris: - https://client.example/updated scope: - access-tokens:read client_id_issued_at: 1748539200 "400": description: Bad request — invalid client ID, empty/malformed body, null patch member, invalid redirect URI, or unsupported scope. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_client_id: summary: Invalid OAuth client identifier value: status: 400 title: Bad Request error: INVALID_UUID source: field: clientId sourceType: PATH insecure_redirect_uri: summary: Redirect URI must use HTTPS value: status: 400 title: Bad Request error: OAUTH_INVALID_CLIENT_METADATA detail: "OAuth client metadata is invalid: OAuth redirect URI must use HTTPS: http://client.example/updated" "401": description: Unauthorized — invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden — the caller is not an administrator or a delegated caller lacks `access-tokens:write`. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: OAuth client administration requires the ADMIN role; delegated callers also require access-tokens:write. "404": description: OAuth client was not found. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: OAUTH_CLIENT_NOT_FOUND "500": description: Internal authentication, authorization, or OAuth client update failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: OAUTH_INTERNAL_ERROR "503": description: Temporary authentication, authorization, or OAuth client update failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: OAUTH_TEMPORARILY_UNAVAILABLE delete: summary: Delete OAuth client metadata for administration description: | Deletes one OAuth client metadata record for an authenticated administrator. User and delegated-user principals must have the persisted `ADMIN` business role. Delegated Aura Historia access-token callers must also have the `access-tokens:write` capability. Deletion atomically invalidates pending authorization codes, OAuth-issued Aura access tokens, and their one-time third-party exchange codes. The OAuth protocol routes remain under `/api/v1/oauth`; this administrative mutation is not a protocol route. operationId: adminDeleteOAuthClient tags: - OAuth security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: clientId in: path required: true description: UUIDv7 identifier of the OAuth client. schema: type: string format: uuid example: 01970f22-2bf0-7000-8000-000000000010 responses: "204": description: OAuth client and all client-issued credentials were deleted successfully. headers: Cache-Control: description: Responses are never stored. schema: type: string example: no-store "400": description: Bad request — the OAuth client path parameter is not a valid UUID. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_UUID source: field: clientId sourceType: PATH "401": description: Unauthorized — invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden — the caller is not an administrator or a delegated caller lacks `access-tokens:write`. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: OAuth client administration requires the ADMIN role; delegated callers also require access-tokens:write. "404": description: OAuth client was not found, including repeated deletion. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: OAUTH_CLIENT_NOT_FOUND "500": description: Internal authentication, authorization, or OAuth client deletion failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: OAUTH_INTERNAL_ERROR "503": description: Temporary authentication, authorization, or OAuth client deletion failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: OAUTH_TEMPORARILY_UNAVAILABLE /api/v1/listing-sources/by-slug/{listingSourceSlugId}: get: summary: Get a ListingSource by slug operationId: getListingSourceBySlug tags: - ListingSources parameters: - name: listingSourceSlugId in: path required: true schema: type: string responses: "200": description: Success content: application/json: schema: $ref: "#/components/schemas/ListingSourceData" "400": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "401": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "403": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "404": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "500": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "503": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" security: - BearerAuth: [] - AccessTokenAuth: [] /api/v1/me/listing-sources: get: summary: List caller-administered ListingSources operationId: getMyListingSources tags: - ListingSources responses: "200": description: Success content: application/json: schema: type: array items: $ref: "#/components/schemas/AdministeredListingSourceData" "401": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "403": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "500": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "503": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" security: - BearerAuth: [] - AccessTokenAuth: [] /api/v1/me/partnership-applications: get: summary: List caller PartnershipApplications operationId: getMyPartnershipApplications tags: - PartnershipApplications responses: "200": description: Success content: application/json: schema: type: array items: $ref: "#/components/schemas/OwnPartnershipApplicationData" "401": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "403": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "500": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "503": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" security: - BearerAuth: [] - AccessTokenAuth: [] post: summary: Submit a PartnershipApplication operationId: postPartnershipApplication tags: - PartnershipApplications requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/SubmitPartnershipApplicationData" responses: "201": description: PartnershipApplication submitted content: application/json: schema: $ref: "#/components/schemas/OwnPartnershipApplicationData" "400": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "401": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "403": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "404": description: "PARTNERSHIP_APPLICATION_LISTING_SOURCE_NOT_FOUND when an existing ListingSource proposal references a missing source" content: application/json: schema: $ref: "#/components/schemas/ApiError" "500": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "503": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" security: - BearerAuth: [] - AccessTokenAuth: [] /api/v1/me/partnership-applications/{partnershipApplicationId}: get: summary: Get a caller PartnershipApplication operationId: getOwnPartnershipApplication tags: - PartnershipApplications parameters: - name: partnershipApplicationId in: path required: true schema: type: string format: uuid responses: "200": description: Success content: application/json: schema: $ref: "#/components/schemas/OwnPartnershipApplicationData" "400": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "401": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "403": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "404": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "500": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "503": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" security: - BearerAuth: [] - AccessTokenAuth: [] delete: summary: Withdraw a PartnershipApplication operationId: deleteOwnPartnershipApplication tags: - PartnershipApplications parameters: - name: partnershipApplicationId in: path required: true schema: type: string format: uuid responses: "204": description: PartnershipApplication withdrawn "400": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "401": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "403": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "404": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "409": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "500": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" "503": description: Problem response content: application/json: schema: $ref: "#/components/schemas/ApiError" security: - BearerAuth: [] - AccessTokenAuth: [] /api/v1/admin/overview: get: summary: Get the administrator operational overview description: | Returns a bounded landing-page summary for authenticated administrators. `schemaVersion` is required so clients can select a compatible response decoder. Every counter is read from authoritative PostgreSQL by one aggregate statement; OpenSearch and other rebuildable projections are not used. `listingSources.methodAssignments` counts source-method assignments, not distinct sources, so its values may sum to more than `listingSources.total`. ProductListing availability counts include only `ACTIVE` listings; `activeWithoutAvailability` identifies active rows without a current source availability assertion. Responses contain no PII, secrets, or embedded collections and always use `Cache-Control: no-store`. operationId: getAdminOverview tags: - Administration security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: Authoritative operational counters returned successfully. headers: Cache-Control: description: Administrator overview responses are never stored. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/AdminOverviewData" "401": description: Missing or invalid bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: The caller does not have the persisted ADMIN role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "500": description: An invalid authoritative read model or internal overview failure occurred. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: ADMIN_OVERVIEW_INTERNAL_ERROR "503": description: PostgreSQL or persisted administrator authorization is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: ADMIN_OVERVIEW_TEMPORARILY_UNAVAILABLE /api/v1/admin/partnerships: get: summary: List Partnerships for administration description: | Lists active Partnerships for authenticated administrators. Results use bounded keyset cursor pagination in fixed `created` descending, then partnership UUID descending order. Page sizes are clamped to 1–100, with a default of 21. The returned `searchAfter` value is a JSON-encoded `[created RFC3339 timestamp, partnership UUID]` cursor; it is omitted on the terminal page and should be sent with the same filters for the next page. Optional `partyId`, `memberUserId`, and `listingSourceId` filters are exact UUID matches and are combined when supplied. Each result is a safe summary containing the Partnership ID, Party ID/immutable slug/name, member count, ListingSource-grant count, and creation/update timestamps. Member identities, grant identities, Party contact data, persistence versions, provider credentials, webhook secrets, and crawler-local configuration are never returned. Responses always use `Cache-Control: no-store`. operationId: adminSearchPartnerships tags: - Partnerships security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: partyId in: query required: false description: Optional exact Party UUID filter. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: memberUserId in: query required: false description: Optional exact member user UUID filter. schema: type: string format: uuid example: 660e8400-e29b-41d4-a716-446655440000 - name: listingSourceId in: query required: false description: Optional exact ListingSource grant UUID filter. schema: type: string format: uuid example: 770e8400-e29b-41d4-a716-446655440000 - name: searchAfter in: query required: false description: JSON-encoded `[created RFC3339 timestamp, partnership UUID]` cursor returned by the previous page. schema: type: string example: '["2026-09-04T12:00:00Z","770e8400-e29b-41d4-a716-446655440000"]' - name: size in: query required: false description: Number of Partnership summaries requested per page. The server clamps values to 1–100. schema: type: integer format: int64 minimum: 1 maximum: 100 default: 21 example: 21 responses: "200": description: Partnership summaries returned successfully. headers: Cache-Control: description: Responses are never stored because this is an administrator-only read. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/AdminPartnershipCollectionData" example: items: - partnershipId: 770e8400-e29b-41d4-a716-446655440000 party: partyId: 550e8400-e29b-41d4-a716-446655440000 partySlugId: safe-party name: Safe Party memberCount: 2 listingSourceGrantCount: 3 created: "2026-09-04T12:00:00Z" updated: "2026-09-04T13:00:00Z" size: 21 searchAfter: - "2026-09-04T12:00:00Z" - 770e8400-e29b-41d4-a716-446655440000 "400": description: Bad request - invalid Partnership query parameter. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the `ADMIN` role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "500": description: Internal authentication or Partnership search failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PARTNERSHIP_INTERNAL_ERROR "503": description: Authentication, admin authorization, or Partnership search is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTNERSHIP_TEMPORARILY_UNAVAILABLE /api/v1/admin/partnerships/{partnershipId}: get: summary: Get a Partnership for administration description: | Returns one established Partnership for an authenticated administrator. The response includes the Partnership identity, its Party reference, current member user references, and current ListingSource grant references. Member and grant references are ordered by UUID and each array is bounded to at most 100 entries. `memberCount` and `listingSourceGrantCount` are complete counts, including references beyond the bounded arrays. Missing Partnerships return `PARTNERSHIP_NOT_FOUND`. All success and error responses use `Cache-Control: no-store` because member user identifiers are returned. operationId: adminGetPartnership tags: - Partnerships security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: partnershipId in: path required: true description: Stable Partnership UUID. schema: type: string format: uuid example: 770e8400-e29b-41d4-a716-446655440000 responses: "200": description: Partnership details returned successfully. headers: Cache-Control: description: Responses are never stored because member user identifiers are returned. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/AdminPartnershipDetailsData" example: partnershipId: 770e8400-e29b-41d4-a716-446655440000 party: partyId: 550e8400-e29b-41d4-a716-446655440000 partySlugId: safe-party name: Safe Party memberUserIds: - 660e8400-e29b-41d4-a716-446655440000 - 660e8400-e29b-41d4-a716-446655440001 listingSourceIds: - 990e8400-e29b-41d4-a716-446655440000 - 990e8400-e29b-41d4-a716-446655440001 memberCount: 2 listingSourceGrantCount: 2 created: "2026-09-04T12:00:00Z" updated: "2026-09-04T13:00:00Z" "400": description: Bad request - invalid Partnership UUID. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_UUID source: field: partnershipId sourceType: path detail: Path parameter 'partnershipId' must be a UUID. "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the `ADMIN` role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN "404": description: Partnership not found. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: PARTNERSHIP_NOT_FOUND "500": description: Internal authentication or Partnership detail failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PARTNERSHIP_INTERNAL_ERROR "503": description: Authentication, admin authorization, or Partnership detail is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTNERSHIP_TEMPORARILY_UNAVAILABLE delete: summary: Dissolve a Partnership description: | Semantically deletes a Partnership for an authenticated administrator. The Partnership row remains as a `DISSOLVED` historical reference for approved PartnershipApplications, while all current membership and ListingSource-grant rows are removed in the same PostgreSQL transaction. ListingSources, Parties, users, ProductListings, and applications are not deleted. Repeating a successful dissolution is an idempotent `204` no-op. No user can retain Partnership authorization after the transaction commits. All responses use `Cache-Control: no-store`. operationId: adminDissolvePartnership tags: - Partnerships security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: partnershipId in: path required: true description: Stable Partnership UUID. schema: type: string format: uuid responses: "204": description: Partnership dissolved, or already dissolved. headers: Cache-Control: description: Responses are never stored. schema: type: string example: no-store "400": description: Bad request - invalid Partnership UUID (`INVALID_UUID`). "401": description: Unauthorized - invalid or missing bearer credentials (`INVALID_CREDENTIALS`). "403": description: Forbidden - this endpoint requires the `ADMIN` role (`FORBIDDEN`). "404": description: Partnership not found. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 404 title: Not Found error: PARTNERSHIP_NOT_FOUND "409": description: Partnership was changed concurrently. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT "500": description: Internal Partnership dissolution failure (`PARTNERSHIP_INTERNAL_ERROR`). "503": description: Authentication, admin authorization, or Partnership dissolution is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTNERSHIP_TEMPORARILY_UNAVAILABLE /api/v1/admin/partnerships/{partnershipId}/listing-source-grants/{listingSourceId}: put: summary: Grant a ListingSource to a Partnership description: | Idempotently grants an existing ListingSource to an existing Partnership for an authenticated administrator. The Partnership and ListingSource must belong to the same Party; a mismatched Party is rejected with `409 CONFLICT`. An existing grant is a successful no-op. The operation is committed in one PostgreSQL transaction and returns no response body. All success and error responses use `Cache-Control: no-store`. operationId: adminGrantPartnershipListingSource tags: - Partnerships security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: partnershipId in: path required: true description: Stable Partnership UUID. schema: type: string format: uuid example: 770e8400-e29b-41d4-a716-446655440000 - name: listingSourceId in: path required: true description: Existing ListingSource UUID to grant to the Partnership. schema: type: string format: uuid example: 990e8400-e29b-41d4-a716-446655440000 responses: "204": description: ListingSource grant created, or ListingSource grant already existed. headers: Cache-Control: description: Responses are never stored because this is an administrator-only mutation. schema: type: string example: no-store "400": description: Bad request - invalid Partnership or ListingSource UUID. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalidPartnershipId: value: status: 400 title: Bad Request error: INVALID_UUID source: field: partnershipId type: PATH detail: Path parameter 'partnershipId' must be a UUID. invalidListingSourceId: value: status: 400 title: Bad Request error: INVALID_UUID source: field: listingSourceId type: PATH detail: Path parameter 'listingSourceId' must be a UUID. "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the `ADMIN` role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: Partnership or ListingSource not found. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: partnershipNotFound: value: status: 404 title: Not Found error: PARTNERSHIP_NOT_FOUND detail: Partnership was not found. listingSourceNotFound: value: status: 404 title: Not Found error: LISTING_SOURCE_NOT_FOUND detail: Listing source was not found. "409": description: Partnership and ListingSource belong to different Parties. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 409 title: Conflict error: CONFLICT detail: Partnership and ListingSource belong to different Parties. "500": description: Internal Partnership ListingSource grant failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PARTNERSHIP_INTERNAL_ERROR "503": description: Authentication, admin authorization, transaction, or Partnership ListingSource grant persistence is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTNERSHIP_TEMPORARILY_UNAVAILABLE delete: summary: Revoke a ListingSource from a Partnership description: | Idempotently removes an existing ListingSource grant from an existing Partnership for an authenticated administrator. Only the targeted Partnership/ListingSource join row is removed; the Partnership, ListingSource, members, and historical PartnershipApplications are preserved. An absent grant is a successful no-op. The operation is committed in one PostgreSQL transaction and returns no response body. All success and error responses use `Cache-Control: no-store`. operationId: adminRevokePartnershipListingSource tags: - Partnerships security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: partnershipId in: path required: true description: Stable Partnership UUID. schema: type: string format: uuid example: 770e8400-e29b-41d4-a716-446655440000 - name: listingSourceId in: path required: true description: Existing ListingSource UUID whose Partnership grant should be revoked. schema: type: string format: uuid example: 990e8400-e29b-41d4-a716-446655440000 responses: "204": description: ListingSource grant removed, or ListingSource grant was already absent. headers: Cache-Control: description: Responses are never stored because this is an administrator-only mutation. schema: type: string example: no-store "400": description: Bad request - invalid Partnership or ListingSource UUID. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalidPartnershipId: value: status: 400 title: Bad Request error: INVALID_UUID source: field: partnershipId type: PATH detail: Path parameter 'partnershipId' must be a UUID. invalidListingSourceId: value: status: 400 title: Bad Request error: INVALID_UUID source: field: listingSourceId type: PATH detail: Path parameter 'listingSourceId' must be a UUID. "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the `ADMIN` role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: Partnership or ListingSource not found. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: partnershipNotFound: value: status: 404 title: Not Found error: PARTNERSHIP_NOT_FOUND detail: Partnership was not found. listingSourceNotFound: value: status: 404 title: Not Found error: LISTING_SOURCE_NOT_FOUND detail: Listing source was not found. "500": description: Internal Partnership ListingSource grant revocation failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PARTNERSHIP_INTERNAL_ERROR "503": description: Authentication, admin authorization, transaction, or Partnership ListingSource grant revocation persistence is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTNERSHIP_TEMPORARILY_UNAVAILABLE /api/v1/admin/partnerships/{partnershipId}/members/{userId}: put: summary: Grant a user membership in a Partnership description: | Idempotently grants the target user membership in the specified Partnership for an authenticated administrator. The target user and Partnership must already exist. An existing membership is a successful no-op. The operation is committed in one PostgreSQL transaction and returns no response body. All success and error responses use `Cache-Control: no-store`. operationId: adminGrantPartnershipMembership tags: - Partnerships security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: partnershipId in: path required: true description: Stable Partnership UUID. schema: type: string format: uuid example: 770e8400-e29b-41d4-a716-446655440000 - name: userId in: path required: true description: Existing user UUID to add as a Partnership member. schema: type: string format: uuid example: 660e8400-e29b-41d4-a716-446655440000 responses: "204": description: Membership granted, or membership already existed. headers: Cache-Control: description: Responses are never stored because this is an administrator-only mutation. schema: type: string example: no-store "400": description: Bad request - invalid Partnership or user UUID. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalidPartnershipId: value: status: 400 title: Bad Request error: INVALID_UUID source: field: partnershipId type: PATH detail: Path parameter 'partnershipId' must be a UUID. invalidUserId: value: status: 400 title: Bad Request error: INVALID_UUID source: field: userId type: PATH detail: Path parameter 'userId' must be a UUID. "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the `ADMIN` role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: Partnership or target user not found. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: partnershipNotFound: value: status: 404 title: Not Found error: PARTNERSHIP_NOT_FOUND detail: Partnership was not found. userNotFound: value: status: 404 title: Not Found error: USER_NOT_FOUND detail: User was not found. "500": description: Internal Partnership membership failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PARTNERSHIP_INTERNAL_ERROR "503": description: Authentication, admin authorization, transaction, or Partnership membership persistence is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTNERSHIP_TEMPORARILY_UNAVAILABLE delete: summary: Revoke a user membership in a Partnership description: | Idempotently removes the target user membership from the specified Partnership for an authenticated administrator. The target user and Partnership must already exist. A missing membership is a successful no-op. Only the membership row is removed; the user, Partnership, ListingSource, and historical PartnershipApplication records are preserved. The operation is committed in one PostgreSQL transaction and returns no response body. All success and error responses use `Cache-Control: no-store`. operationId: adminRevokePartnershipMembership tags: - Partnerships security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: partnershipId in: path required: true description: Stable Partnership UUID. schema: type: string format: uuid example: 770e8400-e29b-41d4-a716-446655440000 - name: userId in: path required: true description: Existing user UUID to remove from the Partnership. schema: type: string format: uuid example: 660e8400-e29b-41d4-a716-446655440000 responses: "204": description: Membership removed, or membership was already absent. headers: Cache-Control: description: Responses are never stored because this is an administrator-only mutation. schema: type: string example: no-store "400": description: Bad request - invalid Partnership or user UUID. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalidPartnershipId: value: status: 400 title: Bad Request error: INVALID_UUID source: field: partnershipId type: PATH detail: Path parameter 'partnershipId' must be a UUID. invalidUserId: value: status: 400 title: Bad Request error: INVALID_UUID source: field: userId type: PATH detail: Path parameter 'userId' must be a UUID. "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "403": description: Forbidden - this endpoint requires the `ADMIN` role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 403 title: Forbidden error: FORBIDDEN detail: Operation is not permitted. "404": description: Partnership or target user not found. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: partnershipNotFound: value: status: 404 title: Not Found error: PARTNERSHIP_NOT_FOUND detail: Partnership was not found. userNotFound: value: status: 404 title: Not Found error: USER_NOT_FOUND detail: User was not found. "500": description: Internal Partnership membership failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: PARTNERSHIP_INTERNAL_ERROR "503": description: Authentication, admin authorization, transaction, or Partnership membership persistence is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: PARTNERSHIP_TEMPORARILY_UNAVAILABLE /api/v1/admin/partnership-applications: get: summary: Search PartnershipApplications for administration description: | Searches PartnershipApplications for authenticated administrators. `state` and `proposalType` accept repeated exact canonical values. `applicantUserId` restricts the applicant. `listingSourceId` matches the approved ListingSource or an existing ListingSource proposed by the application. `created` and `updated` accept inclusive RFC3339 ranges using `min` and `max` query members. Results use deterministic keyset cursor pagination. The default sort is `created` descending; override it by providing both `sort` and `order`. Valid sort fields are `created` and `updated`; PartnershipApplication ID is always the final deterministic tie-breaker. Page sizes are clamped to 1–100, with a default of 21. The returned JSON `[timestamp, application UUID]` `searchAfter` cursor is omitted on the terminal page and must be sent with the same filters and sort. Results contain review-queue summary data only and never expose persistence version values. Responses always use `Cache-Control: no-store`. operationId: adminSearchPartnershipApplications tags: - PartnershipApplications security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: state in: query required: false description: Repeated exact application state filters. Matching any supplied state is allowed. style: form explode: true schema: type: array items: $ref: "#/components/schemas/PartnershipApplicationStateData" example: - SUBMITTED - IN_REVIEW - name: applicantUserId in: query required: false description: Optional applicant user UUID filter. schema: type: string format: uuid example: 550e8400-e29b-41d4-a716-446655440000 - name: proposalType in: query required: false description: Repeated exact proposal type filters. Matching any supplied type is allowed. style: form explode: true schema: type: array items: $ref: "#/components/schemas/PartnershipProposalTypeData" example: - EXISTING_LISTING_SOURCE - name: listingSourceId in: query required: false description: Optional approved or existing-proposal ListingSource UUID filter. schema: type: string format: uuid example: 660e8400-e29b-41d4-a716-446655440000 - name: "created[min]" in: query required: false description: Optional inclusive lower bound for application creation time (RFC3339). schema: type: string format: date-time example: "2026-01-01T00:00:00Z" - name: "created[max]" in: query required: false description: Optional inclusive upper bound for application creation time (RFC3339). schema: type: string format: date-time example: "2026-12-31T23:59:59Z" - name: "updated[min]" in: query required: false description: Optional inclusive lower bound for application update time (RFC3339). schema: type: string format: date-time example: "2026-01-01T00:00:00Z" - name: "updated[max]" in: query required: false description: Optional inclusive upper bound for application update time (RFC3339). schema: type: string format: date-time example: "2026-12-31T23:59:59Z" - name: sort in: query required: false description: Sort field. Valid values are `created` and `updated`. The default is `created` descending. schema: type: string enum: - created - updated example: created - name: order in: query required: false description: Sort direction for `sort`. Provide both `sort` and `order` to override the default. schema: type: string enum: - asc - desc example: desc - name: searchAfter in: query required: false description: Opaque JSON-encoded `[timestamp, application UUID]` cursor returned by the previous page. schema: type: string example: '["2026-09-04T12:00:00Z","770e8400-e29b-41d4-a716-446655440000"]' - name: size in: query required: false description: Number of application summaries requested per page. The server clamps values to 1–100. schema: type: integer minimum: 1 maximum: 100 default: 21 example: 21 responses: "200": description: PartnershipApplication summaries returned successfully. headers: Cache-Control: description: Responses are never stored because administrator review data is returned. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/AdminPartnershipApplicationCollectionData" example: items: - id: 770e8400-e29b-41d4-a716-446655440000 applicantUserId: 550e8400-e29b-41d4-a716-446655440000 state: SUBMITTED proposal: type: EXISTING_LISTING_SOURCE listingSourceId: 660e8400-e29b-41d4-a716-446655440000 approvedPartnershipId: null approvedListingSourceId: null created: "2026-09-04T12:00:00Z" updated: "2026-09-04T12:00:00Z" size: 21 searchAfter: - "2026-09-04T12:00:00Z" - 770e8400-e29b-41d4-a716-446655440000 "400": description: Bad request - invalid query parameters. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_state: summary: Invalid state value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: state type: QUERY invalid_proposal_type: summary: Invalid proposal type value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: proposalType type: QUERY invalid_sort: summary: Invalid sort field value: status: 400 title: Bad Request error: BAD_SORT_VALUE source: field: sort type: QUERY invalid_order: summary: Invalid sort order value: status: 400 title: Bad Request error: BAD_ORDER_VALUE source: field: order type: QUERY invalid_cursor: summary: Invalid searchAfter cursor value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: searchAfter type: QUERY invalid_size: summary: Invalid page size value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: size type: QUERY "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Forbidden - this endpoint requires the ADMIN role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Internal authentication or PartnershipApplication search failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Authentication, authorization, or PartnershipApplication data is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" /api/v1/admin/partnership-applications/{partnershipApplicationId}: get: summary: Get a PartnershipApplication for administration description: "Retrieves a single PartnershipApplication for authenticated administrators. The response includes the applicant, state, proposal, and nullable approval references. All responses send `Cache-Control: no-store`. " operationId: adminGetPartnershipApplication tags: - PartnershipApplications parameters: - name: partnershipApplicationId in: path required: true description: Unique identifier (UUID) of the PartnershipApplication to retrieve. schema: type: string format: uuid example: 770e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: PartnershipApplication returned successfully. headers: Cache-Control: description: Caching directive. Always set to `no-store`. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/AdminPartnershipApplicationData" example: id: 770e8400-e29b-41d4-a716-446655440000 applicantUserId: 550e8400-e29b-41d4-a716-446655440000 state: APPROVED proposal: type: EXISTING_LISTING_SOURCE listingSourceId: 660e8400-e29b-41d4-a716-446655440000 approvedPartnershipId: 880e8400-e29b-41d4-a716-446655440000 approvedListingSourceId: 660e8400-e29b-41d4-a716-446655440000 "400": description: Bad request - invalid PartnershipApplication ID. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Forbidden - this endpoint requires the `ADMIN` role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: PartnershipApplication not found. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Internal server error. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: PartnershipApplication data is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" patch: summary: Mark a PartnershipApplication in review as admin description: | Marks a PartnershipApplication in review for an authenticated administrator. This operation accepts no request body and only performs the domain-authoritative `SUBMITTED` to `IN_REVIEW` transition. It does not accept arbitrary state assignment. The response contains the resulting admin PartnershipApplication representation and responses always use `Cache-Control: no-store`. operationId: adminMarkPartnershipApplicationInReview tags: - PartnershipApplications parameters: - name: partnershipApplicationId in: path required: true description: Unique identifier (UUID) of the PartnershipApplication to mark in review. schema: type: string format: uuid example: 770e8400-e29b-41d4-a716-446655440000 security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: PartnershipApplication marked in review successfully. headers: Cache-Control: description: Caching directive. Always set to `no-store`. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/AdminPartnershipApplicationData" "400": description: Bad request - invalid PartnershipApplication ID. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Forbidden - this endpoint requires the `ADMIN` role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: PartnershipApplication not found. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "409": description: Conflict - the PartnershipApplication is not in the Submitted state or was changed concurrently. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Internal authentication or PartnershipApplication transition failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Authentication, authorization, or PartnershipApplication data is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" /api/v1/admin/partnership-applications/{partnershipApplicationId}/decision: post: summary: Decide a PartnershipApplication as admin description: | Approves or rejects a PartnershipApplication for an authenticated administrator. The request accepts only `APPROVE` or `REJECT`; arbitrary state assignment is not supported. State-changing decisions follow the domain lifecycle and are accepted only from `IN_REVIEW`; terminal replays retain their existing idempotent behavior. Approval atomically creates or finds the Party and Partnership, grants membership and ListingSource access, updates the application, and creates the applicant notification. Rejection preserves the existing application and notification semantics. Responses always use `Cache-Control: no-store`. operationId: adminDecidePartnershipApplication tags: - PartnershipApplications parameters: - name: partnershipApplicationId in: path required: true description: Unique identifier (UUID) of the PartnershipApplication to decide. schema: type: string format: uuid example: 770e8400-e29b-41d4-a716-446655440000 requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/DecidePartnershipApplicationData" security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: PartnershipApplication decided successfully. headers: Cache-Control: description: Caching directive. Always set to `no-store`. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/AdminPartnershipApplicationData" "400": description: Bad request - invalid PartnershipApplication ID or decision body. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Unauthorized - invalid or missing bearer credentials. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Forbidden - this endpoint requires the `ADMIN` role. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: PartnershipApplication or referenced ListingSource not found. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "409": description: Conflict - the PartnershipApplication is not in a decisionable state or was changed concurrently. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Internal authentication or PartnershipApplication decision failure. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Authentication, authorization, or PartnershipApplication data is temporarily unavailable. headers: Cache-Control: description: Error responses are never stored. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" components: securitySchemes: BearerAuth: type: http scheme: bearer bearerFormat: JWT description: "Cognito JWT bearer token. This scheme is used for authenticated user routes and for partner routes that accept Cognito user tokens. " AccessTokenAuth: type: http scheme: bearer bearerFormat: opaque description: "Aura Historia bearer access token created through `/api/v1/me/access-tokens`. Tokens are sent as standard bearer authorization headers. Selected partner routes accept this scheme instead of the removed partner API-key flow. " schemas: ActorData: oneOf: - type: string enum: - SYSTEM description: Backend-initiated actor marker. - type: string format: uuid description: UUID of the user who performed the change. description: "Audit actor encoded as a plain string. Values are either the literal `SYSTEM` or the UUID of the acting user. " example: SYSTEM ProductListingSummaryData: type: object description: Lightweight ProductListing summary for search and similar-listing results. required: - productListingId - eventId - source - sourceListingId - priceValuation - availability - lifecycle - url - viewUrl - images - updated properties: productListingId: type: string format: uuid productListingTitleSlugId: type: string minLength: 8 maxLength: 120 pattern: "^[a-z0-9]+(-[a-z0-9]+)*-[0-9a-f]{6}$" description: Immutable Aura-owned public locator. Omitted when the listing is redacted as hidden. example: museum-cabinet-18-19-century-a1b2c3 eventId: type: string format: uuid source: $ref: "#/components/schemas/ProductListingSourceData" sourceListingId: type: string title: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true displayPrice: allOf: - $ref: "#/components/schemas/PriceData" nullable: true priceValuation: $ref: "#/components/schemas/ProductListingSummaryPriceValuationData" availability: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true lifecycle: $ref: "#/components/schemas/ListingLifecycleData" url: type: string format: uri viewUrl: type: string format: uri images: type: array items: $ref: "#/components/schemas/ProductListingImageData" contentPolicy: allOf: - $ref: "#/components/schemas/ContentPolicyData" nullable: true updated: type: string format: date-time ProductListingSummaryPriceValuationData: oneOf: - type: object required: - type - fxRateId - capturedAt properties: type: type: string enum: - CURRENT fxRateId: type: string format: uuid capturedAt: type: string format: date-time - type: object required: - type - fxRateId - observedAt properties: type: type: string enum: - SALE_OBSERVATION fxRateId: type: string format: uuid observedAt: type: string format: date-time ProductListingDetailsData: type: object description: Canonical ProductListing detail inside a personalized response wrapper. required: - productListingId - eventId - source - sourceListingId - pricing - availability - lifecycle - url - viewUrl - images - auction - created - updated properties: productListingId: type: string format: uuid productListingTitleSlugId: type: string minLength: 8 maxLength: 120 pattern: "^[a-z0-9]+(-[a-z0-9]+)*-[0-9a-f]{6}$" description: Immutable Aura-owned public locator. Omitted when the listing is redacted as hidden. example: museum-cabinet-18-19-century-a1b2c3 eventId: type: string format: uuid source: $ref: "#/components/schemas/ProductListingSourceData" sourceListingId: type: string productTitle: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true productDescription: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true title: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true description: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true pricing: type: object required: - source - display - valuation properties: source: $ref: "#/components/schemas/ProductListingPricingData" display: $ref: "#/components/schemas/ProductListingPricingData" valuation: $ref: "#/components/schemas/ProductListingPricingValuationData" availability: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true lifecycle: $ref: "#/components/schemas/ListingLifecycleData" url: type: string format: uri viewUrl: type: string format: uri images: type: array items: $ref: "#/components/schemas/ProductListingImageData" contentPolicy: allOf: - $ref: "#/components/schemas/ContentPolicyData" nullable: true auction: type: object required: - start - end properties: start: type: string format: date-time nullable: true end: type: string format: date-time nullable: true created: type: string format: date-time updated: type: string format: date-time ProductListingPricingData: type: object properties: price: allOf: - $ref: "#/components/schemas/PriceData" nullable: true priceEstimateMin: allOf: - $ref: "#/components/schemas/PriceData" nullable: true priceEstimateMax: allOf: - $ref: "#/components/schemas/PriceData" nullable: true ProductListingPricingValuationData: oneOf: - type: object required: - type - fxRateId - capturedAt properties: type: type: string enum: - CURRENT fxRateId: type: string format: uuid capturedAt: type: string format: date-time - type: object required: - type - fxRateId - capturedAt - observedAt properties: type: type: string enum: - SALE_OBSERVATION fxRateId: type: string format: uuid capturedAt: type: string format: date-time observedAt: type: string format: date-time PersonalizedProductListingDetailsData: type: object description: Canonical Product detail with optional user-specific state. required: - item properties: item: $ref: "#/components/schemas/ProductListingDetailsData" userState: allOf: - $ref: "#/components/schemas/ProductListingUserStateData" nullable: true description: Present only for a valid user or delegated-user bearer token. PersonalizedProductListingSummaryData: type: object description: Canonical Product summary with optional user-specific state. required: - item properties: item: $ref: "#/components/schemas/ProductListingSummaryData" userState: allOf: - $ref: "#/components/schemas/ProductListingUserStateData" nullable: true description: Present only for a valid user or delegated-user bearer token. ProductListingSearchResultData: type: object description: Cursor-paged canonical Product search response. required: - items - size properties: items: type: array items: $ref: "#/components/schemas/PersonalizedProductListingSummaryData" description: Personalized Product summaries in OpenSearch result order. size: type: integer minimum: 1 maximum: 100 description: Requested page size after server-side clamping. example: 21 total: type: integer minimum: 0 nullable: true description: Total matching product-listings for BM25 search when the backend provides it. Hybrid BM25 plus KNN search omits this value because its fused candidate pool is not a reliable total. example: 127 searchAfter: allOf: - $ref: "#/components/schemas/ProductListingSearchCursorData" nullable: true description: Opaque cursor for the next page. It must be sent back as one JSON-encoded `searchAfter` query value. ProductListingSearchCursorData: type: object required: - fxRateId - searchAfter properties: fxRateId: type: string format: uuid description: Persisted FX snapshot used for active Product filtering and display values in this cursor chain. searchAfter: type: array items: {} description: Internal OpenSearch continuation token. example: fxRateId: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 searchAfter: - 2999 - 550e8400-e29b-41d4-a716-446655440000 PersonalizedProductListingSearchResultData: type: object description: "Paginated collection of personalized product-listings using cursor-based pagination (search-after pattern). Each product may include user-specific state when the request is authenticated. " required: - items - size properties: items: type: array items: $ref: "#/components/schemas/PersonalizedProductListingSummaryData" description: Array of personalized product summaries in the current page size: type: integer minimum: 0 description: Number of product-listings returned in the current page example: 21 total: type: integer minimum: 0 description: Total number of product-listings matching the query (optional, may not always be available) nullable: true example: 127 searchAfter: type: array items: {} description: "Cursor for the next page (JSON value). Present when there are more results. Pass this value as the `searchAfter` query parameter to get the next page. This can be ANY heterogeneous array. " nullable: true example: '[2999, "550e8400-e29b-41d4-a716-446655440000"]' ProductListingUserStateData: type: object description: User-specific state information for a product required: - watchlist - contentVisibility - notification - searchFilter properties: watchlist: $ref: "#/components/schemas/WatchlistUserStateData" contentVisibility: $ref: "#/components/schemas/ContentVisibilityUserStateData" notification: $ref: "#/components/schemas/NotificationUserStateData" searchFilter: $ref: "#/components/schemas/SearchFilterUserStateData" WatchlistUserStateData: type: object description: Watchlist-specific user state for a product required: - watching - notifications properties: watching: type: boolean description: Whether the product is on the user's watchlist example: true notifications: type: boolean description: Whether notifications are enabled for this watchlist product example: false ContentVisibilityUserStateData: type: object description: Stored viewer preference used when presenting unassessed or sensitive listing content. required: - showUnassessedOrSensitiveContent properties: showUnassessedOrSensitiveContent: type: boolean description: Whether unassessed or sensitive content may be shown to this viewer. example: false NotificationUserStateData: type: object description: Unseen notification IDs for this product, ordered newest first. required: - unseenNotificationIds properties: unseenNotificationIds: type: array description: Every unseen notification UUID for this product. Empty when none exist. default: [] items: type: string format: uuid SearchFilterUserStateData: type: object description: "User's saved-search-filter match state for a product. When `matched` is `true`, at least one of the user's saved search filters matched this product. `userSearchFilterId`, `userSearchFilterName`, `matchReason`, and `matchFeedback` are only present when `matched` is `true`. Defaults to `matched: false` when the user has no saved search filters or none of them matched. When `hidden` is `true`, the match exceeds the user's monthly search-filter match quota and the product data is anonymized (nil IDs, placeholder title, unknown enums, nulled timestamps). Only matches ranked beyond the quota (by ascending match creation time) are hidden; the first N matches (where N is the user's tier quota) remain fully visible. " required: - matched - hidden properties: matched: type: boolean description: Whether any of the user's saved search filters matched this product. Defaults to `false`. default: false example: true hidden: type: boolean description: "Whether this match is hidden because the user has exceeded their monthly search-filter match quota. When `true`, the product data in the response is anonymized: the `productListingId` is set to a nil UUID, the title becomes a language-specific placeholder, all other UUID fields are nil, enum fields are set to their unknown variants, optional fields are omitted, and timestamps are set to the Unix epoch. Defaults to `false`. " default: false example: false userSearchFilterId: type: string format: uuid description: "The ID of the saved search filter that matched this product. Present only when `matched` is `true`; omitted otherwise. " example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 userSearchFilterName: type: string description: "The user-defined name of the saved search filter that matched this product. Present only when `matched` is `true`; omitted otherwise. " example: Vintage Art Deco matchReason: type: string description: "A human-readable explanation of why the saved search filter matched this product. Only set for AI-enhanced filters (i.e. those with `search.enhancedSearchDescription`). Present only when `matched` is `true` and an enhanced match reason exists; omitted otherwise. " example: Product matches the vintage Art Deco style described in your search filter. matchFeedback: type: boolean description: "Optional feedback previously recorded by the authenticated user for this saved-search match. Present only when `matched` is `true` and feedback has been explicitly stored for the match. `true` marks the match as relevant, `false` marks it as not relevant. " example: true LocalizedTextData: type: object description: Text content with language information required: - text - language properties: text: type: string description: The text content language: $ref: "#/components/schemas/LanguageData" PriceData: type: object description: Price information with currency required: - currency - amount properties: currency: $ref: "#/components/schemas/CurrencyData" amount: type: integer description: Price amount in minor currency units (e.g., cents for most supported currencies, whole yen for JPY) minimum: 0 example: 2999 ProductListingHistoryEntryData: type: object description: One immutable committed ProductListing domain-history entry. required: - eventType - productListingId - eventId - payload - timestamp properties: eventType: $ref: "#/components/schemas/ProductListingHistoryEntryTypeData" productListingId: type: string format: uuid description: Unique internal ProductListing identifier eventId: type: string format: uuid description: Unique identifier for this committed event payload: $ref: "#/components/schemas/ProductListingHistoryPayloadData" timestamp: type: string format: date-time description: When the event occurred (RFC3339 format) LanguageData: type: string enum: - de - en - fr - es - it - zh - pt - pl - tr - nl - cs - ja - ru - ar description: "Supported language codes (ISO 639-1 canonical values): - de: German (includes de-DE, de-AT, de-CH, de-LU, de-LI) - en: English (includes en-US, en-GB, en-AU, en-CA, en-NZ, and backend-specific accepted alias en_IE) - fr: French (includes fr-FR, fr-CA, fr-BE, fr-CH, fr-LU) - es: Spanish (includes es-ES, es-MX, es-AR, es-CO, es-CL, es-PE, es-VE) - it: Italian (includes it-IT, it-CH) - zh: Chinese (Simplified) (includes zh-CN, zh-Hans) - pt: Portuguese (includes pt-PT, pt-BR) - pl: Polish (includes pl-PL) - tr: Turkish (includes tr-TR) - nl: Dutch (includes nl-NL, nl-BE) - cs: Czech (includes cs-CZ) - ja: Japanese (includes ja-JP) - ru: Russian (includes ru-RU) - ar: Arabic (includes ar-SA, ar-EG, ar-AE) `de`, `en`, `fr`, `es`, and `it` are fully supported localization/translation-target languages. `zh`, `pt`, `pl`, `tr`, `nl`, `cs`, `ja`, `ru`, and `ar` are ingestion-only languages: they can appear in stored/native content and are accepted anywhere `LanguageData` is used, but backend-generated translations and fallback localized strings are not produced in these languages. " default: en example: de CurrencyData: type: string enum: - EUR - GBP - USD - AUD - CAD - NZD - CNY - BRL - PLN - TRY - JPY - CZK - RUB - AED - SAR - HKD - SGD - CHF description: "Supported currencies (ISO 4217 codes): - EUR: Euro - GBP: British Pound - USD: US Dollar - AUD: Australian Dollar - CAD: Canadian Dollar - NZD: New Zealand Dollar - CNY: Chinese Yuan - BRL: Brazilian Real - PLN: Polish Złoty - TRY: Turkish Lira - JPY: Japanese Yen - CZK: Czech Koruna - RUB: Russian Ruble - AED: UAE Dirham - SAR: Saudi Riyal - HKD: Hong Kong Dollar - SGD: Singapore Dollar - CHF: Swiss Franc " default: EUR example: EUR MeasurementUnitData: type: string enum: - METRIC - IMPERIAL description: "User preference for distance/size presentation: - METRIC: Metric units. - IMPERIAL: Imperial units. " default: METRIC example: METRIC ListingAvailabilityData: type: string enum: - AVAILABLE - IN_STOCK - LIMITED_AVAILABILITY - BACK_ORDER - MADE_TO_ORDER - PRE_ORDER - PRE_SALE - UNAVAILABLE - RESERVED - OUT_OF_STOCK - SOLD_OUT description: Current reliable source assertion of listing purchase availability. Omitted or `null` means Aura has no reliable current assertion. example: IN_STOCK ListingOrderabilityData: type: string enum: - ORDERABLE_NOW - ORDERABLE_CONDITIONALLY - NOT_ORDERABLE description: Broad classification derived from a concrete availability assertion; it is not independently stored. example: ORDERABLE_NOW ListingLifecycleData: type: string enum: - ACTIVE - WITHDRAWN description: "Source-catalog membership of a ProductListing: - ACTIVE: The listing is currently offered by its authoritative source. - WITHDRAWN: The listing is retained for history but is no longer offered. " example: ACTIVE AuctionData: type: object description: "Auction time window information for product-listings from auction houses. Contains optional start and end timestamps for scheduled auctions. At least one of the fields (start or end) must be present when this object is included. " properties: start: type: string format: date-time nullable: true description: "Start datetime of the auction window for this product (RFC3339 format). Only present for product-listings from auction houses with scheduled auction start times. Used to indicate when bidding begins or when the item will be auctioned. " example: "2025-05-01T12:00:00Z" end: type: string format: date-time nullable: true description: "End datetime of the auction window for this product (RFC3339 format). Only present for product-listings from auction houses with scheduled auction end times. Used to indicate when bidding ends or when the auction session concludes. " example: "2025-05-10T12:00:00Z" PriceEstimateData: type: object description: "Price estimate range for a product. Contains optional minimum and maximum estimated prices. " properties: min: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Optional minimum estimated price for the product max: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Optional maximum estimated price for the product PricingData: type: object description: "Complete pricing information for a product. Contains both the offer price and optional price estimates. " properties: offer: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: "The actual offer or asking price for the product. This is the price at which the product is currently being sold. " estimate: allOf: - $ref: "#/components/schemas/PriceEstimateData" nullable: true description: "Optional price estimate range. Common for auction items where an estimated value range is provided. " ProductListingImageData: type: object description: Product image after viewer-specific presentation. required: - url properties: url: type: string format: uri nullable: true description: URL to the product image, or `null` when it is not shown to this viewer. example: https://my-listing-source.com/images/product-1.jpg ContentPolicyData: oneOf: - type: object required: - decision properties: decision: type: string enum: - ALLOWED - type: object required: - decision - category properties: decision: type: string enum: - REQUIRES_CONSENT category: type: string enum: - NAZI_GERMANY description: Listing-level content decision. This schema is used only when the listing has an assessment. SortProductListingFieldData: type: string enum: - score - updated - created description: "Fields available for sorting: - score: Sort by relevance score (default, only available when searching with text query) - updated: Sort by last updated timestamp - created: Sort by creation timestamp " default: score example: updated ProductListingHistoryEntryTypeData: type: string enum: - PRODUCT_LISTING_DISCOVERED - PRODUCT_LISTING_CHANGED description: Canonical ProductListing domain-history entry type. example: PRODUCT_LISTING_CHANGED ProductListingHistoryPayloadData: oneOf: - $ref: "#/components/schemas/ProductListingDiscoveryHistoryPayloadData" - $ref: "#/components/schemas/ProductListingChangedHistoryPayloadData" description: Immutable semantic history payload. It never contains image URLs or enrichment data. ProductListingDiscoveryHistoryPayloadData: type: object required: - listingSourceId - sourceListingId - pricing - availability - url - imageCount - auction properties: listingSourceId: type: string format: uuid sourceListingId: type: string title: $ref: "#/components/schemas/LocalizedTextData" description: $ref: "#/components/schemas/LocalizedTextData" pricing: $ref: "#/components/schemas/ProductListingPricingData" availability: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true url: type: string format: uri imageCount: type: integer format: int64 minimum: 0 auction: $ref: "#/components/schemas/AuctionData" ProductListingChangedHistoryPayloadData: type: object required: - changes properties: changes: type: array minItems: 1 description: "Deterministic semantic-change order: main price, minimum estimate, maximum estimate, availability, URL, images, auction, lifecycle, sale observation." items: $ref: "#/components/schemas/ProductListingHistoryChangeData" ProductListingHistoryChangeData: oneOf: - $ref: "#/components/schemas/ProductListingPriceHistoryChangeData" - $ref: "#/components/schemas/ProductListingAvailabilityHistoryChangeData" - $ref: "#/components/schemas/ProductListingUrlHistoryChangeData" - $ref: "#/components/schemas/ProductListingImagesHistoryChangeData" - $ref: "#/components/schemas/ProductListingAuctionHistoryChangeData" - $ref: "#/components/schemas/ProductListingWithdrawalHistoryChangeData" - $ref: "#/components/schemas/ProductListingRestorationHistoryChangeData" - $ref: "#/components/schemas/ProductListingSaleObservationHistoryChangeData" description: Typed semantic change. ProductListingPriceHistoryChangeData: type: object required: - type - previous - current properties: type: type: string enum: - MAIN_PRICE_CHANGED - MINIMUM_ESTIMATE_CHANGED - MAXIMUM_ESTIMATE_CHANGED previous: allOf: - $ref: "#/components/schemas/PriceData" nullable: true current: allOf: - $ref: "#/components/schemas/PriceData" nullable: true ProductListingAvailabilityHistoryChangeData: type: object required: - type - previous - current properties: type: type: string enum: - AVAILABILITY_CHANGED previous: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true current: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true ProductListingUrlHistoryChangeData: type: object required: - type - previous - current properties: type: type: string enum: - URL_CHANGED previous: type: string format: uri current: type: string format: uri ProductListingImagesHistoryChangeData: type: object required: - type - previousCount - currentCount properties: type: type: string enum: - IMAGES_CHANGED previousCount: type: integer format: int64 minimum: 0 currentCount: type: integer format: int64 minimum: 0 ProductListingAuctionHistoryChangeData: type: object required: - type - previous - current properties: type: type: string enum: - AUCTION_CHANGED previous: $ref: "#/components/schemas/AuctionData" current: $ref: "#/components/schemas/AuctionData" ProductListingWithdrawalHistoryChangeData: type: object required: - type - previousAvailability properties: type: type: string enum: - WITHDRAWN previousAvailability: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true ProductListingRestorationHistoryChangeData: type: object required: - type properties: type: type: string enum: - RESTORED ProductListingSaleObservationHistoryChangeData: type: object required: - type - observation properties: type: type: string enum: - SALE_OBSERVED - SALE_OBSERVATION_RETRACTED observation: type: object required: - observedAt - fxRateId properties: observedAt: type: string format: date-time fxRateId: type: string format: uuid ApiError: type: object description: Standard error response format (RFC 9457) required: - status - title - error properties: status: type: integer description: HTTP status code example: 400 title: type: string description: HTTP status code in human-readable form example: Bad Request error: type: string description: Error code identifier example: BAD_PARAMETER source: $ref: "#/components/schemas/ApiErrorSource" detail: type: string description: Human-readable error message example: "Expected any of: 'true' or 'false'. Got: 'invalid'" ApiErrorSource: type: object description: Information about the source of the error required: - field - sourceType properties: field: type: string description: Name of the field that caused the error example: listingSourceId sourceType: type: string enum: - query - path - header - body description: Type of parameter that caused the error example: path ProductListingSearchData: type: object description: ProductListing search criteria used by saved search filters. properties: language: $ref: "#/components/schemas/LanguageData" currency: $ref: "#/components/schemas/CurrencyData" productQuery: type: array items: &id001 type: string minLength: 1 enhancedSearchDescription: type: string minLength: 1 maxLength: 1000 nullable: true excludeProductId: type: array items: type: string format: uuid uniqueItems: true description: ProductListing IDs to exclude. listingSourceId: type: array items: &id002 type: string format: uuid uniqueItems: true description: ListingSource IDs to include. excludeListingSourceId: type: array items: &id003 type: string format: uuid uniqueItems: true description: ListingSource IDs to exclude. price: allOf: &id004 - $ref: "#/components/schemas/RangeQueryUInt64" nullable: true availability: type: array items: &id005 $ref: "#/components/schemas/ListingAvailabilityData" uniqueItems: true nullable: true orderability: type: array items: &id006 $ref: "#/components/schemas/ListingOrderabilityData" uniqueItems: true nullable: true includeUnspecifiedAvailability: type: boolean nullable: true created: allOf: &id007 - $ref: "#/components/schemas/RangeQueryDateTime" nullable: true updated: allOf: &id008 - $ref: "#/components/schemas/RangeQueryDateTime" nullable: true auctionStart: allOf: &id009 - $ref: "#/components/schemas/RangeQueryDateTime" nullable: true auctionEnd: allOf: &id010 - $ref: "#/components/schemas/RangeQueryDateTime" nullable: true PostUserSearchFilterData: type: object description: Request body for creating a new search filter required: - name - search properties: name: type: string description: User-defined name for the search filter (max 255 characters, will be truncated if longer) maxLength: 255 example: My Tech Store Search search: $ref: "#/components/schemas/ProductListingSearchData" PatchUserSearchFilterData: type: object description: "Partial search filter update data. All fields are optional and only provided fields will be updated. Can update the search filter name, notifications preference, activation state, and/or the search filter criteria. " properties: name: type: string description: User-defined name for the search filter (max 255 characters, will be truncated if longer) maxLength: 255 nullable: true example: Updated Filter Name notifications: type: boolean nullable: true description: "Whether to enable or disable email notifications for new product-listings matching this search filter. When `true`, the user will be notified when a new product matches this filter. When `false`, no notifications are sent for this filter. Omit to leave unchanged. " example: true state: allOf: - $ref: "#/components/schemas/PatchResourceStateData" nullable: true description: "Optional activation state update for this saved search filter. `ACTIVE` reactivates the filter if the current user tier still permits the configured search features and quota. `INACTIVE_BY_USER` manually disables the filter. Omit to leave the current state unchanged. " search: allOf: - $ref: "#/components/schemas/PatchProductListingSearchData" nullable: true description: Partial search filter criteria to update PatchUserSearchFilterMatchData: type: object description: "Partial update payload for a saved-search product match. Omitting `feedback` leaves the stored match feedback unchanged. " properties: feedback: type: boolean nullable: true description: "Optional relevance feedback for the matched product. `true` marks the product as relevant to the saved search filter, `false` marks it as not relevant. " example: true PatchProductListingSearchData: type: object description: Partial ProductListing search criteria. Omitted fields are unchanged. `enhancedSearchDescription`, price, date ranges, and the availability query may be cleared with `null`; availability, orderability, and includeUnspecifiedAvailability` must be supplied together or all be `null`. properties: language: $ref: "#/components/schemas/LanguageData" nullable: true currency: $ref: "#/components/schemas/CurrencyData" nullable: true productQuery: type: array items: *id001 nullable: true enhancedSearchDescription: type: string minLength: 1 maxLength: 1000 nullable: true listingSourceId: type: array items: *id002 uniqueItems: true description: ListingSource IDs to include. nullable: true excludeListingSourceId: type: array items: *id003 uniqueItems: true description: ListingSource IDs to exclude. nullable: true price: allOf: *id004 nullable: true availability: type: array items: *id005 uniqueItems: true nullable: true orderability: type: array items: *id006 uniqueItems: true nullable: true includeUnspecifiedAvailability: type: boolean nullable: true created: allOf: *id007 nullable: true updated: allOf: *id008 nullable: true auctionStart: allOf: *id009 nullable: true auctionEnd: allOf: *id010 nullable: true UserSearchFilterData: type: object description: Complete user search filter with metadata required: - userId - userSearchFilterId - name - notifications - state - search - createdBy - updatedBy - created - updated properties: userId: type: string format: uuid description: Unique identifier of the user who owns this search filter example: 550e8400-e29b-41d4-a716-446655440000 userSearchFilterId: type: string format: uuid description: Unique identifier for this search filter example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 name: type: string description: User-defined name for the search filter example: My Tech Store Search notifications: type: boolean description: "Whether notifications are enabled for this search filter. When `true`, the user will be notified when a new product matches this filter. When `false`, no notifications are sent for this filter. Defaults to `true` for newly created filters. " example: true state: $ref: "#/components/schemas/ResourceStateData" search: $ref: "#/components/schemas/ProductListingSearchData" createdBy: $ref: "#/components/schemas/ActorData" updatedBy: $ref: "#/components/schemas/ActorData" created: type: string format: date-time description: When the search filter was created (RFC3339 format) example: "2024-01-01T10:00:00Z" updated: type: string format: date-time description: When the search filter was last updated (RFC3339 format) example: "2024-01-01T12:00:00Z" ResourceStateData: type: string description: "Activation state of a stored user-owned resource such as a saved search filter. `ACTIVE` resources are eligible for their normal runtime behavior. `INACTIVE_BY_USER` resources were explicitly deactivated by the user. `INACTIVE_BY_RESTRICTED_PLAN` resources remain stored but are inactive because the user's current tier no longer allows them to stay active. " enum: - ACTIVE - INACTIVE_BY_USER - INACTIVE_BY_RESTRICTED_PLAN example: ACTIVE PatchResourceStateData: type: string description: "Client-settable activation state for resources that support manual activation toggles. Clients may reactivate a resource with `ACTIVE` or manually disable it with `INACTIVE_BY_USER`. `INACTIVE_BY_RESTRICTED_PLAN` is managed internally by the backend and is never accepted from clients. " enum: - ACTIVE - INACTIVE_BY_USER example: INACTIVE_BY_USER SearchFilterProductMatchData: type: object description: Persisted match record for one of a user's saved search filters. required: - userId - userSearchFilterId - productListingId - originEventId - created - updated properties: userId: type: string format: uuid description: Unique identifier of the user who owns the matched search filter. example: 550e8400-e29b-41d4-a716-446655440000 userSearchFilterId: type: string format: uuid description: Unique identifier of the saved search filter that matched the product. example: 6ba7b810-9dad-11d1-80b4-00c04fd430c8 userSearchFilterName: type: string description: Saved-search name captured with the match, when available. example: Golden Accessories productListingId: type: string format: uuid description: Unique internal identifier of the matched product. example: 550e8400-e29b-41d4-a716-446655440111 originEventId: type: string format: uuid description: Product event that produced this match. example: 550e8400-e29b-41d4-a716-446655440222 enhancedMatchReason: type: string description: Enhanced-match explanation, when matching supplied one. example: The product matches the saved enhanced description. feedback: type: boolean description: Optional feedback recorded by the user for this saved-search match. example: true created: type: string format: date-time description: When the search-filter product match was originally created (RFC3339 format). example: "2024-01-01T10:00:00Z" updated: type: string format: date-time description: When the search-filter product match was last updated (RFC3339 format). example: "2024-01-01T12:30:00Z" RangeQueryUInt64: type: object description: Range query for numeric values in minor currency units for the selected indexed currency; no request-time conversion occurs properties: min: type: integer minimum: 0 description: Minimum value (inclusive) example: 1000 max: type: integer minimum: 0 description: Maximum value (inclusive) example: 5000 RangeQueryDateTime: type: object description: Range query for date and time values properties: min: type: string format: date-time description: Minimum date and time (inclusive, RFC3339 format) example: "2024-01-01T00:00:00Z" max: type: string format: date-time description: Maximum date and time (inclusive, RFC3339 format) example: "2024-12-31T23:59:59Z" UserSearchFilterCollectionData: type: object description: Paginated collection of user search filters with flattened pagination required: - items - from - size properties: items: type: array items: $ref: "#/components/schemas/UserSearchFilterData" description: Array of search filters in the current page from: type: integer minimum: 0 description: Number of product-listings skipped (offset) example: 0 size: type: integer minimum: 0 description: Number of product-listings in the current page example: 21 total: type: integer minimum: 0 description: Total number of product-listings matching the query nullable: true example: 127 AccessTokenScopeData: type: string enum: - product-listings:write - users:read - users:write - access-tokens:read - access-tokens:write - search-filters:write - watchlist:read - watchlist:write description: "Aura Historia access-token and OAuth scope string. The value is one of the capabilities supported by the canonical access-token and OAuth flows. " example: access-tokens:read AccessTokenTypeData: type: string enum: - BEARER description: Token type returned for Aura Historia access tokens. example: BEARER AdminAccessTokenCollectionData: type: object description: "Cursor-paginated, secret-free access-token metadata for an administrator's explicit target user. `searchAfter` is a `[created RFC3339 timestamp, access-token UUID]` cursor and is omitted when the current page is terminal. The cursor timestamp is not returned as item metadata. This schema intentionally has no raw token, masked token, short-token, or token-hash property. " required: - items - size properties: items: type: array description: Access-token metadata returned for the target user, including expired and current tokens. items: $ref: "#/components/schemas/AdminAccessTokenData" size: type: integer format: int64 minimum: 1 maximum: 100 description: Requested page size after server-side clamping. example: 21 searchAfter: type: array minItems: 2 maxItems: 2 items: type: string description: "`[created RFC3339 timestamp, access-token UUID]` cursor for the next page, omitted when no more results are available." nullable: true example: - "2026-09-04T12:00:00Z" - 01970f22-2bf0-7000-8000-000000000001 AdminAccessTokenData: type: object description: "Secret-free Aura Historia access-token metadata returned by the admin target-user listing. The representation contains no raw token, masked token, short-token, or token-hash value. `expires` is omitted when the token has no expiration and is returned for both expired and current tokens. " required: - userId - accessTokenId - name - scopes - origin properties: userId: type: string format: uuid description: Explicit target user who owns the access token. example: 550e8400-e29b-41d4-a716-446655440000 accessTokenId: type: string format: uuid description: Unique identifier of the access token. example: 01970f22-2bf0-7000-8000-000000000001 name: type: string maxLength: 128 description: User-defined display name of the access token. example: Incident review token scopes: type: array uniqueItems: true description: Granted access-token scopes. items: $ref: "#/components/schemas/AccessTokenScopeData" example: - users:read origin: type: string description: Origin recorded by the canonical access-token model. example: User expires: type: string format: date-time description: Optional RFC3339 expiration timestamp; expired and current tokens are both listed. nullable: true example: "2026-09-30T00:00:00Z" GetAccessTokenData: type: object description: "Access-token read model. `POST /api/v1/me/access-tokens` returns the plaintext bearer token once; list/get/update endpoints return the masked display value instead. " required: - accessTokenId - name - token - tokenType - createdBy - updatedBy - created - updated properties: accessTokenId: type: string format: uuid description: Unique identifier of the access token. example: 01970f22-2bf0-7000-8000-000000000001 name: type: string maxLength: 128 description: User-defined display name of the access token. example: Partner product sync scope: type: array items: $ref: "#/components/schemas/AccessTokenScopeData" uniqueItems: true description: Granted access-token scopes. Omitted when the token has no scopes. example: - product-listings:write token: type: string description: "Plaintext or masked bearer token value. Create responses return the full token once; subsequent reads return a masked value such as `aurahistoria_abcdefghijk_****`. " example: aurahistoria_abcdefghijk_**** tokenType: $ref: "#/components/schemas/AccessTokenTypeData" expiresAt: type: string format: date-time nullable: true description: Optional absolute expiration timestamp in RFC 3339 format. example: "2026-06-30T00:00:00Z" expiresIn: type: integer minimum: 0 nullable: true description: Optional non-negative number of seconds remaining until expiry. example: 2851200 createdBy: $ref: "#/components/schemas/ActorData" description: Actor who created this access token updatedBy: $ref: "#/components/schemas/ActorData" description: Actor who last updated this access token created: type: string format: date-time description: Timestamp when the access token was created. example: "2026-05-28T06:30:00Z" updated: type: string format: date-time description: Timestamp when the access token metadata was last updated. example: "2026-05-28T07:00:00Z" PostAccessTokenData: type: object description: Request body for creating an Aura Historia access token. required: - name properties: name: type: string maxLength: 128 description: User-defined display name of the new access token. example: Partner product sync scope: type: array items: $ref: "#/components/schemas/AccessTokenScopeData" uniqueItems: true description: Optional scopes granted to the access token. Defaults to an empty set. example: - product-listings:write expiresAt: type: string format: date-time nullable: true description: Optional expiration timestamp in RFC 3339 format. example: "2026-06-30T00:00:00Z" PatchAccessTokenData: type: object description: "Request body for updating access-token metadata. Omitted fields remain unchanged. `expires: null` clears the expiry; `name` and `scopes` reject null, and `scopes: []` replaces the scope set with an empty set. " required: - accessTokenId properties: accessTokenId: type: string format: uuid description: Unique identifier of the access token to update. example: 01970f22-2bf0-7000-8000-000000000001 name: type: string maxLength: 128 description: Omit to leave unchanged. Null is invalid. example: Renamed partner sync token scopes: type: array items: $ref: "#/components/schemas/AccessTokenScopeData" uniqueItems: true description: Omit to leave unchanged. Send `[]` to empty the set; null is invalid. example: - product-listings:write expires: type: string format: date-time nullable: true description: Omit to leave unchanged. Send null to clear the expiration timestamp. example: "2026-07-31T00:00:00Z" OAuthClientMetadataRequestData: type: object description: "Request body for creating OAuth client metadata. `redirect_uris` must contain at least one HTTPS redirect URI. " required: - client_name - tos_uri - policy_uri - client_uri - logo_uri - redirect_uris properties: client_name: type: string description: Display name of the OAuth client. example: Acceptance OAuth client tos_uri: type: string format: uri description: HTTPS URL of the OAuth client's terms-of-service document. example: https://client.example/tos policy_uri: type: string format: uri description: HTTPS URL of the OAuth client's privacy policy document. example: https://client.example/policy client_uri: type: string format: uri description: HTTPS URL of the OAuth client's homepage or product site. example: https://client.example logo_uri: type: string format: uri description: HTTPS URL of the OAuth client's logo image. example: https://client.example/logo.png redirect_uris: type: array minItems: 1 uniqueItems: true description: Registered HTTPS redirect URIs for the OAuth client. items: type: string format: uri example: - https://client.example/callback scope: type: array uniqueItems: true description: Optional allowed scopes for tokens issued to this OAuth client. items: $ref: "#/components/schemas/AccessTokenScopeData" example: - product-listings:write OAuthClientMetadataPatchData: type: object description: "Request body for updating OAuth client metadata. Omitted properties remain unchanged; every explicit null is invalid because metadata values are non-nullable. " properties: client_name: type: string description: Optional replacement display name for the OAuth client. example: Updated acceptance OAuth client tos_uri: type: string format: uri description: Optional replacement terms-of-service document URL for the OAuth client. example: https://client.example/updated-tos policy_uri: type: string format: uri description: Optional replacement privacy-policy document URL for the OAuth client. example: https://client.example/updated-policy client_uri: type: string format: uri description: Optional replacement homepage or product-site URL for the OAuth client. example: https://updated-client.example logo_uri: type: string format: uri description: Optional replacement logo image URL for the OAuth client. example: https://updated-client.example/logo.png redirect_uris: type: array minItems: 1 uniqueItems: true description: Optional replacement set of registered HTTPS redirect URIs. items: type: string format: uri example: - https://client.example/updated scope: type: array uniqueItems: true description: Omit to leave unchanged. Send `[]` to replace with an empty set; null is invalid. items: $ref: "#/components/schemas/AccessTokenScopeData" example: - access-tokens:read OAuthClientMetadataResponseData: type: object description: "OAuth client metadata create response. `client_secret` contains the plaintext secret only in the create response. Admin list, detail, and update responses use `OAuthClientAdminData` and omit `client_secret` entirely. " required: - client_id - client_secret - client_name - tos_uri - policy_uri - client_uri - logo_uri - redirect_uris - scope - client_id_issued_at properties: client_id: type: string format: uuid description: UUIDv7 identifier of the OAuth client. example: 01970f22-2bf0-7000-8000-000000000010 client_secret: type: string description: "Plaintext OAuth client secret returned only once by the create response. Admin list, detail, and update responses omit this property entirely. " example: aurahistoria_oauth_client_secret_abcdefghijk_abcdefghijklmnopqrstuvwxyz1234567 client_name: type: string description: Display name of the OAuth client. example: Acceptance OAuth client tos_uri: type: string format: uri description: Terms-of-service document URL registered for the OAuth client. example: https://client.example/tos policy_uri: type: string format: uri description: Privacy-policy document URL registered for the OAuth client. example: https://client.example/policy client_uri: type: string format: uri description: Homepage or product-site URL registered for the OAuth client. example: https://client.example logo_uri: type: string format: uri description: Logo image URL registered for the OAuth client. example: https://client.example/logo.png redirect_uris: type: array uniqueItems: true description: Registered redirect URIs for the OAuth client. items: type: string format: uri example: - https://client.example/callback scope: type: array uniqueItems: true description: Allowed scopes for tokens issued to this OAuth client. items: $ref: "#/components/schemas/AccessTokenScopeData" example: - product-listings:write client_id_issued_at: type: integer format: int64 description: Unix timestamp (seconds) when the OAuth client was created and its client ID was issued. example: 1748539200 OAuthClientAdminCollectionData: type: object description: "Cursor-paginated OAuth client summaries for the administrator collection endpoint. `searchAfter` is a `[created RFC3339 timestamp, client UUID]` cursor and is omitted when the current page is terminal. The collection and its items never contain client secret plaintext, secret hashes, or masked secret values. " required: - items - size properties: items: type: array items: $ref: "#/components/schemas/OAuthClientAdminData" description: OAuth client summaries returned in the current page. size: type: integer format: int64 minimum: 1 maximum: 100 description: Requested page size after server-side clamping. example: 21 searchAfter: type: array minItems: 2 maxItems: 2 items: type: string description: "`[created RFC3339 timestamp, client UUID]` cursor for the next page, omitted when no more results are available." nullable: true example: - "2026-09-04T12:00:00Z" - 01970f22-2bf0-7000-8000-000000000010 total: type: integer format: int64 minimum: 0 description: Total matching OAuth clients when provided by the reader; omitted otherwise. nullable: true example: 42 OAuthClientAdminData: type: object description: "Secret-free OAuth client metadata returned by the admin OAuth client collection, detail, and update endpoints under `/api/v1/admin/oauth-clients`. This schema intentionally has no `client_secret` property. " required: - client_id - client_name - tos_uri - policy_uri - client_uri - logo_uri - redirect_uris - scope - client_id_issued_at properties: client_id: type: string format: uuid description: UUIDv7 identifier of the OAuth client. example: 01970f22-2bf0-7000-8000-000000000010 client_name: type: string description: Display name of the OAuth client. example: Dashboard integration tos_uri: type: string format: uri description: Terms-of-service document URL registered for the OAuth client. example: https://client.example/tos policy_uri: type: string format: uri description: Privacy-policy document URL registered for the OAuth client. example: https://client.example/policy client_uri: type: string format: uri description: Homepage or product-site URL registered for the OAuth client. example: https://client.example logo_uri: type: string format: uri description: Logo image URL registered for the OAuth client. example: https://client.example/logo.png redirect_uris: type: array uniqueItems: true description: Registered redirect URIs for the OAuth client. items: type: string format: uri example: - https://client.example/callback scope: type: array uniqueItems: true description: Allowed scopes for tokens issued to this OAuth client. items: $ref: "#/components/schemas/AccessTokenScopeData" example: - access-tokens:read client_id_issued_at: type: integer format: int64 description: Unix timestamp (seconds) when the OAuth client was created and its client ID was issued. example: 1748539200 OAuthTokenResponseData: type: object description: "Token response returned by `POST /api/v1/oauth/token` and `GET /api/v1/oauth/tokens/by-third-party-code/{thirdPartyCode}`. The `access_token` is an Aura Historia bearer access token. `expires_in` is omitted (`null`) for non-expiring tokens. `third_party_exchange_code` is only present on the direct token exchange response. " required: - access_token - token_type - scope properties: access_token: type: string description: The issued Aura Historia access token (plaintext bearer value). example: aurahistoria_abcdefghijk_verylongtokenvalue token_type: $ref: "#/components/schemas/AccessTokenTypeData" expires_in: type: integer format: int64 minimum: 0 nullable: true description: Seconds until the access token expires. `null` when the token does not expire. example: null scope: type: string description: Space-separated list of scopes granted to the access token. example: product-listings:write third_party_exchange_code: type: string format: uuid description: "Optional one-time exchange code returned by `POST /api/v1/oauth/token`. It can be redeemed once via `GET /api/v1/oauth/tokens/by-third-party-code/{thirdPartyCode}` within 60 seconds to obtain the same access token. " example: 01970f22-2bf0-7000-8000-000000000099 OAuthIntrospectionResponseData: type: object description: "Introspection response returned by `POST /api/v1/oauth/introspect` (RFC 7662). When `active` is `false` all other fields are omitted. When `active` is `true` the available token metadata is populated. " required: - active properties: active: type: boolean description: Whether the token is currently active (not expired, not revoked, and known to this server). example: true scope: type: string description: Space-separated list of scopes granted to the token. Present only when `active` is `true`. example: product-listings:write client_id: type: string format: uuid description: UUIDv7 OAuth client identifier that issued the token. Present only when `active` is `true` and the token was issued via the OAuth flow. example: 01970f22-2bf0-7000-8000-000000000010 sub: type: string format: uuid description: Subject — the Aura Historia user ID who authorized the token. Present only when `active` is `true`. example: 0196580c-e4ca-723f-a7e0-1a73588380f1 token_type: type: string description: Token type. Always `"Bearer"` when `active` is `true`. example: Bearer exp: type: integer format: int64 description: Unix timestamp (seconds) when the token expires. Present only when `active` is `true` and the token has an expiry. example: 1780000000 iat: type: integer format: int64 description: Unix timestamp (seconds) when the token was issued. Present only when `active` is `true` and the issue time is known. example: 1748400000 PostWatchlistData: type: object description: Request body for creating a watchlist entry. required: - productListingId properties: productListingId: type: string format: uuid description: Canonical identifier of the product to add to the watchlist. example: 550e8400-e29b-41d4-a716-446655440000 notifications: type: boolean description: Whether notifications are enabled for this entry. Defaults to `true`. default: true example: true WatchlistEntryData: type: object description: A user's watchlist entry. required: - userId - productListingId - notifications - state properties: userId: type: string format: uuid description: Identifier of the user who owns the entry. example: 550e8400-e29b-41d4-a716-446655440000 productListingId: type: string format: uuid description: Canonical identifier of the watched product. example: 550e8400-e29b-41d4-a716-446655440000 notifications: type: boolean description: Whether notifications are enabled for this entry. example: true state: $ref: "#/components/schemas/ResourceStateData" created: type: string format: date-time description: When the entry was created, when available. example: "2026-08-06T12:00:00Z" updated: type: string format: date-time description: When the entry was last updated, when available. example: "2026-08-06T12:00:00Z" WatchlistProductPatch: type: object description: Patch object for updating watchlist product settings properties: notifications: type: boolean description: Whether to enable or disable notifications for this watchlist product example: true state: allOf: - $ref: "#/components/schemas/PatchResourceStateData" nullable: true description: "Optional activation state update for this watchlist entry. `ACTIVE` reactivates the entry if the user's current tier still has watchlist quota. `INACTIVE_BY_USER` manually disables the entry. Omit to leave the current state unchanged. " SortListingSourceFieldData: type: string enum: - name - slug - created - updated description: "Fields available for sorting admin ListingSource-search results. ListingSource ID is always used as a deterministic tie-breaker." example: name SortPartyFieldData: type: string enum: - name - email - phone - created - updated description: "Fields available for sorting admin Party-search results. Party ID is always used as a deterministic tie-breaker; absent contact values sort last." example: name CreatePartyData: type: object description: Party name and optional contact information used to create an admin Party. required: - name properties: name: type: string maxLength: 255 description: Party name. Outer Unicode whitespace is trimmed; blank values and values over 255 UTF-8 bytes are rejected. phone: type: string description: Optional Party phone number. email: type: string format: email description: Optional Party email address. UpdatePartyData: type: object description: "Partial admin Party update. Omitted members remain unchanged; null clears only phone and email." properties: name: type: string maxLength: 255 description: "Replacement Party name. Outer Unicode whitespace is trimmed; blank values, values over 255 UTF-8 bytes, and null are rejected." phone: type: string nullable: true description: "Replacement Party phone number; null clears it." email: type: string format: email nullable: true description: "Replacement Party email address; null clears it." PartyData: type: object description: Party representation returned after admin creation, update, and detail routes. required: - partyId - partySlugId - name - contact - created - updated properties: partyId: type: string format: uuid description: Stable Party identifier. partySlugId: type: string description: Immutable Party slug identifier. name: type: string maxLength: 255 description: Party name. contact: $ref: "#/components/schemas/PartyContactData" created: type: string format: date-time description: When the Party was created. updated: type: string format: date-time description: When the Party was last updated. PartyCollectionData: type: object description: "Cursor-paginated Party summaries for the admin search endpoint. `searchAfter` is a Party UUID cursor and is omitted when the current page is terminal." required: - items - size properties: items: type: array items: $ref: "#/components/schemas/PartySummaryData" description: Party summaries returned in the current page. size: type: integer minimum: 1 maximum: 100 description: Requested page size after server-side clamping. example: 21 searchAfter: type: string format: uuid description: Party UUID cursor for the next page, omitted when no more results are available. example: 7f1c2a88-4e18-4c3a-bf0d-6a0fd5e6d901 total: type: integer minimum: 0 description: Total matching Parties when provided by the reader; omitted otherwise. example: 42 PartySummaryData: type: object description: Compact Party data returned by the administrator Party-search endpoint. required: - partyId - partySlugId - name - contact - created - updated properties: partyId: type: string format: uuid description: Stable Party identifier. partySlugId: type: string description: Immutable Party slug identifier. name: type: string maxLength: 255 description: Party name. contact: $ref: "#/components/schemas/PartyContactData" created: type: string format: date-time description: When the Party was created. updated: type: string format: date-time description: When the Party was last updated. PartyContactData: type: object description: Optional Party contact summary. Absent contact members are omitted. properties: phone: type: string description: Party phone number, omitted when absent. email: type: string format: email description: Party email address, omitted when absent. SortUserFieldData: type: string enum: - name - email - firstName - lastName - tier - role - updated - created description: "Fields available for sorting admin user-search results: - name: First name followed by last name - email: User email address - firstName: User first name - lastName: User last name - tier: User subscription tier - role: User role - updated: Last update timestamp - created: Creation timestamp Every sort uses user ID as a deterministic tie-breaker. " example: email SearchFilterMatchProductCollectionData: type: object description: Cursor-paginated full personalized Product details for persisted search-filter matches. required: - items - size properties: items: type: array items: $ref: "#/components/schemas/PersonalizedProductListingDetailsData" description: Matched product-listings in persisted-match order. size: type: integer minimum: 0 description: Number of matched product-listings in the current page. example: 21 searchAfter: type: array minItems: 2 maxItems: 2 items: type: string description: "[RFC3339 match creation timestamp, product UUID] cursor for the next page, when present." nullable: true example: - "2024-01-15T08:00:00Z" - 550e8400-e29b-41d4-a716-446655440111 total: type: integer minimum: 0 description: Total number of persisted matches, when available. nullable: true example: 127 UserCollectionData: type: object description: "Cursor-paginated collection of users for the admin search endpoint. Items are compact admin user summaries. `searchAfter` is a user UUID cursor for the next page and is omitted when the current page is terminal. The cursor must be sent with the same filters and sort as the page that returned it. " required: - items - size properties: items: type: array items: $ref: "#/components/schemas/AdminUserSummaryData" description: Admin user summaries returned in the current page. size: type: integer minimum: 1 maximum: 100 description: Requested page size after server-side clamping. example: 21 searchAfter: type: string format: uuid description: User UUID cursor for the next page, omitted when more results are unavailable. example: 7f1c2a88-4e18-4c3a-bf0d-6a0fd5e6d901 total: type: integer minimum: 0 description: Total matching users when the reader provides it; omitted otherwise. example: 42 AdminUserSummaryData: type: object description: Compact user account data returned by the administrator user-search endpoint. required: - userId - email - tier - role properties: userId: type: string format: uuid description: Unique identifier for the user. email: type: string format: email description: User's email address. firstName: type: string maxLength: 64 description: User's first name, omitted when absent. lastName: type: string maxLength: 64 description: User's last name, omitted when absent. tier: $ref: "#/components/schemas/UserTierData" role: $ref: "#/components/schemas/UserRoleData" stripeCustomerId: type: string description: Persisted Stripe customer identifier, omitted when absent. OwnUserAccountData: type: object description: User-owned account information for `/api/v1/me/account`. Role, tier, and Stripe customer ID are read-only here. required: - userId - email - showUnassessedOrSensitiveContent - tier - role properties: userId: type: string format: uuid description: Unique identifier for the user. email: type: string format: email description: User's email address. firstName: type: string maxLength: 64 nullable: true lastName: type: string maxLength: 64 nullable: true language: allOf: - $ref: "#/components/schemas/LanguageData" nullable: true currency: allOf: - $ref: "#/components/schemas/CurrencyData" nullable: true measurementUnit: allOf: - $ref: "#/components/schemas/MeasurementUnitData" nullable: true showUnassessedOrSensitiveContent: type: boolean description: Stored preference for showing unassessed or sensitive content. tier: $ref: "#/components/schemas/UserTierData" role: $ref: "#/components/schemas/UserRoleData" stripeCustomerId: type: string nullable: true description: Persisted Stripe customer identifier, omitted when absent. AdminUserAccountData: allOf: - $ref: "#/components/schemas/OwnUserAccountData" - type: object description: Admin user account view. Same read fields as own account; write permissions differ by endpoint. SuspendUserData: type: object description: Required administrator-supplied explanation for suspending a user. required: - reason properties: reason: type: string minLength: 1 maxLength: 1000 description: Non-empty, non-whitespace explanation for the suspension. This value is emitted to structured operational logs; do not include tokens, passwords, credentials, or other secrets. Common credential markers are rejected. example: Repeated policy violations SuspendUserResponseData: type: object required: - userId - suspended properties: userId: type: string format: uuid description: Identifier of the target user. suspended: type: boolean description: Current suspension state; true after this operation, including an idempotent repeat. example: true UnsuspendUserResponseData: type: object required: - userId - suspended properties: userId: type: string format: uuid description: Identifier of the target user. suspended: type: boolean description: Current suspension state; false after this operation, including an idempotent repeat. example: false PatchAdminUserData: type: object description: "Partial admin-only user update payload. All fields are optional. Omitted fields remain unchanged. Only one logical change category may be sent per request: profile/preferences fields, `role`, or `tier`. " properties: email: type: string format: email description: New email address. Null is invalid. firstName: type: string maxLength: 64 nullable: true description: New first name. example: Ada lastName: type: string maxLength: 64 nullable: true description: New last name. example: Lovelace language: allOf: - $ref: "#/components/schemas/LanguageData" nullable: true description: New preferred language. currency: allOf: - $ref: "#/components/schemas/CurrencyData" nullable: true description: New preferred currency. measurementUnit: allOf: - $ref: "#/components/schemas/MeasurementUnitData" nullable: true description: New preferred measurement unit system. showUnassessedOrSensitiveContent: type: boolean description: Omit to leave unchanged. Send `true` to show unassessed or sensitive content; send `false` to hide it. `null` is invalid. example: true tier: allOf: - $ref: "#/components/schemas/UserTierData" description: Omit to leave unchanged. Null is invalid. role: allOf: - $ref: "#/components/schemas/UserRoleData" description: Omit to leave unchanged. Null is invalid. PatchUserAccountData: type: object description: "Partial user account update data. All fields are optional - only provided fields will be updated. " properties: firstName: type: string maxLength: 64 nullable: true description: New first name (max 64 characters) example: Jane lastName: type: string maxLength: 64 nullable: true description: New last name (max 64 characters) example: Smith language: allOf: - $ref: "#/components/schemas/LanguageData" nullable: true description: New preferred language currency: allOf: - $ref: "#/components/schemas/CurrencyData" nullable: true description: New preferred currency measurementUnit: allOf: - $ref: "#/components/schemas/MeasurementUnitData" nullable: true description: New preferred measurement unit system showUnassessedOrSensitiveContent: type: boolean description: Omit to leave unchanged. Send `true` to show unassessed or sensitive content; send `false` to hide it. `null` is invalid. example: true PostBillingCheckoutData: type: object description: Request body for creating a Stripe checkout session for a subscription purchase required: - plan - cycle properties: plan: $ref: "#/components/schemas/BillingPlanData" cycle: $ref: "#/components/schemas/BillingCycleData" BillingPlanData: type: string description: Subscription plan that should be purchased through Stripe Checkout enum: - PRO - ULTIMATE example: PRO BillingCycleData: type: string description: Billing interval that should be used for the subscription purchase enum: - MONTHLY - YEARLY example: MONTHLY BillingSessionUrlData: type: object description: Hosted Stripe session URL returned by the billing endpoints required: - url properties: url: type: string format: uri description: Hosted Stripe URL that the frontend should redirect the authenticated user to example: https://checkout.stripe.com/c/pay/cs_test_123 PutNewsletterSubscriptionData: type: object description: "Request body for creating or updating a newsletter subscription. `email` is required; all other fields are optional and may be omitted or set to `null`. " required: - email properties: email: type: string format: email description: Email address to subscribe to the newsletter example: collector@example.com firstName: type: string maxLength: 64 nullable: true description: Optional first name to sync with the subscription example: Ada lastName: type: string maxLength: 64 nullable: true description: Optional last name to sync with the subscription example: Lovelace language: allOf: - $ref: "#/components/schemas/LanguageData" nullable: true description: Optional preferred language to sync with the subscription currency: allOf: - $ref: "#/components/schemas/CurrencyData" nullable: true description: Optional preferred currency to sync with the subscription UserTierData: type: string description: "The user's subscription tier, which determines limits and feature access (e.g. max watchlist entries, max search filters, allowed search filter fields). - `FREE`: Default tier for all users. Allows up to 20 watchlist entries, up to 1 search filter (limited to `productQuery`, `price`, `state`, `excludeProductId`, and `lifecycle` filter fields), and up to 10 product matches per filter. - `PRO`: Premium tier. Allows up to 100 watchlist entries, up to 5 search filters with access to all filter fields, and unlimited product matches per filter. - `ULTIMATE`: Highest tier. Allows unlimited watchlist entries, unlimited search filters with access to all filter fields, and unlimited product matches per filter. " enum: - FREE - PRO - ULTIMATE example: FREE UserRoleData: type: string description: "The user's role used for API authorization. - `USER`: Standard authenticated user. - `ADMIN`: Administrator with access to admin-only endpoints. " enum: - USER - ADMIN example: USER NotificationData: type: object required: - notificationId - kind - payload - seen - created - updated properties: notificationId: type: string format: uuid kind: type: string enum: - WATCHLIST_PRICE_CHANGED - WATCHLIST_AVAILABILITY_CHANGED - SEARCH_FILTER_MATCH - PARTNERSHIP_APPLICATION_APPROVED - PARTNERSHIP_APPLICATION_REJECTED payload: $ref: "#/components/schemas/NotificationPayloadData" seen: type: boolean created: type: string format: date-time updated: type: string format: date-time NotificationPayloadData: description: Reason-specific immutable snapshot. Its shape is selected by the parent `NotificationData.kind`. oneOf: - $ref: "#/components/schemas/WatchlistNotificationPayloadData" - $ref: "#/components/schemas/SearchFilterNotificationPayloadData" - $ref: "#/components/schemas/PartnershipApplicationNotificationPayloadData" WatchlistNotificationPayloadData: type: object required: - productListingId - listingSourceId - sourceListingId - listingSourceSlugId - productListingTitleSlugId - listingSourceName - title - image - url - viewUrl - change properties: productListingId: type: string format: uuid listingSourceId: type: string format: uuid sourceListingId: type: string listingSourceSlugId: type: string productListingTitleSlugId: type: string listingSourceName: type: string title: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true image: allOf: - $ref: "#/components/schemas/ProductListingImageData" nullable: true url: type: string format: uri viewUrl: type: string format: uri change: $ref: "#/components/schemas/WatchlistNotificationChangeData" WatchlistNotificationChangeData: oneOf: - $ref: "#/components/schemas/WatchlistNotificationPriceChangeData" - $ref: "#/components/schemas/WatchlistNotificationAvailabilityChangeData" WatchlistNotificationPriceChangeData: type: object required: - type - oldPrice - newPrice properties: type: type: string enum: - PRICE_CHANGE oldPrice: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Previous event price, preserving its source currency. Null when unavailable. newPrice: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: New event price, preserving its source currency. Null when unavailable. WatchlistNotificationAvailabilityChangeData: type: object required: - type - oldAvailability - newAvailability properties: type: type: string enum: - AVAILABILITY_CHANGE oldAvailability: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true newAvailability: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true SearchFilterNotificationPayloadData: type: object required: - productListingId - userSearchFilterId - userSearchFilterName - listingSourceId - sourceListingId - listingSourceSlugId - productListingTitleSlugId - listingSourceName - title - image - url - viewUrl properties: productListingId: type: string format: uuid userSearchFilterId: type: string format: uuid userSearchFilterName: type: string listingSourceId: type: string format: uuid sourceListingId: type: string listingSourceSlugId: type: string productListingTitleSlugId: type: string listingSourceName: type: string title: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true image: allOf: - $ref: "#/components/schemas/ProductListingImageData" nullable: true url: type: string format: uri viewUrl: type: string format: uri PartnershipApplicationNotificationPayloadData: type: object required: - partnershipApplicationId - decision - listingSourceName - image properties: partnershipApplicationId: type: string format: uuid decision: type: string enum: - APPROVED - REJECTED listingSourceName: type: string image: type: string format: uri nullable: true UpdateNotificationSeenData: type: object required: - seen properties: seen: type: boolean UpdateNotificationsSeenData: type: object required: - notificationIds - seen properties: notificationIds: type: array minItems: 1 items: type: string format: uuid seen: type: boolean NotificationCollectionData: type: object required: - items - size properties: items: type: array items: $ref: "#/components/schemas/NotificationData" size: type: integer minimum: 1 maximum: 100 searchAfter: type: array nullable: true minItems: 2 maxItems: 2 items: type: string description: Opaque `[created RFC3339 timestamp, notification UUID]` cursor for the next page; omitted when no further page exists. CreateProductListingData: type: object description: "Data for creating a single product via the partner batch-create endpoint. " required: - sourceListingId - title - description - url - images properties: sourceListingId: type: string description: The listing source's own identifier for the product. Must be unique within the listing source. example: baroque-violin-001 title: $ref: "#/components/schemas/LocalizedTextData" description: $ref: "#/components/schemas/LocalizedTextData" price: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Optional asking price for the product priceEstimateMin: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Optional lower bound of the estimated price range priceEstimateMax: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Optional upper bound of the estimated price range availability: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true description: Omit or send `null` when Aura has no reliable current availability assertion. url: type: string format: uri description: URL to the product on the listing source's website example: https://my-listing-source.com/product-listings/baroque-violin images: type: array items: type: string format: uri description: List of image URLs for the product. May be empty. example: - https://my-listing-source.com/images/violin-1.jpg - https://my-listing-source.com/images/violin-2.jpg auctionStart: type: string format: date-time nullable: true description: Optional RFC3339 auction timestamp. example: "2025-05-01T12:00:00Z" auctionEnd: type: string format: date-time nullable: true description: Optional RFC3339 auction timestamp. example: "2025-05-10T12:00:00Z" PartnerProductListingBatchFailuresResponse: type: array description: "Failed entries from a synchronous partner-product batch. This response body is returned with HTTP 200 only when at least one entry in a non-empty batch succeeded. " items: type: object required: - listingSourceId - sourceListingId - error properties: listingSourceId: type: string format: uuid description: ListingSource that owns the failed product entry. sourceListingId: type: string description: Partner-controlled identifier of the failed product entry. error: type: string description: Stable API error key for the failed product entry. example: PRODUCT_LISTING_NOT_FOUND example: - listingSourceId: 550e8400-e29b-41d4-a716-446655440000 sourceListingId: baroque-violin-002 error: PRODUCT_LISTING_NOT_FOUND WithdrawProductListingData: type: object required: - sourceListingId properties: sourceListingId: type: string description: Partner-controlled identifier of the ProductListing to withdraw. example: baroque-violin-001 UpdateProductListingData: type: object description: "Data for synchronously updating one ProductListing. Omitted fields remain unchanged. `availability: null` clears the current assertion. `null` clears price and auction fields; `url` and `images` reject null. Send `images: []` to remove all images. " required: - sourceListingId properties: sourceListingId: type: string description: The listing source's own identifier for the product to update. Product existence is evaluated synchronously. example: baroque-violin-001 price: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Omit to leave unchanged. Send null to clear the current asking price. availability: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true description: Omit to leave unchanged. Send null to clear the current assertion. priceEstimateMin: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Omit to leave unchanged. Send null to clear the current value. priceEstimateMax: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Omit to leave unchanged. Send null to clear the current value. url: type: string format: uri description: Omit to leave unchanged. Null is invalid. example: https://my-listing-source.com/product-listings/baroque-violin images: type: array items: type: string format: uri description: Omit to leave unchanged. Send [] to remove all images; null is invalid. example: - https://my-listing-source.com/images/violin-1.jpg auctionStart: type: string format: date-time nullable: true description: Omit to leave unchanged. Send null to clear the auction start. example: "2026-04-10T10:00:00Z" auctionEnd: type: string format: date-time nullable: true description: Omit to leave unchanged. Send null to clear the auction end. example: "2026-04-10T12:00:00Z" UpsertProductListingData: type: object description: "Data for synchronously upserting one ProductListing. Only `sourceListingId` is required. Availability, price, each price estimate, and each auction timestamp are tri-state: omitted preserves an existing value, `null` clears it, and a concrete value sets it. On creation, omitted and `null` both mean no value. Upsert restores a withdrawn listing before current facts apply. Images and URL use the field-specific rules documented below. " required: - sourceListingId properties: sourceListingId: type: string description: The listing source's own identifier for the product. Must be unique within the listing source. example: baroque-violin-001 title: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true description: Optional localized title for the product. Used only when creating a new product. description: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true description: Optional localized description of the product. Used only when creating a new product. price: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Optional current asking price. Omit to leave an existing price unchanged; send null to clear it; send a value to set it. On creation, omitted and null both mean no current price. priceEstimateMin: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Optional lower bound of the estimated price range. On an existing listing, omit to preserve, send `null` to clear, or send a value to set. On creation, omitted and `null` both mean no lower bound. priceEstimateMax: allOf: - $ref: "#/components/schemas/PriceData" nullable: true description: Optional upper bound of the estimated price range. On an existing listing, omit to preserve, send `null` to clear, or send a value to set. On creation, omitted and `null` both mean no upper bound. availability: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true description: Omit to preserve an existing assertion; send null to clear it; send a code to set it. url: type: string format: uri nullable: true description: URL to the product on the listing source's website. On an existing listing, omit or send `null` to preserve the current URL; send a value to replace it. URL is non-clearable. example: https://my-listing-source.com/product-listings/baroque-violin images: type: array items: type: string format: uri description: List of image URLs for the product. On an existing listing, omit to preserve the stored image set, send `[]` to clear it, or send an array to replace it. `null` is invalid. example: - https://my-listing-source.com/images/violin-1.jpg - https://my-listing-source.com/images/violin-2.jpg auctionStart: type: string format: date-time nullable: true description: Optional RFC3339 auction timestamp. example: "2025-05-01T12:00:00Z" auctionEnd: type: string format: date-time nullable: true description: Optional RFC3339 auction timestamp. example: "2025-05-10T12:00:00Z" WoocommerceProductWebhookImageData: type: object description: Image object extracted from a WooCommerce product payload. required: - src properties: src: type: string format: uri description: Absolute URL of a product image. example: https://aura-historia-test.local/wp-content/uploads/2026/05/product.jpg WoocommerceProductWebhookUpsertData: type: object description: "WooCommerce product payload accepted for `product.created` and `product.updated`. Every event requires `id`. `status: publish` captures an UPSERT and requires nonblank `name` and `permalink`. `status: trash`, `draft`, `pending`, or `private` captures a DELETE and requires only `id`; missing or unsupported status is authorized ignored input and also requires only `id`. " required: - id properties: id: type: integer format: int64 minimum: 0 description: WooCommerce product identifier. It is converted to the internal `sourceListingId`. example: 17 name: type: string description: "Nonblank product title required only when `status: publish` captures an UPSERT." example: Test Produkt Titel permalink: type: string format: uri description: "Public URL required only when `status: publish` captures an UPSERT." example: http://aura-historia-test.local/product/test-produkt-titel/ description: type: string nullable: true description: Optional HTML product description. The backend converts it to plain text for indexing. example: "

Hayde yallah test beschreibung

" short_description: type: string nullable: true description: Optional HTML short description used when `description` is absent. example: "

Hayde yallah kurze test beschreibung

" price: type: string nullable: true pattern: '^(?:[0-9]+(?:\.[0-9]+)?|)$' description: "Optional WooCommerce price string for a captured `status: publish` UPSERT. Intake writes raw-values V2 `MACHINE_DECIMAL`. A nonblank value must be an untrimmed unsigned ASCII decimal (`digits` or `digits.digits`): no whitespace, signs, grouping separators, or currency symbols. A nonblank `SET` requires the listing source's configured `woocommerceCurrency`. Fractional digits beyond the configured currency minor-unit scale are accepted only when every excess digit is `0`; a nonzero excess digit is rejected rather than truncated. A blank source string maps to `CLEAR`. " example: "42.69" status: type: string nullable: true description: "Optional WooCommerce product status string. `publish` captures an UPSERT and requires nonblank `name` and `permalink`. `draft`, `pending`, `private`, and `trash` capture a DELETE and require only `id`. Unsupported or missing status is authorized ignored input: it causes no destructive write and does not require `name` or `permalink`. " example: publish stock_status: type: string nullable: true description: "Optional WooCommerce stock-status string. For `publish`, `instock` maps to `IN_STOCK`, `outofstock` to `OUT_OF_STOCK`, and `onbackorder` to `BACK_ORDER`. Missing or unsupported stock status never implies a sale. " example: instock date_modified_gmt: type: string nullable: true description: Optional WooCommerce product modification timestamp. When the topic/status maps to raw capture, accepted forms are no-offset GMT interpreted as UTC, RFC3339 `Z`, and RFC3339 `+00:00`; malformed or nonzero-offset values are rejected. A valid value establishes provider source order; omit it when no source-ordering guarantee is available. example: "2026-09-06T12:34:56" images: type: array description: Optional list of WooCommerce product images. Missing payload fields are treated as an empty list. items: $ref: "#/components/schemas/WoocommerceProductWebhookImageData" example: [] WoocommerceProductWebhookDeleteData: type: object description: "WooCommerce product payload accepted for `product.deleted`. Only `id` is required. If `name` or `permalink` are provided they may still be used by the backend, but the event remains valid with just the product ID. " required: - id properties: id: type: integer format: int64 minimum: 0 description: WooCommerce product identifier. It is converted to the internal `sourceListingId`. example: 17 date_modified_gmt: type: string nullable: true description: Optional WooCommerce product modification timestamp. Accepted forms are no-offset GMT interpreted as UTC, RFC3339 `Z`, and RFC3339 `+00:00`; nonzero offsets are rejected. A valid value establishes provider source order; omit it when no source-ordering guarantee is available. example: "2026-09-06T12:34:56" AdminOverviewData: type: object description: Versioned bounded administrator operational summary sourced from authoritative PostgreSQL. required: - schemaVersion - users - partnershipApplications - parties - listingSources - partnerships - productListings properties: schemaVersion: type: integer minimum: 1 enum: [1] description: Response contract version. users: $ref: "#/components/schemas/AdminOverviewUsersData" partnershipApplications: $ref: "#/components/schemas/AdminOverviewPartnershipApplicationsData" parties: $ref: "#/components/schemas/AdminOverviewCountData" listingSources: $ref: "#/components/schemas/AdminOverviewListingSourcesData" partnerships: $ref: "#/components/schemas/AdminOverviewCountData" productListings: $ref: "#/components/schemas/AdminOverviewProductListingsData" AdminOverviewCountData: type: object required: [total] properties: total: type: integer format: int64 minimum: 0 AdminOverviewUsersData: type: object required: [total, byTier, byRole] properties: total: type: integer format: int64 minimum: 0 byTier: $ref: "#/components/schemas/AdminOverviewUserTierCountsData" byRole: $ref: "#/components/schemas/AdminOverviewUserRoleCountsData" AdminOverviewUserTierCountsData: type: object required: [free, pro, ultimate] properties: free: { type: integer, format: int64, minimum: 0 } pro: { type: integer, format: int64, minimum: 0 } ultimate: { type: integer, format: int64, minimum: 0 } AdminOverviewUserRoleCountsData: type: object required: [user, admin] properties: user: { type: integer, format: int64, minimum: 0 } admin: { type: integer, format: int64, minimum: 0 } AdminOverviewPartnershipApplicationsData: type: object required: [total, byState] properties: total: type: integer format: int64 minimum: 0 byState: $ref: "#/components/schemas/AdminOverviewPartnershipApplicationStateCountsData" AdminOverviewPartnershipApplicationStateCountsData: type: object required: [submitted, inReview, approved, rejected, withdrawn] properties: submitted: { type: integer, format: int64, minimum: 0 } inReview: { type: integer, format: int64, minimum: 0 } approved: { type: integer, format: int64, minimum: 0 } rejected: { type: integer, format: int64, minimum: 0 } withdrawn: { type: integer, format: int64, minimum: 0 } AdminOverviewListingSourcesData: type: object description: "methodAssignments counts source-method rows and need not sum to total." required: [total, withoutIngestionMethod, methodAssignments] properties: total: type: integer format: int64 minimum: 0 withoutIngestionMethod: type: integer format: int64 minimum: 0 methodAssignments: $ref: "#/components/schemas/AdminOverviewListingSourceMethodAssignmentCountsData" AdminOverviewListingSourceMethodAssignmentCountsData: type: object required: [webCrawl, shopify, woocommerce, partnerApi] properties: webCrawl: { type: integer, format: int64, minimum: 0 } shopify: { type: integer, format: int64, minimum: 0 } woocommerce: { type: integer, format: int64, minimum: 0 } partnerApi: { type: integer, format: int64, minimum: 0 } AdminOverviewProductListingsData: type: object description: Availability counts include ACTIVE listings only. required: [total, byLifecycle, activeAvailability, activeWithoutAvailability] properties: total: type: integer format: int64 minimum: 0 byLifecycle: $ref: "#/components/schemas/AdminOverviewProductListingLifecycleCountsData" activeAvailability: $ref: "#/components/schemas/AdminOverviewActiveListingAvailabilityCountsData" activeWithoutAvailability: type: integer format: int64 minimum: 0 AdminOverviewProductListingLifecycleCountsData: type: object required: [active, withdrawn] properties: active: { type: integer, format: int64, minimum: 0 } withdrawn: { type: integer, format: int64, minimum: 0 } AdminOverviewActiveListingAvailabilityCountsData: type: object required: [ available, inStock, limitedAvailability, backOrder, madeToOrder, preOrder, preSale, unavailable, reserved, outOfStock, soldOut, ] properties: available: { type: integer, format: int64, minimum: 0 } inStock: { type: integer, format: int64, minimum: 0 } limitedAvailability: { type: integer, format: int64, minimum: 0 } backOrder: { type: integer, format: int64, minimum: 0 } madeToOrder: { type: integer, format: int64, minimum: 0 } preOrder: { type: integer, format: int64, minimum: 0 } preSale: { type: integer, format: int64, minimum: 0 } unavailable: { type: integer, format: int64, minimum: 0 } reserved: { type: integer, format: int64, minimum: 0 } outOfStock: { type: integer, format: int64, minimum: 0 } soldOut: { type: integer, format: int64, minimum: 0 } AdminPartnershipCollectionData: type: object description: "Cursor-paginated safe Partnership summaries for the admin collection. `searchAfter` is omitted on the terminal page; no total is returned." required: - items - size properties: items: type: array items: $ref: "#/components/schemas/AdminPartnershipSummaryData" description: Partnership summaries returned in the current page. size: type: integer format: int64 minimum: 1 maximum: 100 description: Requested page size after server-side clamping. example: 21 searchAfter: type: array minItems: 2 maxItems: 2 items: type: string description: Partnership cursor for the next page, omitted when no more results are available. example: - "2026-09-04T12:00:00Z" - 770e8400-e29b-41d4-a716-446655440000 AdminPartnershipDetailsData: type: object description: "Administrator Partnership detail. Member and ListingSource reference arrays are ordered by UUID and capped at 100 entries; count fields are complete." required: - partnershipId - party - memberUserIds - listingSourceIds - memberCount - listingSourceGrantCount - created - updated properties: partnershipId: type: string format: uuid description: Stable Partnership identifier. party: $ref: "#/components/schemas/PartnershipPartySummaryData" memberUserIds: type: array maxItems: 100 uniqueItems: true description: Current member user UUIDs in deterministic ascending UUID order. At most 100 references are returned. items: type: string format: uuid listingSourceIds: type: array maxItems: 100 uniqueItems: true description: Current ListingSource grant UUIDs in deterministic ascending UUID order. At most 100 references are returned. items: type: string format: uuid memberCount: type: integer format: int64 minimum: 0 description: Complete number of current Partnership members. listingSourceGrantCount: type: integer format: int64 minimum: 0 description: Complete number of current ListingSource grants. created: type: string format: date-time description: When the Partnership was created. updated: type: string format: date-time description: When the Partnership was last updated. AdminPartnershipSummaryData: type: object description: "Safe administrator Partnership summary. Member/grant identities, Party contact data, persistence versions, and credentials are excluded." required: - partnershipId - party - memberCount - listingSourceGrantCount - created - updated properties: partnershipId: type: string format: uuid description: Stable Partnership identifier. party: $ref: "#/components/schemas/PartnershipPartySummaryData" memberCount: type: integer format: int64 minimum: 0 description: Number of users in the Partnership. listingSourceGrantCount: type: integer format: int64 minimum: 0 description: Number of ListingSource grants in the Partnership. created: type: string format: date-time description: When the Partnership was created. updated: type: string format: date-time description: When the Partnership was last updated. PartnershipPartySummaryData: type: object description: Party summary embedded in an administrator Partnership result. required: - partyId - partySlugId - name properties: partyId: type: string format: uuid description: Stable Party identifier. partySlugId: type: string description: Immutable Party slug identifier. name: type: string maxLength: 255 description: Party name. PartnershipApplicationStateData: type: string enum: - SUBMITTED - IN_REVIEW - APPROVED - REJECTED - WITHDRAWN OwnPartnershipApplicationData: type: object required: - id - state - proposal properties: id: type: string format: uuid state: $ref: "#/components/schemas/PartnershipApplicationStateData" proposal: $ref: "#/components/schemas/PartnershipApplicationProposalData" AdminPartnershipApplicationData: type: object required: - id - applicantUserId - state - proposal - approvedPartnershipId - approvedListingSourceId properties: id: type: string format: uuid applicantUserId: type: string format: uuid state: $ref: "#/components/schemas/PartnershipApplicationStateData" proposal: $ref: "#/components/schemas/PartnershipApplicationProposalData" approvedPartnershipId: type: string format: uuid nullable: true approvedListingSourceId: type: string format: uuid nullable: true PartnershipProposalTypeData: type: string enum: - EXISTING_LISTING_SOURCE - PROPOSED_LISTING_SOURCE AdminPartnershipApplicationSummaryData: type: object required: - id - applicantUserId - state - proposal - approvedPartnershipId - approvedListingSourceId - created - updated properties: id: type: string format: uuid applicantUserId: type: string format: uuid state: $ref: "#/components/schemas/PartnershipApplicationStateData" proposal: $ref: "#/components/schemas/PartnershipApplicationProposalData" approvedPartnershipId: type: string format: uuid nullable: true approvedListingSourceId: type: string format: uuid nullable: true created: type: string format: date-time updated: type: string format: date-time AdminPartnershipApplicationCollectionData: type: object required: - items - size properties: items: type: array items: $ref: "#/components/schemas/AdminPartnershipApplicationSummaryData" size: type: integer format: int64 minimum: 1 maximum: 100 searchAfter: type: array minItems: 2 maxItems: 2 items: type: string description: JSON cursor containing the sort timestamp followed by the application UUID. total: type: integer format: int64 minimum: 0 nullable: true ListingSourceReferenceData: type: object required: - listingSourceId - listingSourceSlugId properties: listingSourceId: type: string format: uuid listingSourceSlugId: type: string example: ada-antiques ListingSourceData: type: object required: - listingSourceId - listingSourceSlugId - name - operator - ingestionMethods - created - updated properties: listingSourceId: type: string format: uuid listingSourceSlugId: type: string name: type: string description: ListingSource name. Outer Unicode whitespace is trimmed; blank values and values over 255 UTF-8 bytes are rejected. operator: $ref: "#/components/schemas/OperatorPartyData" ingestionMethods: type: array items: $ref: "#/components/schemas/ListingIngestionMethodData" url: type: string format: uri image: type: string format: uri created: type: string format: date-time updated: type: string format: date-time AdministeredListingSourceData: type: object required: - listingSourceId - listingSourceSlugId - name properties: listingSourceId: type: string format: uuid listingSourceSlugId: type: string name: type: string ListingSourceSearchCollectionData: type: object description: "Cursor-paginated safe ListingSource summaries for the admin search endpoint. `searchAfter` is a ListingSource UUID cursor and is omitted when the current page is terminal." required: - items - size properties: items: type: array items: $ref: "#/components/schemas/ListingSourceSearchSummaryData" description: ListingSource summaries returned in the current page. size: type: integer minimum: 1 maximum: 100 description: Requested page size after server-side clamping. example: 21 searchAfter: type: string format: uuid description: ListingSource UUID cursor for the next page, omitted when no more results are available. example: 7f1c2a88-4e18-4c3a-bf0d-6a0fd5e6d901 total: type: integer minimum: 0 description: Total matching ListingSources when provided by the reader; omitted otherwise. example: 42 ListingSourceSearchSummaryData: type: object description: Safe ListingSource administration summary. Provider credentials, webhook secrets, and crawler-local configuration are excluded. required: - listingSourceId - listingSourceSlugId - name - operator - ingestionMethods - presentation - created - updated properties: listingSourceId: type: string format: uuid description: Stable ListingSource identifier. listingSourceSlugId: type: string description: Immutable ListingSource slug identifier. name: type: string description: ListingSource name. operator: $ref: "#/components/schemas/OperatorPartyData" ingestionMethods: type: array items: $ref: "#/components/schemas/ListingIngestionMethodData" description: Active canonical ingestion methods. presentation: $ref: "#/components/schemas/ListingSourcePresentationData" referralConfiguration: allOf: - $ref: "#/components/schemas/ReferralConfigurationData" nullable: true description: Safe referral summary; provider credentials are never included. created: type: string format: date-time updated: type: string format: date-time ListingSourcePresentationData: type: object description: Optional public presentation URLs for a ListingSource. properties: url: type: string format: uri image: type: string format: uri ListingIngestionMethodData: type: string enum: - WEB_CRAWL - SHOPIFY - WOOCOMMERCE - PARTNER_API ListingIngestionConfigurationData: oneOf: - type: object required: - type - ingestionMethod properties: type: type: string enum: - UNCONFIGURED ingestionMethod: $ref: "#/components/schemas/ListingIngestionMethodData" - type: object required: - type properties: type: type: string enum: - WEB_CRAWL fallbackCurrency: type: string description: Optional ISO 4217 fallback used only when crawler-extracted price text contains no currency hint. - type: object required: - type - domain properties: type: type: string enum: - SHOPIFY domain: type: string currency: type: string language: type: string - type: object required: - type properties: type: type: string enum: - WOOCOMMERCE currency: type: string language: type: string - type: object required: - type properties: type: type: string enum: - PARTNER_API ReferralConfigurationData: type: object required: - type - camref properties: type: type: string enum: - PARTNERIZE camref: type: string OperatorPartyData: type: object required: - partyId - partySlugId - name properties: partyId: type: string format: uuid partySlugId: type: string name: type: string ListingSourceOperatorInputData: oneOf: - type: object required: - type - partyId properties: type: type: string enum: - EXISTING partyId: type: string format: uuid - type: object required: - type - name properties: type: type: string enum: - NEW name: type: string description: Party name. Outer Unicode whitespace is trimmed; blank values and values over 255 UTF-8 bytes are rejected. phone: type: string email: type: string format: email CreateListingSourceData: type: object required: - name - operator - ingestionConfiguration properties: name: type: string description: ListingSource name. Outer Unicode whitespace is trimmed; blank values and values over 255 UTF-8 bytes are rejected. operator: $ref: "#/components/schemas/ListingSourceOperatorInputData" ingestionConfiguration: type: array items: $ref: "#/components/schemas/ListingIngestionConfigurationData" woocommerceWebhookSecret: type: string writeOnly: true description: Optional WooCommerce webhook secret. Accepted only when WOOCOMMERCE ingestion is configured; never returned or logged. url: type: string format: uri image: type: string format: uri referralConfiguration: $ref: "#/components/schemas/ReferralConfigurationData" UpdateListingSourceData: type: object description: Omitted fields are unchanged. `null` clears only `woocommerceWebhookSecret`, `url`, `image`, and `referralConfiguration`. properties: name: type: string description: ListingSource name. Outer Unicode whitespace is trimmed; blank values and values over 255 UTF-8 bytes are rejected. ingestionConfiguration: type: array items: $ref: "#/components/schemas/ListingIngestionConfigurationData" woocommerceWebhookSecret: type: string nullable: true writeOnly: true description: Optional WooCommerce webhook secret. Never returned or logged. url: type: string format: uri nullable: true image: type: string format: uri nullable: true referralConfiguration: allOf: - $ref: "#/components/schemas/ReferralConfigurationData" nullable: true PartnershipApplicationProposalData: oneOf: - type: object required: - type - listingSourceId properties: type: type: string enum: - EXISTING_LISTING_SOURCE listingSourceId: type: string format: uuid - type: object required: - type - party - listingSource properties: type: type: string enum: - PROPOSED_LISTING_SOURCE party: $ref: "#/components/schemas/ProposedPartyData" listingSource: $ref: "#/components/schemas/ProposedListingSourceData" ProposedPartyData: type: object required: - name properties: name: type: string description: Party name. Outer Unicode whitespace is trimmed; blank values and values over 255 UTF-8 bytes are rejected. phone: type: string email: type: string format: email ProposedListingSourceData: type: object required: - name - requestedIngestionMethods properties: name: type: string description: ListingSource name. Outer Unicode whitespace is trimmed; blank values and values over 255 UTF-8 bytes are rejected. url: type: string format: uri image: type: string format: uri requestedIngestionMethods: type: array items: $ref: "#/components/schemas/ListingIngestionMethodData" SubmitPartnershipApplicationData: type: object required: - proposal properties: proposal: $ref: "#/components/schemas/PartnershipApplicationProposalData" DecidePartnershipApplicationData: type: object required: - decision properties: decision: type: string enum: - APPROVE - REJECT ProductListingSourceData: type: object description: ListingSource presentation hydrated for a ProductListing response. required: - listingSourceId - name - slugId properties: listingSourceId: type: string format: uuid name: type: string slugId: type: string x-tagGroups: - name: Discovery tags: - ProductListings - ListingSources - name: User Experience tags: - Search Filters - Watchlist - Notifications - User Account - Billing - Newsletter - name: Partner & Platform Integrations tags: - Partnerships - PartnershipApplications - OAuth tags: - name: ProductListings description: Product retrieval, product history, similar-product discovery, product search, and partner catalog ingestion workflows. - name: ListingSources description: ListingSource retrieval, authenticated creation and update, and caller-administered listing sources. - name: Search Filters description: Saved search creation, maintenance, live previews, stored matches, and match-feedback workflows. - name: Watchlist description: Watchlist membership, notification preferences, and watchlist-specific product state for authenticated users. - name: Notifications description: In-app notification retrieval, bulk updates, single-notification updates, and notification cleanup workflows. - name: User Account description: Authenticated user profile management, admin user management, access-token lifecycle management, and partnership listing. - name: Billing description: Stripe checkout, billing-portal, and subscription-management session creation for authenticated users. - name: Newsletter description: Newsletter subscription upsert workflows for website or product-surface sign-up flows. - name: Partnerships description: Partnership administration, safe summary reads, and membership/ListingSource count workflows. - name: PartnershipApplications description: Partnership application submission, review tracking, administration, and decision workflows. - name: OAuth description: OAuth 2.0 authorization, token, revocation, introspection, and OAuth client metadata management.