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.