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 preserves immutable source values and accepts optional `currency` for historical FX display projections.\n History text is not localized.\n- Request-wide 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- Origin API responses include\ \ 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; CloudFront removes them from viewer responses on the six cache-enabled discovery behaviors so shared cache hits cannot replay origin IDs.\n- Requests are limited to 1 MiB and time out after 30 seconds.\n- `POST /api/v1/webhooks/loops` is limited to 64 KiB and has a 10-second processing budget; it requires a valid Loops signature but no Aura bearer.\n- `GET /api/v1/health` is a\ \ process liveness check. `GET /api/v1/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. A mapped raw-capture command receives `204` only after confirmed shared FIFO admission; authorized ignored create/update status events are no-op `204`. Queue admission is not raw capture, provider receipt persistence, canonical normalization, or search visibility. Capture conflicts are handled by the consumer, not returned synchronously; existing partner Product batch writes remain synchronous.\n\n## Environment\ \ endpoints\n\n- **Development:** `https://api.stage.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.stage.aura-historia.com description: Development environment - url: https://api.aura-historia.com description: Production environment paths: /api/v1/health: get: summary: Check process liveness description: Returns the fixed `ok` response without checking dependencies. operationId: getHealth tags: [Operations] security: [] responses: "200": description: The process is alive. content: text/plain: schema: { type: string } example: "ok\n" /api/v1/ready: get: summary: Check service readiness description: Returns no diagnostic body when readiness fails. operationId: getReadiness tags: [Operations] security: [] responses: "204": description: The readiness check succeeded. "503": description: The readiness check failed or timed out; no diagnostic body is returned. /api/v1/auctions: get: summary: List public Auctions operationId: listAuctions tags: [Auctions] parameters: - { name: listingSourceId, in: query, schema: { type: string } } - { name: format, in: query, schema: { type: string, enum: [LIVE, TIMED] }, } - { name: reportedStatus, in: query, schema: { type: string, enum: [SCHEDULED, IN_PROGRESS, ENDED, POSTPONED, CANCELLED], }, } - { name: timeRole, in: query, schema: { type: string, enum: [ BIDDING_OPENS, LIVE_STARTS, LOTS_BEGIN_CLOSING, SCHEDULED_END, ], }, } - { name: from, in: query, schema: { type: string, format: date-time } } - { name: to, in: query, schema: { type: string, format: date-time } } - name: sort in: query description: "`created` sorts by creation time; `scheduled` requires `timeRole` and sorts on that role's instant, with missing instants last when no window is specified." schema: { type: string, enum: [created, scheduled], default: created } - name: order in: query description: Defaults to `desc` for created and `asc` for scheduled. schema: { type: string, enum: [asc, desc] } - { name: pageSize, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 21 }, } - name: searchAfter in: query description: Complete returned JSON cursor. Its scope must match the source, format, reported-status, exact-time filters, sort and order of this request. schema: { type: string } responses: "200": description: "Bounded Auction directory, newest-created-first by default. Scheduled order uses the chosen role instant, with Auction ID as the final tie-breaker. Anonymous successful 200 responses without Authorization use `public, max-age=0, s-maxage=60, stale-if-error=0`; requests with any Authorization header and all non-200 responses use `private, no-store`." content: application/json: schema: $ref: "#/components/schemas/PublicAuctionDirectoryData" "400": description: "Invalid ID, filter, cursor, or incomplete exact-time range." /api/v1/auctions/{auctionId}: get: summary: Get public Auction detail operationId: getAuction tags: [Auctions] parameters: - name: auctionId in: path required: true schema: { type: string } description: Strict `auc_` UUIDv7 TypeID. responses: "200": description: "Public Auction facts, source summary, schedule, reported lot count, and visible listing count. Anonymous successful 200 responses without Authorization use `public, max-age=0, s-maxage=60, stale-if-error=0`; requests with any Authorization header and all non-200 responses use `private, no-store`." content: application/json: schema: $ref: "#/components/schemas/PublicAuctionData" "400": { description: Invalid Auction ID (`INVALID_OBJECT_ID`). } "404": { description: Auction missing (`AUCTION_NOT_FOUND`). } /api/v1/auctions/{auctionId}/product-listings: get: summary: Get public Auction catalogue operationId: getAuctionCatalogue tags: [Auctions] parameters: - name: auctionId in: path required: true schema: { type: string } - { name: language, in: query, schema: { type: string, default: en } } - { name: currency, in: query, schema: { type: string, default: EUR } } - { name: pageSize, in: query, schema: { type: integer, minimum: 1, maximum: 100, default: 21 }, } - name: searchAfter in: query description: Complete returned JSON cursor. It is scoped to this Auction and includes `auctionId`, catalogue position, and ProductListing ID. schema: { type: string } responses: "200": description: "Visible assigned ProductListings in normal personalized detail presentation, ordered by catalogue position, null positions last, then backing listing UUID. Anonymous successful 200 responses without Authorization use `public, max-age=0, s-maxage=60, stale-if-error=0`; requests with any Authorization header and all non-200 responses use `private, no-store`." content: application/json: schema: $ref: "#/components/schemas/PublicAuctionCatalogueData" "400": { description: Invalid Auction ID or cursor. } "404": { description: Auction missing (`AUCTION_NOT_FOUND`). } /api/v1/admin/auctions: get: summary: Search Auctions for administration operationId: adminSearchAuctions description: Administrator authority is required. Delegated Aura access tokens also require `auctions:read`; Cognito users do not require a delegated scope. Success and errors are always `no-store`. tags: [Auctions] security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: query in: query description: NUL-free, case-insensitive literal substring of the localized Auction name or sourceAuctionId. schema: { type: string } - { name: listingSourceId, in: query, schema: { type: string } } - name: sourceAuctionId in: query description: Exact source-scoped Auction key, trimmed as on create. Combine with listingSourceId to locate a create conflict. schema: { type: string } - { name: format, in: query, schema: { $ref: "#/components/schemas/AuctionFormatData" }, } - { name: reportedStatus, in: query, schema: { $ref: "#/components/schemas/AuctionReportedStatusData" }, } - name: sort in: query description: Supply both sort and order to override the default updated/descending ordering. schema: { type: string, enum: [name, created, updated], default: updated } - name: order in: query description: Supply both sort and order to override the default updated/descending ordering. schema: { type: string, enum: [asc, desc], default: desc } - name: size in: query description: Requested page limit, clamped to 1–100. schema: { type: integer, minimum: 1, maximum: 100, default: 21 } - name: searchAfter in: query description: Opaque cursor from the preceding page. Filters, sort and order must match the request that issued it. schema: { type: string } responses: "200": description: "All matching Auctions, regardless of status or visible listing count. Always `Cache-Control: no-store`." headers: Cache-Control: description: Always `no-store`. schema: { type: string, example: no-store } content: application/json: schema: { $ref: "#/components/schemas/AdminAuctionSearchCollectionData", } "400": description: Invalid filter, sort, or cursor (`BAD_QUERY_PARAMETER_VALUE`, `BAD_SORT_VALUE`, or `INVALID_OBJECT_ID`). "401": { description: Missing or invalid credentials (`INVALID_CREDENTIALS`)., } "403": description: Administrator authority and delegated `auctions:read` capability required (`FORBIDDEN`). "503": { description: Auction search temporarily unavailable (`AUCTION_TEMPORARILY_UNAVAILABLE`)., } post: summary: Create a source-scoped Auction (admin) description: Delegated Aura access tokens require `auctions:write`; missing scope returns `403 FORBIDDEN`. operationId: createAdminAuction tags: [Auctions] security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/CreateAuctionData" responses: "201": description: Auction created. `Location` names the new admin detail resource. headers: Location: schema: { type: string } Cache-Control: description: Always `no-store`. schema: { type: string, example: no-store } content: application/json: schema: $ref: "#/components/schemas/AuctionAdminData" "400": { description: Invalid body or value (`BAD_BODY_VALUE`). } "401": { description: Missing or invalid credentials (`INVALID_CREDENTIALS`)., } "403": { description: Administrator authority required (`FORBIDDEN`). } "404": { description: ListingSource missing (`LISTING_SOURCE_NOT_FOUND`). } "409": { description: The ListingSource/sourceAuctionId key already exists (`CONFLICT`)., } "503": { description: Temporary Auction persistence failure (`AUCTION_TEMPORARILY_UNAVAILABLE`)., } /api/v1/admin/auctions/{auctionId}: get: summary: Get Auction administration detail operationId: getAdminAuction description: Administrator authority is required. Delegated Aura access tokens also require `auctions:read`; Cognito users do not require a delegated scope. Success and errors are always `no-store`. tags: [Auctions] security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: auctionId in: path required: true schema: { type: string } description: Strict `auc_` UUIDv7 TypeID. Bare UUIDs and wrong prefixes are invalid. responses: "200": description: "Current administrative Auction data. Always `Cache-Control: no-store`." headers: Cache-Control: description: Always `no-store`. schema: { type: string, example: no-store } content: application/json: schema: $ref: "#/components/schemas/AuctionAdminData" "400": { description: Invalid Auction ID (`INVALID_OBJECT_ID`). } "401": { description: Missing or invalid credentials (`INVALID_CREDENTIALS`)., } "403": description: Administrator authority and delegated `auctions:read` capability required (`FORBIDDEN`). "404": { description: Auction missing (`AUCTION_NOT_FOUND`). } "503": { description: Temporary Auction persistence failure (`AUCTION_TEMPORARILY_UNAVAILABLE`)., } patch: summary: Update Auction metadata (admin) description: Delegated Aura access tokens require `auctions:write`; missing scope returns `403 FORBIDDEN`. operationId: updateAdminAuction tags: [Auctions] security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: auctionId in: path required: true schema: { type: string } description: Strict `auc_` UUIDv7 TypeID. Auction identity and source key are immutable. requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/UpdateAuctionData" responses: "200": description: "Updated administrative Auction data. Always `Cache-Control: no-store`." headers: Cache-Control: description: Always `no-store`. schema: { type: string, example: no-store } content: application/json: schema: $ref: "#/components/schemas/AuctionAdminData" "400": description: "Invalid body, expectedVersion, or schedule (`BAD_BODY_VALUE`)." "401": { description: Missing or invalid credentials (`INVALID_CREDENTIALS`)., } "403": { description: Administrator authority required (`FORBIDDEN`). } "404": { description: Auction missing (`AUCTION_NOT_FOUND`). } "409": { description: Stale expectedVersion (`CONFLICT`). } "503": { description: Temporary Auction persistence failure (`AUCTION_TEMPORARILY_UNAVAILABLE`)., } /api/v1/listing-sources/{listingSourceId}/ingestion-configurations/woocommerce: put: summary: Replace WooCommerce ListingSource ingestion configuration description: | Creates or completely replaces the WooCommerce ingestion configuration for a ListingSource. The caller must be a Cognito-authenticated user or Aura access token owner with Partnership write access to the source. Delegated Aura access tokens require `listing-sources:write`. This PUT atomically enables WOOCOMMERCE when needed. The webhook secret is required, nonblank, write-only, and never returned. Omitted or null currency and language clear those optional values. Repeating the same request is safe. operationId: putWoocommerceListingSourceIngestionConfiguration tags: - ListingSources parameters: - name: listingSourceId in: path required: true description: Strict `ls_` ListingSource TypeID. schema: { type: string, example: ls_6rd827eqfefsva9teecmwa3ate } security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/PutWoocommerceListingSourceIngestionConfigurationData" example: webhookSecret: generated-provider-secret currency: EUR language: de responses: "201": description: WooCommerce was newly enabled and configured. No response body is returned. headers: Cache-Control: { description: Always `no-store`., schema: { type: string, example: no-store }, } "204": description: Existing WooCommerce configuration was replaced or already matched. No response body is returned. headers: Cache-Control: { description: Always `no-store`., schema: { type: string, example: no-store }, } "400": description: Invalid ListingSource ID (`INVALID_OBJECT_ID`), or malformed JSON, unknown request property, or invalid request value (`BAD_BODY_VALUE`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Missing or invalid bearer credential (`INVALID_CREDENTIALS`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Delegated scope missing or Partnership write access denied (`FORBIDDEN`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: Authorized ListingSource no longer exists (`LISTING_SOURCE_NOT_FOUND`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "409": description: Optimistic concurrency conflict (`CONFLICT`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Unexpected internal failure (`LISTING_SOURCE_INTERNAL_ERROR`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Temporary authorization or persistence failure (`LISTING_SOURCE_TEMPORARILY_UNAVAILABLE`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" /api/v1/listing-sources/{listingSourceId}/ingestion-configurations/shopify: put: summary: Replace Shopify ListingSource ingestion configuration description: | Creates or completely replaces the Shopify ingestion configuration for a ListingSource. The caller must be a Cognito-authenticated user or Aura access token owner with Partnership write access to the source. Delegated Aura access tokens require `listing-sources:write`. This PUT atomically enables SHOPIFY when needed. The domain is validated and remains unique across configured sources. Omitted or null currency and language clear those optional values. Repeating the same request is safe. operationId: putShopifyListingSourceIngestionConfiguration tags: - ListingSources parameters: - name: listingSourceId in: path required: true description: Strict `ls_` ListingSource TypeID. schema: { type: string, example: ls_6rd827eqfefsva9teecmwa3ate } security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/PutShopifyListingSourceIngestionConfigurationData" example: domain: merchant.myshopify.com currency: EUR language: de responses: "201": description: Shopify was newly enabled and configured. No response body is returned. headers: Cache-Control: { description: Always `no-store`., schema: { type: string, example: no-store }, } "204": description: Existing Shopify configuration was replaced or already matched. No response body is returned. headers: Cache-Control: { description: Always `no-store`., schema: { type: string, example: no-store }, } "400": description: Invalid ListingSource ID (`INVALID_OBJECT_ID`), or malformed JSON, unknown request property, or invalid request value (`BAD_BODY_VALUE`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "401": description: Missing or invalid bearer credential (`INVALID_CREDENTIALS`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Delegated scope missing or Partnership write access denied (`FORBIDDEN`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: Authorized ListingSource no longer exists (`LISTING_SOURCE_NOT_FOUND`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "409": description: Shopify domain uniqueness or optimistic concurrency conflict (`CONFLICT`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Unexpected internal failure (`LISTING_SOURCE_INTERNAL_ERROR`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Temporary authorization or persistence failure (`LISTING_SOURCE_TEMPORARILY_UNAVAILABLE`). content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" /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. Localized `title` and `description` are optional. Omit or send `null` when the source has no value to record. 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 example: ls_6rd827eqfefsva9teecmwa3ate 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: ls_6rd827eqfefsva9teecmwa3ate 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 and price. Omit `auction` to preserve its context; `auction: null`, `url: null`, and `images: null` return `400 BAD_BODY_VALUE`; send `[]` to remove all images. Within an asserted `auction`, omit a leaf to preserve it, send `null` to clear a clearable lot/timing leaf, or send a value to set it. `auction.auctionId` must name an existing Auction for this ListingSource. Omit it to preserve membership, send `null` to clear membership, or send an ID to set it. 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 example: ls_6rd827eqfefsva9teecmwa3ate 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: ls_6rd827eqfefsva9teecmwa3ate 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, and estimates create no value; `auction` is omitted or an asserted nested Auction/lot-facts patch (not `null`).\n- **Existing listing** — availability, price, and each price estimate\n are tri-state: omitted preserves, `null` clears, and a concrete value replaces. Omit `auction` to preserve it; `auction: null` is invalid. Within an asserted Auction/lot-facts patch, each lot/timing leaf is tri-state: omit to preserve, `null` to clear clearable leaves, or provide a value to set. `auctionId` must name an existing Auction for this ListingSource; omit it to preserve membership, send `null` to clear it, or send an ID to set it. 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 example: ls_6rd827eqfefsva9teecmwa3ate 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 auction: lotNumber: "42A" cataloguePosition: 7 timing: biddingOpens: "2025-05-01T08:00:00Z" scheduledCloses: "2025-05-10T12:30:00Z" reportedClosedAt: "2025-05-10T12:45: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: ls_6rd827eqfefsva9teecmwa3ate 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 example: ls_6rd827eqfefsva9teecmwa3ate 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: ls_6rd827eqfefsva9teecmwa3ate 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/listing-sources/{listingSourceId}/product-listings/async: post: summary: Submit a batch of product-listing creates asynchronously (Partner API) operationId: postAsyncPartnerProductListings tags: [ProductListings] description: | Submits the same JSON array of create objects as the synchronous POST, with up to 100 original entries. Accepts a partner user's Cognito access JWT or Aura access token; delegated access requires `product-listings:write`. No client-supplied queue or authority metadata is accepted. Each entry is parsed and validated independently after whole-request JSON/array/size validation; invalid entries do not prevent eligible siblings from being Existing create field rules apply (including optional `title` and `description`, omitted/null availability, optional pricing, and invalid outer `auction: null`). A syntactically valid missing Auction, existing listing key, partnership/source authority, lifecycle, and other business conflicts are decided downstream, not reported as immediate item failures. Only confirmed queue admission counts as accepted. `202` means admission, not creation, completion, rollback, or immediate search visibility. An unconfirmed send may already have been queued. `submissionId` is an opaque correlation identifier, not a job ID: there is no submission status endpoint, batch-level deduplication, or Location header. No created listing IDs or synchronous mutation results are returned. For a non-empty evaluated batch, at least one confirmed acceptance yields `202`, even with item failures. With zero accepted, any unconfirmed send or retryable send/deadline/FIFO blocked outcome yields `503`; otherwise internal/configuration failures yield `500`; otherwise all encoded-message-size failures yield `413`; remaining validation/value/size failures yield `400`. An empty array returns `202` after authentication and capability checks, with no sends. Every evaluated report uses the same JSON schema and echoes the effective `Idempotency-Key`, including non-2xx reports. Before item evaluation, bad path/key, malformed JSON, non-array, empty text, or over-100 array returns `400`; authentication `401`, insufficient capability `403`, whole-body/front-door limit `413`, and pre-evaluation internal/configuration or transient dependency failure `500` or `503`. These request-wide failures use `ApiError` and send nothing. Supply one `Idempotency-Key` for retries; a key is generated if omitted and echoed on evaluated responses. A lost response containing a generated key cannot be recovered. After transport uncertainty retry the exact unchanged ordered logical batch with the same key, including previously rejected entries at their original zero-based indices; never compact, reorder, or change its content under that key. Per-command downstream receipts deduplicate independently; the submission ID does not suppress the batch. Correct definitely invalid items in a separate request with a new key and do not include accepted/uncertain siblings. A same-group eligible successor is blocked when its FIFO predecessor is unresolved; unrelated groups may still be accepted. FIFO covers shared-queue acceptance per group, not cross-group or execution order. parameters: - name: listingSourceId in: path required: true description: Strict `ls_` ListingSource TypeID. schema: { type: string, example: ls_6rd827eqfefsva9teecmwa3ate } - $ref: "#/components/parameters/AsyncProductListingIdempotencyKey" security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Same array and item contract as synchronous create; individual invalid items produce report failures, not whole-batch rejection. content: application/json: schema: type: array maxItems: 100 items: $ref: "#/components/schemas/CreateProductListingData" example: - sourceListingId: SKU-001 title: { text: Baroque Violin, language: en } description: { text: An eighteenth-century violin., language: en } url: https://my-listing-source.com/product-listings/SKU-001 images: [https://my-listing-source.com/images/violin-1.jpg] responses: "202": description: At least one item confirmed admitted, or an authorized empty array. May include item-level failures. headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" examples: partial_admission: summary: Four original entries; indices 0 and 2 confirmed accepted value: submissionId: plis1_example acceptedCount: 2 failures: - { index: 1, sourceListingId: SKU-002, error: BAD_BODY_VALUE, retryable: false, } - { index: 3, sourceListingId: SKU-004, error: ENQUEUE_UNCONFIRMED, retryable: true, } empty_batch: summary: Empty authorized batch; no sends value: { submissionId: plis1_example, acceptedCount: 0, failures: [], } "400": description: Pre-evaluation invalid request (ApiError), or zero accepted with only item validation/value/size rejections (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: { status: 400, title: Bad Request, error: BAD_BODY_VALUE } application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" example: submissionId: plis1_example acceptedCount: 0 failures: - { index: 0, error: BAD_BODY_VALUE, retryable: false } "401": description: Missing or invalid bearer credential; no sends. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Insufficient credential capability; no sends. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "413": description: Whole-body/front-door limit before evaluation (ApiError), or zero accepted with only encoded-message-size item failures (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" example: submissionId: plis1_example acceptedCount: 0 failures: - { index: 0, sourceListingId: SKU-001, error: PRODUCT_LISTING_INGESTION_PAYLOAD_TOO_LARGE, retryable: false, } "500": description: Pre-evaluation internal/configuration failure (ApiError), or zero accepted with an item internal/configuration failure and no retryable send outcome (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" example: submissionId: plis1_example acceptedCount: 0 failures: - { index: 0, sourceListingId: SKU-001, error: PRODUCT_LISTING_INGESTION_INTERNAL_ERROR, retryable: false, } "503": description: Pre-evaluation transient dependency failure (ApiError), or zero accepted with an unconfirmed/retryable send, deadline or blocked outcome (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" example: submissionId: plis1_example acceptedCount: 0 failures: - { index: 0, sourceListingId: SKU-001, error: ENQUEUE_UNCONFIRMED, retryable: true, } - { index: 1, sourceListingId: SKU-001, error: ENQUEUE_BLOCKED, retryable: true, } patch: summary: Submit a batch of product-listing updates asynchronously (Partner API) operationId: patchAsyncPartnerProductListings tags: [ProductListings] description: | Submits the same JSON array of up to 100 `UpdateProductListingData` items as the synchronous PATCH. Accepts a partner user's Cognito access JWT or Aura access token; delegated access requires `product-listings:write`. No client-supplied queue or authority metadata is accepted. After whole-request JSON/array/size validation, each item is parsed and validated independently; invalid items do not prevent eligible siblings from being queued. The synchronous PATCH field rules apply: omit fields to preserve them; `null` clears availability, price, price estimates and clearable nested auction leaves; omit `auction` to preserve its context, or send an object to patch it. `auction: null`, `url: null` and `images: null` are invalid; `images: []` clears images. Omit `auction.auctionId` to preserve membership, use `null` to clear it or an ID to set it. A syntactically valid but missing listing or Auction, partnership/source authority, lifecycle and other business conflicts are checked downstream, not reported as immediate item failures or a synchronous `404`. Admission does not guarantee the patch is applied. Only confirmed queue admission counts as accepted. `202` means admission, not update completion, rollback, or immediate search visibility. An unconfirmed send may already have been queued. `submissionId` is an opaque correlation identifier, not a job ID: there is no submission status endpoint, batch-level deduplication, or Location header. No updated listing IDs or synchronous mutation results are returned. For a non-empty evaluated batch, at least one confirmed acceptance yields `202`, even with item failures. With zero accepted, any unconfirmed send or retryable send/deadline/FIFO blocked outcome yields `503`; otherwise internal/configuration failures yield `500`; otherwise all encoded-message-size failures yield `413`; remaining validation/value/size failures yield `400`. An empty array returns `202` after authentication and capability checks, with no sends. Every evaluated report uses the shared async batch JSON schema and echoes the effective `Idempotency-Key`, including non-2xx reports. Before item evaluation, bad path/key, malformed JSON, non-array, empty text, or over-100 array returns `400`; authentication `401`, insufficient capability `403`, whole-body/front-door limit `413`, and pre-evaluation internal/configuration or transient dependency failure `500` or `503`. These request-wide failures use `ApiError` and send nothing. Supply one `Idempotency-Key` for retries; a key is generated if omitted and echoed on evaluated responses. A lost response containing a generated key cannot be recovered. After transport uncertainty retry the exact unchanged ordered logical batch with the same key, including previously rejected entries at their original zero-based indices; never compact, reorder, or change its content under that key. Per-command downstream receipts deduplicate independently; the submission ID does not suppress the batch. Correct definitely invalid items in a separate request with a new key and do not include accepted/uncertain siblings. A same-group eligible successor is blocked when its FIFO predecessor is unresolved; unrelated groups may still be accepted. FIFO covers shared-queue acceptance per group, not cross-group or execution order. parameters: - name: listingSourceId in: path required: true description: Strict `ls_` ListingSource TypeID. schema: { type: string, example: ls_6rd827eqfefsva9teecmwa3ate } - $ref: "#/components/parameters/AsyncProductListingIdempotencyKey" security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Same array and item contract as synchronous PATCH; individual invalid items produce report failures, not whole-batch rejection. content: application/json: schema: type: array maxItems: 100 items: $ref: "#/components/schemas/UpdateProductListingData" example: - sourceListingId: baroque-violin-001 availability: null - sourceListingId: baroque-violin-002 url: null - sourceListingId: baroque-violin-003 price: { type: MONETARY, currency: EUR, amount: 5000 } - sourceListingId: baroque-violin-004 images: [] responses: "202": description: At least one update confirmed admitted, or an authorized empty array. May include item-level failures; no update completion implied. headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" examples: partial_admission: summary: Four original updates; indices 0 and 2 confirmed accepted value: submissionId: plis1_example acceptedCount: 2 failures: - { index: 1, sourceListingId: baroque-violin-002, error: BAD_BODY_VALUE, retryable: false, } - { index: 3, sourceListingId: baroque-violin-004, error: ENQUEUE_UNCONFIRMED, retryable: true, } empty_batch: summary: Empty authorized batch; no sends value: { submissionId: plis1_example, acceptedCount: 0, failures: [], } "400": description: Pre-evaluation invalid request (ApiError), or zero accepted with only item validation/value/size rejections (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" "401": description: Missing or invalid bearer credential; no sends. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Insufficient credential capability; no sends. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "413": description: Whole-body/front-door limit before evaluation (ApiError), or zero accepted with only encoded-message-size item failures (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" "500": description: Pre-evaluation internal/configuration failure (ApiError), or zero accepted with an item internal/configuration failure and no retryable send outcome (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" "503": description: Pre-evaluation transient dependency failure (ApiError), or zero accepted with an unconfirmed/retryable send, deadline or blocked outcome (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" put: summary: Submit a batch of product-listing upserts asynchronously (Partner API) operationId: putAsyncPartnerProductListings tags: [ProductListings] description: | Submits the same JSON array of up to 100 `UpsertProductListingData` items as the synchronous PUT. Accepts a partner user's Cognito access JWT or Aura access token; delegated access requires `product-listings:write`. No client-supplied queue or authority metadata is accepted. After whole-request JSON/array/size validation, items are parsed and validated independently; invalid items do not prevent eligible siblings from being queued. For a new listing, omitted or null availability, price and estimates create no value; `auction` may be omitted or asserted as an object, not null. For an existing listing, omitted availability, price, estimates and auction facts preserve their values; null clears clearable values and nested auction leaves. `auction.auctionId` may be omitted to preserve membership, null to clear it or an ID to set it. `images: []` clears images, while `images: null` and `auction: null` are invalid. An omitted or null `url` preserves an existing URL; a URL value replaces it. Title and description apply on creation only. Auction existence, listing state, partnership/source authority, lifecycle and other business conflicts are decided downstream, not as an immediate item failure or synchronous `404`. Only confirmed queue admission counts as accepted. `202` means admission, not an applied upsert, rollback, or immediate search visibility. An unconfirmed send may already have been queued. `submissionId` is an opaque correlation identifier, not a job ID; there is no submission status endpoint, batch-level deduplication, or Location header. No created or updated listing IDs or synchronous mutation results are returned. For a non-empty evaluated batch, at least one confirmed acceptance yields `202`, even with item failures. With zero accepted, an unconfirmed send or retryable send/deadline/FIFO blocked outcome yields `503`; otherwise internal/configuration failures yield `500`; otherwise all encoded-message-size failures yield `413`; remaining validation/value/size failures yield `400`. An empty array returns `202` after authentication and capability checks, with no sends. Every evaluated report uses the shared async batch JSON schema and echoes the effective `Idempotency-Key`, including non-2xx reports. Before evaluation, bad path/key, malformed JSON, non-array, empty text or over-100 array returns `400`; authentication `401`, insufficient capability `403`, whole-body/front-door limit `413`, and pre-evaluation internal/configuration or transient dependency failure `500` or `503`. These request-wide failures use `ApiError` and send nothing. Supply one `Idempotency-Key` for retries; a key is generated if omitted and echoed on evaluated responses. A lost response containing a generated key cannot be recovered. After transport uncertainty retry the exact unchanged ordered logical batch with the same key, including rejected items at their original zero-based indices; never compact, reorder or change its content under that key. Per-command downstream receipts deduplicate independently; the submission ID does not suppress the batch. Correct definitely invalid items separately with a new key, excluding accepted/uncertain siblings. A same-group eligible successor is blocked when its FIFO predecessor is unresolved; unrelated groups may still be accepted. FIFO covers shared-queue acceptance per group, not execution order. parameters: - name: listingSourceId in: path required: true description: Strict `ls_` ListingSource TypeID. schema: { type: string, example: ls_6rd827eqfefsva9teecmwa3ate } - $ref: "#/components/parameters/AsyncProductListingIdempotencyKey" security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Same array and item contract as synchronous PUT; individual invalid items produce report failures, not whole-batch rejection. content: application/json: schema: type: array maxItems: 100 items: $ref: "#/components/schemas/UpsertProductListingData" example: - sourceListingId: baroque-violin-001 title: { text: Baroque Violin, language: en } url: https://my-listing-source.com/product-listings/baroque-violin - sourceListingId: baroque-violin-002 images: [] - sourceListingId: baroque-violin-003 auction: null responses: "202": description: At least one upsert confirmed admitted, or an authorized empty array. May include item-level failures; no upsert completion implied. headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" examples: partial_admission: summary: Three original upserts; indices 0 and 1 confirmed accepted value: submissionId: plis1_example acceptedCount: 2 failures: - { index: 2, sourceListingId: baroque-violin-003, error: BAD_BODY_VALUE, retryable: false, } empty_batch: summary: Empty authorized batch; no sends value: { submissionId: plis1_example, acceptedCount: 0, failures: [], } "400": description: Pre-evaluation invalid request (ApiError), or zero accepted with only item validation/value/size rejections (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" "401": description: Missing or invalid bearer credential; no sends. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Insufficient credential capability; no sends. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "413": description: Whole-body/front-door limit before evaluation (ApiError), or zero accepted with only encoded-message-size item failures (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" "500": description: Pre-evaluation internal/configuration failure (ApiError), or zero accepted with an item internal/configuration failure and no retryable send outcome (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" "503": description: Pre-evaluation transient dependency failure (ApiError), or zero accepted with an unconfirmed/retryable send, deadline or blocked outcome (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" delete: summary: Submit a batch of product-listing withdrawals asynchronously (Partner API) operationId: deleteAsyncPartnerProductListings tags: [ProductListings] description: | Submits the same JSON array of up to 100 `WithdrawProductListingData` items as the synchronous DELETE, without changing that synchronous operation. Each item identifies a listing by `sourceListingId`; withdrawal is reversible, not physical deletion. Accepts a partner user's Cognito access JWT or Aura access token; delegated access requires `product-listings:write`. No client-supplied queue or authority metadata is accepted. After whole-request JSON/array/size validation, items are parsed and validated independently; invalid items do not prevent eligible siblings from being queued. Listing existence, partnership/source authority, lifecycle and other business conflicts are checked downstream, not as immediate item failures or a synchronous `404`. Only confirmed queue admission counts as accepted. `202` means admission, not an applied withdrawal, rollback, or immediate removal from public search. An unconfirmed send may already have been queued. `submissionId` is an opaque correlation identifier, not a job ID; there is no submission status endpoint, batch-level deduplication, or Location header. No synchronous deletion results or withdrawn listing IDs are returned. For a non-empty evaluated batch, at least one confirmed acceptance yields `202`, even with item failures. With zero accepted, an unconfirmed send or retryable send/deadline/FIFO blocked outcome yields `503`; otherwise internal/configuration failures yield `500`; otherwise all encoded-message-size failures yield `413`; remaining validation/value/size failures yield `400`. An empty array returns `202` after authentication and capability checks, with no sends. Every evaluated report uses the shared async batch JSON schema and echoes the effective `Idempotency-Key`, including non-2xx reports. Before evaluation, bad path/key, malformed JSON, non-array, empty text or over-100 array returns `400`; authentication `401`, insufficient capability `403`, whole-body/front-door limit `413`, and pre-evaluation internal/configuration or transient dependency failure `500` or `503`. These request-wide failures use `ApiError` and send nothing. Supply one `Idempotency-Key` for retries; a key is generated if omitted and echoed on evaluated responses. A lost response containing a generated key cannot be recovered. After transport uncertainty retry the exact unchanged ordered logical batch with the same key, including rejected items at their original zero-based indices; never compact, reorder or change its content under that key. Per-command downstream receipts deduplicate independently; the submission ID does not suppress the batch. Correct definitely invalid items separately with a new key, excluding accepted/uncertain siblings. A same-group eligible successor is blocked when its FIFO predecessor is unresolved; unrelated groups may still be accepted. FIFO covers shared-queue acceptance per group, not execution order. parameters: - name: listingSourceId in: path required: true description: Strict `ls_` ListingSource TypeID. schema: { type: string, example: ls_6rd827eqfefsva9teecmwa3ate } - $ref: "#/components/parameters/AsyncProductListingIdempotencyKey" security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Same array and item contract as synchronous DELETE; individual invalid items produce report failures, not whole-batch rejection. content: application/json: schema: type: array maxItems: 100 items: $ref: "#/components/schemas/WithdrawProductListingData" example: - sourceListingId: baroque-violin-001 - sourceListingId: " " - sourceListingId: baroque-violin-003 responses: "202": description: At least one withdrawal confirmed admitted, or an authorized empty array. May include item-level failures; no withdrawal completion implied. headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" examples: partial_admission: summary: Three original withdrawals; indices 0 and 2 confirmed accepted value: submissionId: plis1_example acceptedCount: 2 failures: - { index: 1, error: BAD_BODY_VALUE, retryable: false } empty_batch: summary: Empty authorized batch; no sends value: { submissionId: plis1_example, acceptedCount: 0, failures: [], } "400": description: Pre-evaluation invalid request (ApiError), or zero accepted with only item validation/value/size rejections (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" "401": description: Missing or invalid bearer credential; no sends. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Insufficient credential capability; no sends. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "413": description: Whole-body/front-door limit before evaluation (ApiError), or zero accepted with only encoded-message-size item failures (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" "500": description: Pre-evaluation internal/configuration failure (ApiError), or zero accepted with an item internal/configuration failure and no retryable send outcome (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" "503": description: Pre-evaluation transient dependency failure (ApiError), or zero accepted with an unconfirmed/retryable send, deadline or blocked outcome (batch report). headers: Idempotency-Key: $ref: "#/components/headers/AsyncProductListingIdempotencyKey" content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" application/json: schema: $ref: "#/components/schemas/AsyncProductListingBatchReport" /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 over the exact, untouched request body bytes 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 may be deduplicated during downstream capture; reuse with different evidence fails in the consumer, not as an immediate HTTP `409`. 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` fails during downstream capture (`WOOCOMMERCE_PROVIDER_SOURCE_ORDER_AMBIGUOUS`), not as an immediate HTTP `409`. Admission does not create a provider receipt or raw revision; investigate the failed FIFO command before reconciliation or redrive. The service verifies the signature over untouched body bytes before JSON parsing, then submits mapped observations as `CAPTURE_RAW` to the shared FIFO after bearer/capability checks and source configuration lookup. The consumer checks the current partner/source grant for mapped observations; ignored no-ops check it immediately. Only a matching, confirmed queue acceptance permits `204`; rejected, oversized, unconfirmed, and not-attempted submissions are not acknowledged. The FIFO consumer later commits raw capture with the command receipt; provider receipt and source-order decisions occur during that capture. Authorized ignored create/update events are no-op `204` without queue send or receipt, even when they include a delivery identifier. Retry an uncertain submission with the same signed body, topic, source, actor and delivery identifier where provided; without a delivery identifier, retries cannot recover the original command identity after a lost reply and have no provider receipt-based deduplication guarantee. A lost reply may already have enqueued work; never infer rollback from a non-204. 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 WooCommerce ingestion configuration with a nonblank write-only `webhookSecret` and a configured language. 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 example: ls_6rd827eqfefsva9teecmwa3ate - 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 WooCommerce ingestion configuration's `webhookSecret`. " 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 downstream provider receipt retains only a canonical semantic source-payload SHA-256 evidence digest for 90 days and deduplicates retries when applied. Reuse with different evidence fails during consumer capture, not as an immediate HTTP 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. WooCommerce `description` and `short_description` are accepted but ignored on all topics. They do not populate Aura's canonical product description and are omitted from persisted `source_payload`; provider-receipt evidence digests use this sanitized payload. The webhook signature is still verified over the exact original request body bytes. 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` before submission. Valid timestamps are checked for conflicts only during consumer capture. `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 bearer/capability, source configuration, and exact-body signature are validated. Current partner/source grant authorization for mapped commands happens at consumer execution; ignored no-ops check it before response. A `204` acknowledges either an authorized ignored create/update status (no queue send or receipt) or matching confirmed acceptance of one `CAPTURE_RAW` command by the shared ProductListing FIFO. It does not confirm raw capture, provider receipt persistence, source-order checks, canonical normalization, or search visibility. Those decisions occur when the consumer executes the command. Ignored events do not parse `date_modified_gmt` or construct a provider receipt. When a delivery ID or source timestamp is omitted, no corresponding receipt or source-ordering guarantee is made. 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 type: PATH detail: Missing field 'listingSourceId'. invalid_listing_source_id: summary: Invalid listing source ID format value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: listingSourceId type: PATH detail: must be a valid ListingSource ID 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 type: 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 type: 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 type: 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 type: 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 "413": description: Payload too large — the HTTP body limit or encoded SQS message limit was exceeded before confirmed admission. No queue acceptance is claimed. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: body_limit: value: status: 413 title: Payload Too Large error: BAD_BODY_VALUE encoded_message_limit: value: status: 413 title: Payload Too Large error: PRODUCT_LISTING_INGESTION_PAYLOAD_TOO_LARGE "503": description: Service unavailable — authentication or source lookup is temporarily unavailable, or mapped command admission is rejected retryably, unconfirmed, or not attempted (including timeout). A send may already have succeeded when its acknowledgment was lost; retry unchanged. Uses `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: ProductListing ingestion queue admission is unavailable or unconfirmed value: status: 503 title: Service Unavailable error: PRODUCT_LISTING_TEMPORARILY_UNAVAILABLE "500": description: Internal server error — authentication/source lookup or a non-retryable ingestion setup/encoding failure prevented confirmed admission. A failed response never proves an attempted send was not queued. Uses `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: ProductListing ingestion admission failed internally value: status: 500 title: Internal Server Error error: PRODUCT_LISTING_INTERNAL_ERROR /api/v1/webhooks/loops: post: summary: Apply a signed Loops preference webhook description: >- Accepts one Loops webhook signed over the exact received body bytes. No Aura/Cognito bearer is required; `webhook-id`, `webhook-timestamp`, and `webhook-signature` must form a valid, recent Loops proof. The request body is limited to 64 KiB. The currently supported `webhookSchemaVersion` is `1.0.0`. Supported `eventName` values are `contact.unsubscribed`, `contact.deleted`, `email.unsubscribed`, `contact.mailingList.subscribed`, `contact.mailingList.unsubscribed`, `email.resubscribed`, `email.hardBounced`, and `email.spamReported`. A verified unknown event name is receipted as ignored; malformed events and unsupported payload schema versions return an error for provider retry. `204` means the receipt and any local preference effects committed, or the authenticated delivery was an identical duplicate or a safely ignored event. It does not mean a marketing message was sent or delivered. Conflicting delivery identity returns `409`; temporary database/provider-reader failure or the 10-second processing budget returns retryable `503`. All responses are `no-store`. operationId: postLoopsPreferenceWebhook tags: [Newsletter] security: [] parameters: - name: webhook-id in: header required: true description: Loops delivery identifier used for committed receipt deduplication. schema: { type: string, minLength: 1, maxLength: 256 } - name: webhook-timestamp in: header required: true description: Unix timestamp covered by the Loops signature; stale proofs are rejected. schema: { type: string, pattern: "^[0-9]{1,20}$" } - name: webhook-signature in: header required: true description: Loops HMAC-SHA256 signature over delivery ID, timestamp, and exact raw body bytes. schema: { type: string, maxLength: 8192 } requestBody: required: true description: Loops webhook payload. The service verifies the signature before parsing or mapping the selected event. content: application/json: schema: type: object required: [webhookSchemaVersion, eventName, eventTime] properties: webhookSchemaVersion: { type: string, example: "1.0.0" } eventName: type: string description: Supported event names are listed in the operation description; verified unknown names are ignored and receipted. eventTime: { type: integer, format: int64, description: Provider event time in Unix seconds., } contactIdentity: type: object description: Required for supported contact and email events; verified unknown event names may be safely ignored without it. required: [id, email] properties: id: { type: string } email: { type: string } mailingList: type: object description: Required for supported contact.mailingList events; only the configured marketing list affects consent. required: [id] properties: id: { type: string } additionalProperties: true example: webhookSchemaVersion: "1.0.0" eventName: email.unsubscribed eventTime: 1791373200 contactIdentity: id: contact_fixture email: person@example.test responses: "204": description: Authenticated event receipt and required local effects committed, or identical duplicate/safe ignore. No response body. headers: Cache-Control: schema: { type: string, example: "private, no-store" } "400": description: Malformed body/event, unsupported payload schema version, or invalid authenticated event time. Uses `LOOPS_WEBHOOK_INVALID_BODY`. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } example: { status: 400, title: Bad Request, error: LOOPS_WEBHOOK_INVALID_BODY, } "401": description: Missing, invalid, stale, or ambiguous Loops proof. The response does not distinguish authentication failures. Uses `LOOPS_WEBHOOK_UNAUTHORIZED`. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } example: { status: 401, title: Unauthorized, error: LOOPS_WEBHOOK_UNAUTHORIZED, } "409": description: A verified delivery identifier was previously committed with different body bytes. Uses `LOOPS_WEBHOOK_CONFLICT`. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } example: { status: 409, title: Conflict, error: LOOPS_WEBHOOK_CONFLICT } "413": description: Request body exceeds 64 KiB. Uses `LOOPS_WEBHOOK_PAYLOAD_TOO_LARGE`. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } example: { status: 413, title: Payload Too Large, error: LOOPS_WEBHOOK_PAYLOAD_TOO_LARGE, } "500": description: Redacted internal signing-secret or cryptographic configuration failure. Uses `LOOPS_WEBHOOK_INTERNAL_ERROR`. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } example: { status: 500, title: Internal Server Error, error: LOOPS_WEBHOOK_INTERNAL_ERROR, } "503": description: Temporary persistence/provider-reader failure or exceeded processing budget. Retry the delivery. Uses `LOOPS_WEBHOOK_TEMPORARILY_UNAVAILABLE`. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } example: { status: 503, title: Service Unavailable, error: LOOPS_WEBHOOK_TEMPORARILY_UNAVAILABLE, } /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`. Anonymous successful 200 responses without Authorization use `public, max-age=0, s-maxage=120, stale-if-error=0`; any request with Authorization and all non-200 responses use `private, no-store`. 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 headers: Cache-Control: description: Anonymous edge freshness is 120 seconds; credential-bearing responses are private and not stored. schema: { type: string } example: public, max-age=0, s-maxage=120, stale-if-error=0 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. An anonymous successful 200 response without Authorization uses `public, max-age=0, s-maxage=120, stale-if-error=0`; any request with Authorization and all non-200 responses use `private, no-store`. Responses always contain `item`, the product data. A valid user or delegated-user bearer token adds optional top-level `userState`; credential-bearing responses are not cached. " operationId: getProductListing tags: - ProductListings parameters: - name: productListingId in: path required: true description: Unique identifier of the ProductListing schema: type: string example: pl_5xdb465tg1enarx2knf04mxbxj - 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: Cache-Control: description: Anonymous edge freshness is 120 seconds; credential-bearing responses are private and not stored. schema: { type: string } example: public, max-age=0, s-maxage=120, stale-if-error=0 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. Without `currency`, the response is unchanged. With `currency`, each monetary discovery price/estimate and each previous/current price/estimate change value may include `display` with `currency`, `amount`, `fxRateId` and `capturedAt`. The snapshot is the latest persisted capture at or before this entry's `timestamp`, including an exact capture-time match. Both sides of a change use that entry's snapshot, even if the source currency changed. Conversion uses HalfUp rounding and the requested currency's minor-unit exponent (JPY and KRW: 0; all others: 2). Same-currency amounts retain the snapshot provenance. Missing snapshots or source/target quotes omit `display` for that value; the response still succeeds, and consumers fall back to the labelled source value. ON_REQUEST and null values receive no display projection. No later snapshot, current rates or provider calls are used. Source values, event order and the changes array remain intact. Currency query variants are included in the shared edge cache key. " operationId: getProductListingHistory tags: - ProductListings parameters: - name: productListingId in: path required: true description: Unique ProductListing identifier schema: type: string example: pl_5xdb465tg1enarx2knf04mxbxj - name: currency in: query required: false description: Optional display currency for historical monetary values; omitted means source values only. schema: $ref: "#/components/schemas/CurrencyData" responses: "200": description: Anonymous successful 200 responses without Authorization use `public, max-age=0, s-maxage=300, stale-if-error=0`; any request with Authorization and all errors use `private, no-store`. headers: Cache-Control: schema: type: string example: public, max-age=0, s-maxage=300, stale-if-error=0 content: application/json: schema: type: array items: $ref: "#/components/schemas/ProductListingHistoryEntryData" examples: product_history: summary: Example ProductListing history value: - eventType: PRODUCT_LISTING_DISCOVERED productListingId: pl_5xdb465tg1enarx2knf04mxbxj eventId: evt_7s35w34ftcew1r4cransc55rt4 payload: listingSourceId: ls_6ckwc632p2e2q86nv92g9eyw9q sourceListingId: source-6ba7b810 pricing: price: type: MONETARY currency: EUR amount: 3000 availability: AVAILABLE url: https://listing-source.example/product-listings/6ba7b810 imageCount: 3 auction: lotNumber: "42A" cataloguePosition: 7 biddingOpens: "2026-09-01T08:00:00Z" scheduledCloses: "2026-09-01T10:00:00Z" reportedClosedAt: "2026-09-01T10:15:00Z" timestamp: "2026-09-01T10:00:00Z" - eventType: PRODUCT_LISTING_CHANGED productListingId: pl_5xdb465tg1enarx2knf04mxbxj eventId: evt_2mdmwstjvvfn3rmbakmarshhqg payload: changes: - type: MAIN_PRICE_CHANGED previous: type: MONETARY currency: EUR amount: 3000 current: type: MONETARY currency: EUR amount: 2999 - type: AVAILABILITY_CHANGED previous: AVAILABLE current: SOLD_OUT timestamp: "2026-09-01T10:15:00Z" historical_display: summary: A price change requested in RON, using the snapshot at the event timestamp value: - eventType: PRODUCT_LISTING_CHANGED productListingId: pl_5xdb465tg1enarx2knf04mxbxj eventId: evt_2mdmwstjvvfn3rmbakmarshhqg payload: changes: - type: MAIN_PRICE_CHANGED previous: type: MONETARY currency: EUR amount: 3000 display: currency: RON amount: 15000 fxRateId: fx_0cwktgjtwnf4tb6hf2f8zm0x5k capturedAt: "2026-09-01T10:00:00Z" current: type: MONETARY currency: SEK amount: 29999 display: currency: RON amount: 15000 fxRateId: fx_0cwktgjtwnf4tb6hf2f8zm0x5k capturedAt: "2026-09-01T10:00:00Z" timestamp: "2026-09-01T10:15:00Z" "400": description: Bad request - Invalid parameters, including unsupported currency (BAD_QUERY_PARAMETER_VALUE) 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/FX data or a price conversion overflowed 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`. Ready anonymous 200 responses without Authorization use `public, max-age=0, s-maxage=300, stale-if-error=0`; any request with Authorization uses `private, no-store`. The `202 EmbeddingPending` response and all errors are always `private, no-store`. " operationId: getSimilarProductListings tags: - ProductListings parameters: - name: productListingId in: path required: true description: Unique identifier of the product schema: type: string example: pl_5xdb465tg1enarx2knf04mxbxj - 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: Ready anonymous responses without Authorization are shared for 300 seconds; credential-bearing responses are private and not stored. headers: Cache-Control: schema: { type: string } example: public, max-age=0, s-maxage=300, stale-if-error=0 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. This response is never cached and uses `private, no-store`. headers: Cache-Control: schema: { type: string } example: private, no-store Location: description: URL to poll for similar product-listings once the embedding is available schema: type: string format: uri example: /api/v1/product-listings/pl_5xdb465tg1enarx2knf04mxbxj/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 'ls_6rd827eqfefsva9teecmwa3ate' 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. Any request with an Authorization header is private and not stored, including valid credentials that add user state. Anonymous successful 200 responses use `public, max-age=0, s-maxage=60, stale-if-error=0`; errors use `private, no-store`. Geographic predicates are AND; values in each country or subdivision set are OR. Missing geography never matches. Radius is inclusive in its supplied unit to the same recorded point used by distance ordering. Numeric origins never resolve from addresses/preferences. Distance ordering conflicts with sort and uses lexical text matching. Geographic responses are private, no-store and use eventually consistent OpenSearch projections; location updates can briefly lag in items, ordering, total and continuation. `language` defaults to `en` and `currency` defaults to `EUR`." operationId: simpleSearchProductListings tags: - ProductListings parameters: - name: geography in: query style: deepObject explode: true description: Advertised item geography; source defaults do not confirm individual physical custody. schema: {$ref: '#/components/schemas/GeographicSearchData'} - 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 uniqueItems: true example: - pl_5xdb465tg1enarx2knf04mxbxj - 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 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 uniqueItems: true - name: auctionId in: query required: false description: Exact resolved Auction membership IDs. Repeat up to 100 distinct strict `auc_` TypeIDs; supplied IDs OR together and intersect all other filters. Listings without resolved membership do not match. style: form explode: true schema: type: array maxItems: 100 items: type: string 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: lotBiddingOpens in: query required: false description: "Optional exact lot-bidding-open range (RFC3339 instants). Use `lotBiddingOpens[min]` and/or `lotBiddingOpens[max]` for `[min, max)` bounds. Date-only and absent lot times never match this filter. " style: deepObject explode: true schema: type: object properties: min: type: string format: date-time description: Inclusive minimum exact lot bidding-open instant (RFC3339) example: "2025-05-01T00:00:00Z" max: type: string format: date-time description: Exclusive maximum exact lot bidding-open instant (RFC3339) example: "2025-05-31T23:59:59Z" - name: lotScheduledCloses in: query required: false description: "Optional exact lot-scheduled-close range (RFC3339 instants). Use `lotScheduledCloses[min]` and/or `lotScheduledCloses[max]` for `[min, max)` bounds. Date-only and absent lot times never match this filter. " style: deepObject explode: true schema: type: object properties: min: type: string format: date-time description: Inclusive minimum exact lot scheduled-close instant (RFC3339) example: "2025-05-01T00:00:00Z" max: type: string format: date-time description: Exclusive maximum exact lot scheduled-close instant (RFC3339) 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: type: string example: '{"fxRateId":"fx_0cwktgjtwnf4tb6hf2f8zm0x5k","searchAfter":[2999,"pl_5xdb465tg1enarx2knf04mxbxj"]}' - 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: Anonymous responses without Authorization are shared for 60 seconds; credential-bearing responses are private and not stored. schema: type: string example: public, max-age=0, s-maxage=60, stale-if-error=0 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 type: 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 type: 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: "Delegated Aura access tokens require `search-filters:read`; missing scope returns `403 FORBIDDEN`. 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:read`. " 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: usr_5pvhvpxyhve6ts31n9x8c2y513 userSearchFilterId: sf_4hyqf0gyxveazss7nby5hve5x2 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: - ls_6rd827eqfefsva9teecmwa3ate 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:read` 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: - ls_6rd827eqfefsva9teecmwa3ate 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/sf_4hyqf0gyxveazss7nby5hve5x2 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: usr_5pvhvpxyhve6ts31n9x8c2y513 userSearchFilterId: sf_4hyqf0gyxveazss7nby5hve5x2 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: - ls_6rd827eqfefsva9teecmwa3ate 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: "Delegated Aura access tokens require `search-filters:read`; missing scope returns `403 FORBIDDEN`. 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:read`. " operationId: getUserSearchFilter tags: - Search Filters parameters: - name: userSearchFilterId in: path required: true description: Unique identifier of the search filter schema: type: string example: sf_4hyqf0gyxveazss7nby5hve5x2 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: usr_5pvhvpxyhve6ts31n9x8c2y513 userSearchFilterId: sf_4hyqf0gyxveazss7nby5hve5x2 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: - ls_6rd827eqfefsva9teecmwa3ate 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 type: PATH invalid_object_id: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: userSearchFilterId type: PATH detail: must be a valid UserSearchFilter ID "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:read` 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 example: sf_4hyqf0gyxveazss7nby5hve5x2 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 type: PATH invalid_object_id: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: userSearchFilterId type: PATH detail: must be a valid UserSearchFilter ID "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 example: sf_4hyqf0gyxveazss7nby5hve5x2 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: - ls_6rd827eqfefsva9teecmwa3ate 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: - ls_6rd827eqfefsva9teecmwa3ate 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: usr_5pvhvpxyhve6ts31n9x8c2y513 userSearchFilterId: sf_4hyqf0gyxveazss7nby5hve5x2 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: - ls_6rd827eqfefsva9teecmwa3ate 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 type: PATH invalid_object_id: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: userSearchFilterId type: PATH detail: must be a valid UserSearchFilter ID 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}/matches: get: summary: List persisted matches for a saved search filter description: >- Delegated Aura access tokens require `search-filters:read`; missing scope returns `403 FORBIDDEN`. Returns persisted product-listing matches associated with the authenticated user’s saved search filter. Results are read from stored match state and returned with cursor-based pagination in ascending persisted match-creation order. This endpoint does not execute a live search or preview the filter against current product listings. New matches are discovered only by scheduled matching of active enhanced filters, subject to product-search projection lag. Non-enhanced filters do not discover new matches; their historical matches remain available. Match discovery and notifications are not synchronous with listing changes or filter creation. Requires a Cognito JWT or an Aura access token with `search-filters:read`. operationId: listSearchFilterMatches tags: - Search Filters parameters: - name: userSearchFilterId in: path required: true description: Canonical Aura Historia UserSearchFilter ID. schema: type: string example: sf_4hyqf0gyxveazss7nby5hve5x2 - 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: Currency used for the returned product-listing representation. Defaults to `EUR`. schema: $ref: "#/components/schemas/CurrencyData" example: EUR - name: size in: query required: false description: >- Requested page size. The runtime defaults to 21 and clamps successfully parsed unsigned values to the inclusive range 1..100: `0` becomes 1 and values above 100 become 100. Negative, malformed, and non-integer values are rejected with `BAD_QUERY_PARAMETER_VALUE`. schema: type: integer format: int64 default: 21 example: 21 - name: searchAfter in: query required: false description: >- Cursor returned by the preceding page. It is a JSON string containing `[RFC3339 persisted match creation timestamp, canonical ProductListing ID]`; send the returned tuple as this query parameter to continue. The timestamp and ProductListing ID make the ascending cursor tie-safe. schema: type: string example: '["2026-08-05T12:30:00Z","pl_6zydvb8xqqfcntt760grgm41t4"]' security: - BearerAuth: [] - AccessTokenAuth: [] responses: "200": description: Persisted search-filter matches 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" "400": description: Bad request - invalid path or query parameter content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_search_after: summary: Malformed searchAfter cursor value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: searchAfter type: QUERY detail: searchAfter must contain an RFC3339 timestamp and ProductListing ID. invalid_size: summary: Malformed page size value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: size type: QUERY invalid_search_after_product_listing_id: summary: Noncanonical ProductListing ID in searchAfter value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: searchAfter type: QUERY detail: must be a valid ProductListing ID invalid_search_filter_id: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: userSearchFilterId type: PATH detail: must be a valid UserSearchFilter ID "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:read` 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: SEARCH_FILTER_INTERNAL_ERROR "503": description: Persisted-match details or pricing data 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 example: sf_4hyqf0gyxveazss7nby5hve5x2 - name: productListingId in: path required: true description: Canonical identifier of the matched product schema: type: string example: pl_6zydvb8xqqfcntt760grgm41t4 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: usr_5pvhvpxyhve6ts31n9x8c2y513 userSearchFilterId: sf_4hyqf0gyxveazss7nby5hve5x2 productListingId: pl_6zydvb8xqqfcntt760grgm41t4 originEventId: evt_08nrsmex2je2dvsyh01v2kh78a 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: usr_5pvhvpxyhve6ts31n9x8c2y513 userSearchFilterId: sf_4hyqf0gyxveazss7nby5hve5x2 productListingId: pl_6zydvb8xqqfcntt760grgm41t4 originEventId: evt_08nrsmex2je2dvsyh01v2kh78a 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 type: PATH missing_product_id: summary: Missing product ID value: status: 400 title: Bad Request error: BAD_PATH_PARAMETER_VALUE source: field: productListingId type: PATH invalid_search_filter_id: summary: Invalid search filter ID format value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: userSearchFilterId type: PATH detail: must be a valid UserSearchFilter ID invalid_product_id: summary: Invalid product ID format value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: productListingId type: PATH detail: must be a valid ProductListing ID 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, ProductListing ID]." schema: type: string example: '["2026-08-04T12:34:56Z", "pl_5xdb465tg1enarx2knf04mxbxj"]' 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: 0 maximum: 100 description: Number of items returned in the current page. searchAfter: type: array items: type: string minItems: 2 maxItems: 2 nullable: true example: items: - item: productListingId: pl_5xdb465tg1enarx2knf04mxbxj productListingTitleSlugId: smartphone-case-a1b2c3 eventId: evt_69wh1chr82fen9vvyk6n3mh80m listingSourceId: ls_6rd827eqfefsva9teecmwa3ate 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: pl_5xdb465tg1enarx2knf04mxbxj 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: usr_5pvhvpxyhve6ts31n9x8c2y513 productListingId: pl_5xdb465tg1enarx2knf04mxbxj 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: | Delegated Aura access tokens require `listing-sources:write`; missing scope returns `403 FORBIDDEN`. 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. A WooCommerce ingestion configuration carries its required `webhookSecret` as a write-only member; it 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/ls_6rd827eqfefsva9teecmwa3ate 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: | Delegated Aura access tokens require `listing-sources:read`; missing scope returns `403 FORBIDDEN`. 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 ListingSource ID `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 ID filter. schema: type: string example: ls_6rd827eqfefsva9teecmwa3ate - 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 ID filter. schema: type: string example: pty_11bczr8va9ek4abckdkrqda538 - 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 ID cursor returned by the previous page. It is omitted when no next page exists. schema: type: string example: ls_01jw7j4azge008000000000003 - 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: ls_6rd827eqfefsva9teecmwa3ate listingSourceSlugId: antiques-and-more-550e8400-e29b-41d4-a716-446655440000 name: Antiques and More operator: partyId: pty_11bczr8va9ek4abckdkrqda538 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: ls_6rd827eqfefsva9teecmwa3ate "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_object_id: summary: Invalid ListingSource ID value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: listingSourceId type: QUERY detail: Query parameter 'listingSourceId' must be a valid ListingSource ID invalid_query: summary: Invalid 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: | Delegated Aura access tokens require `parties:write`; missing scope returns `403 FORBIDDEN`. 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/pty_4k4snxc3pkecc9yw66heemzyp0 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: pty_4k4snxc3pkecc9yw66heemzyp0 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: | Delegated Aura access tokens require `parties:read`; missing scope returns `403 FORBIDDEN`. 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 ListingSource ID `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 ID cursor returned by the previous page. It is omitted when no next page exists. schema: type: string example: pty_01jw7j4azge008000000000002 - 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: pty_4k4snxc3pkecc9yw66heemzyp0 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: pty_4k4snxc3pkecc9yw66heemzyp0 "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 valid Party ID." 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}: delete: summary: Hard-delete an unused Party as admin description: | Delegated Aura access tokens require `parties:write`; missing scope returns `403 FORBIDDEN`. Deletes only an unused Party after commit. A Party with any ListingSource or retained Partnership, including `DISSOLVED`, returns `409 CONFLICT`; no dependent business or historical row is cascaded, detached, rewritten, or archived. Responses use `Cache-Control: no-store` and this operation accepts no request body. operationId: adminDeleteParty tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: partyId in: path required: true description: Stable Party identifier. schema: type: string example: pty_4k4snxc3pkecc9yw66heemzyp0 responses: "204": description: Party deleted successfully. headers: Cache-Control: description: Response is never stored. schema: type: string example: no-store "400": description: Invalid Party ID. 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. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "404": description: Party was not found, including after a successful prior delete. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "409": description: Party has a ListingSource or retained Partnership, or changed concurrently. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "503": description: Party persistence is temporarily unavailable. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "500": description: Party persistence failed internally. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" patch: summary: Update a Party as admin description: | Delegated Aura access tokens require `parties:write`; missing scope returns `403 FORBIDDEN`. 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 example: pty_4k4snxc3pkecc9yw66heemzyp0 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: pty_4k4snxc3pkecc9yw66heemzyp0 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 ID, empty/malformed body, or invalid Party value. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: invalid_party_id: summary: Invalid Party ID value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: partyId type: PATH detail: must be a valid Party ID 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: | Delegated Aura access tokens require `parties:read`; missing scope returns `403 FORBIDDEN`. Gets one Party for an authenticated administrator. `partyId` must be a valid Party ID. 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 example: pty_4k4snxc3pkecc9yw66heemzyp0 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: pty_4k4snxc3pkecc9yw66heemzyp0 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 ID. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: partyId type: PATH detail: must be a valid Party ID "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 User ID 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 a valid User ID. The cursor is valid only with the same filters and sort used for the previous page. " schema: type: string example: usr_01jw7j4azge008000000000004 - 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: usr_5pvhvpxyhve6ts31n9x8c2y513 email: ada.admin@example.com firstName: Ada lastName: Lovelace tier: ULTIMATE role: ADMIN stripeCustomerId: cus_admin_550e8400e29b41d4a716446655440000 - userId: usr_681w9znhe8eawrhebxd1046d44 email: grace.user@example.com firstName: Grace lastName: Hopper tier: PRO role: USER size: 2 searchAfter: usr_681w9znhe8eawrhebxd1046d44 "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 type: 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 type: 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 type: QUERY detail: "searchAfter must contain a valid User ID." invalid_size: summary: Invalid page size value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: size type: 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 User ID for the user to retrieve. schema: type: string example: usr_5pvhvpxyhve6ts31n9x8c2y513 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: usr_5pvhvpxyhve6ts31n9x8c2y513 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 User ID for the user to update. schema: type: string example: usr_5pvhvpxyhve6ts31n9x8c2y513 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: usr_5pvhvpxyhve6ts31n9x8c2y513 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 'usr_5pvhvpxyhve6ts31n9x8c2y513' 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 User ID for the user to delete. schema: type: string example: usr_5pvhvpxyhve6ts31n9x8c2y513 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 invalid. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: userId type: PATH detail: must be a valid User ID "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 valid User ID. `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 User ID for the user to suspend. schema: type: string example: usr_5pvhvpxyhve6ts31n9x8c2y513 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: usr_5pvhvpxyhve6ts31n9x8c2y513 suspended: true "400": description: Invalid target User ID 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_OBJECT_ID source: field: userId type: PATH detail: must be a valid User ID 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 valid User ID 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 User ID for the user to reactivate. schema: type: string example: usr_5pvhvpxyhve6ts31n9x8c2y513 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: usr_5pvhvpxyhve6ts31n9x8c2y513 suspended: false "400": description: Invalid target User ID 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_OBJECT_ID source: field: userId type: PATH detail: must be a valid User ID "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 valid User ID 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 User ID for the user whose Cognito sessions are revoked. schema: type: string example: usr_5pvhvpxyhve6ts31n9x8c2y513 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 User ID. 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_OBJECT_ID source: field: userId type: PATH detail: must be a valid User ID "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 valid User ID and the target user must exist. Expired and current tokens are included. Results use bounded keyset pagination in fixed `created ASC, AccessToken ID ASC` order. Page sizes are clamped to 1–100, with a default of 21. `searchAfter` is a JSON-encoded `[created RFC3339 timestamp, AccessToken ID]` 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 User ID for the user whose token metadata is listed. schema: type: string example: usr_5pvhvpxyhve6ts31n9x8c2y513 - 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, AccessToken ID]` cursor from the previous page. schema: type: string example: '["2026-09-04T12:00:00Z","at_4ck23tcv0meqqrbyjaka24ra6g"]' 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: usr_5pvhvpxyhve6ts31n9x8c2y513 accessTokenId: at_4ck23tcv0meqqrbyjaka24ra6g name: Incident review token scopes: - users:read origin: User expires: "2026-09-30T00:00:00Z" size: 21 searchAfter: - "2026-09-04T12:00:00Z" - at_4ck23tcv0meqqrbyjaka24ra6g "400": description: Bad request - invalid target User ID, 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_OBJECT_ID source: field: userId type: PATH detail: must be a valid User ID invalid_query: summary: Invalid page query value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: searchAfter 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" 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 valid User ID 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 User ID for the user whose tokens are revoked. schema: type: string example: usr_5pvhvpxyhve6ts31n9x8c2y513 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 invalid. content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: userId type: PATH detail: must be a valid User ID "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 valid User and AccessToken IDs, respectively. 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 User ID for the user whose token is revoked. schema: type: string example: usr_5pvhvpxyhve6ts31n9x8c2y513 - name: accessTokenId in: path required: true description: Unique AccessToken ID for the access token to revoke. schema: type: string example: at_4ck23tcv0meqqrbyjaka24ra6g 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 invalid. 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_OBJECT_ID source: field: userId type: PATH detail: must be a valid User ID invalid_access_token_id: summary: Invalid access-token identifier value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: accessTokenId type: PATH detail: must be a valid AccessToken ID "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: usr_5pvhvpxyhve6ts31n9x8c2y513 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: usr_5pvhvpxyhve6ts31n9x8c2y513 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 response includes the plaintext bearer token once. List, get, and update responses contain only secret-free metadata and never return the bearer token again. Accepts Cognito access JWTs or Aura Historia opaque bearer access tokens. Aura Historia access tokens require the delegated `access-tokens:write` capability. " operationId: postMyAccessToken tags: - User Account security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Access-token creation payload. content: application/json: schema: $ref: "#/components/schemas/PostAccessTokenData" example: name: Partner product sync scopes: - product-listings:write expires: "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/CreatedAccessTokenData" example: userId: usr_5pvhvpxyhve6ts31n9x8c2y513 accessTokenId: at_4ck23tcv0meqqrbyjaka24ra6g accessToken: aurahistoria_abcdefghijk_abcdefghijklmnopqrstuvwxyz1234567 "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: Request body is required. invalid_scope: summary: Unsupported access-token scope value value: status: 400 title: Bad Request error: BAD_BODY_VALUE detail: "Unsupported scope 'unsupported:scope'." "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 source: field: Authorization type: HEADER detail: Bearer token is invalid. "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 all of the authenticated user's persisted Aura Historia access tokens, including expired and current tokens. The response contains secret-free metadata only. It does not return plaintext or masked token values. Accepts Cognito access JWTs or Aura Historia opaque bearer access tokens. Aura Historia access tokens require the delegated `access-tokens:read` capability. " operationId: getMyAccessTokens tags: - User Account security: - BearerAuth: [] - AccessTokenAuth: [] 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/OwnAccessTokenData" examples: one_token: summary: One persisted access token value: - userId: usr_5pvhvpxyhve6ts31n9x8c2y513 accessTokenId: at_4ck23tcv0meqqrbyjaka24ra6g name: Partner product sync scopes: - product-listings:write origin: User expires: "2026-06-30T00:00:00Z" no_tokens: summary: No persisted access tokens exist 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 source: field: Authorization type: HEADER detail: Bearer token is invalid. "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. The response contains secret-free metadata only and never returns plaintext or masked token values. Accepts Cognito access JWTs or Aura Historia opaque bearer access tokens. Aura Historia access tokens require the delegated `access-tokens:write` capability. " operationId: patchMyAccessToken tags: - User Account security: - BearerAuth: [] - AccessTokenAuth: [] requestBody: required: true description: Partial access-token update payload. content: application/json: schema: $ref: "#/components/schemas/PatchAccessTokenData" example: accessTokenId: at_4ck23tcv0meqqrbyjaka24ra6g 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/OwnAccessTokenData" example: userId: usr_5pvhvpxyhve6ts31n9x8c2y513 accessTokenId: at_4ck23tcv0meqqrbyjaka24ra6g name: Renamed partner sync token scopes: - product-listings:write origin: User expires: "2026-07-31T00: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: Request body is required. invalid_access_token_id: summary: Invalid access token identifier in request body value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: accessTokenId type: BODY detail: must be a valid AccessToken ID "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 source: field: Authorization type: HEADER detail: Bearer token is invalid. "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 persisted Aura Historia access token owned by the authenticated user, including expired and current tokens. The response contains secret-free metadata only. It does not return plaintext or masked token values. Accepts Cognito access JWTs or Aura Historia opaque bearer access tokens. Aura Historia access tokens require the delegated `access-tokens:read` capability. " operationId: getMyAccessToken tags: - User Account security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: accessTokenId in: path required: true description: Unique identifier of the access token schema: type: string example: at_4ck23tcv0meqqrbyjaka24ra6g 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/OwnAccessTokenData" example: userId: usr_5pvhvpxyhve6ts31n9x8c2y513 accessTokenId: at_4ck23tcv0meqqrbyjaka24ra6g name: Partner product sync scopes: - product-listings:write origin: User expires: "2026-06-30T00:00: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 type: PATH detail: Missing field 'accessTokenId'. invalid_access_token_id: summary: Invalid access-token identifier value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: accessTokenId type: PATH detail: must be a valid AccessToken ID "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 source: field: Authorization type: HEADER detail: Bearer token is invalid. "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 example: at_4ck23tcv0meqqrbyjaka24ra6g 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 type: PATH detail: Missing field 'accessTokenId'. invalid_access_token_id: summary: Invalid access-token identifier value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: accessTokenId type: PATH detail: must be a valid AccessToken ID "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 source: field: Authorization type: HEADER detail: Bearer token is invalid. "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 example: pl_5xdb465tg1enarx2knf04mxbxj 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 type: PATH invalid_object_id: summary: Invalid product ID format value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: productListingId type: PATH detail: must be a valid ProductListing ID "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 example: pl_5xdb465tg1enarx2knf04mxbxj - 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: pl_5xdb465tg1enarx2knf04mxbxj productListingTitleSlugId: smartphone-case-a1b2c3 eventId: evt_69wh1chr82fen9vvyk6n3mh80m source: listingSourceId: ls_6rd827eqfefsva9teecmwa3ate 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 type: PATH invalid_object_id: summary: Invalid product ID format value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: productListingId type: PATH detail: must be a valid ProductListing ID 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: "Delegated Aura access tokens require `notifications:read`; missing scope returns `403 FORBIDDEN`. 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 ID]` 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" "403": description: Forbidden - required delegated scope missing 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: Delegated Aura access tokens require `notifications:write`; missing scope returns `403 FORBIDDEN`. 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" "403": description: Forbidden - required delegated scope missing 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 description: Delegated Aura access tokens require `notifications:write`; missing scope returns `403 FORBIDDEN`. operationId: deleteNotifications tags: - Notifications security: - BearerAuth: [] - AccessTokenAuth: [] responses: "204": description: Notifications deleted. "403": description: Forbidden - required delegated scope missing 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/all: patch: summary: Update all notification seen states description: Delegated Aura access tokens require `notifications:write`; missing scope returns `403 FORBIDDEN`. 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" "403": description: Forbidden - required delegated scope missing 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 description: Delegated Aura access tokens require `notifications:write`; missing scope returns `403 FORBIDDEN`. operationId: updateNotificationSeen tags: - Notifications security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: notificationId in: path required: true schema: type: string 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" "403": description: Forbidden - required delegated scope missing 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 description: Delegated Aura access tokens require `notifications:write`; missing scope returns `403 FORBIDDEN`. operationId: deleteNotification tags: - Notifications security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: notificationId in: path required: true schema: type: string responses: "204": description: Notification deleted. "400": description: Bad request content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" "403": description: Forbidden - required delegated scope missing 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: Request newsletter confirmation description: | Requests double opt-in confirmation for the submitted email and optional profile. The endpoint accepts anonymous requests. An optional Cognito JWT or Aura Historia access-token bearer credential may be supplied; an invalid supplied credential returns 401. For an authenticated user, omitted profile values can fall back to that user's stored profile, but the credential never proves ownership of the submitted mailbox and never skips confirmation, including when the submitted address exactly matches the caller's account email. The body is limited to 8 KiB. Every result is `Cache-Control: no-store`. Empty 204 means the request was handled, including when rate suppression applies. It does not reveal account or pending-challenge state, confirm a subscription, or mean that email was delivered or Loops was called. Loops synchronization occurs asynchronously after confirmation. operationId: putNewsletterSubscription tags: - Newsletter security: - BearerAuth: [] - {} requestBody: required: true description: Newsletter confirmation request data. content: application/json: schema: $ref: "#/components/schemas/PutNewsletterSubscriptionData" examples: minimal: summary: Request confirmation with email only value: email: collector@example.com complete: summary: Request confirmation with explicit profile values value: email: collector@example.com firstName: Ada lastName: Lovelace language: en currency: EUR responses: "204": description: Confirmation request handled. The requested mailbox may be pending, already confirmed, or rate suppressed. headers: Cache-Control: description: Shared caching is disabled for the response. schema: type: string example: no-store "400": description: Missing, malformed, oversized, or invalid request body. headers: Cache-Control: description: Shared caching is disabled for the response. 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 invalid_body: summary: Invalid request body value: status: 400 title: Bad Request error: BAD_BODY_VALUE "401": description: A supplied bearer credential is invalid. headers: Cache-Control: description: Shared caching is disabled for the response. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 401 title: Unauthorized error: INVALID_CREDENTIALS "503": description: Newsletter persistence, confirmation-template retrieval, or SES is temporarily unavailable, SES throttling was rejected, or SES acceptance is ambiguous. headers: Cache-Control: description: Shared caching is disabled for the response. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: NEWSLETTER_TEMPORARILY_UNAVAILABLE "500": description: Internal/configuration failure, including a permanent definite SES rejection. headers: Cache-Control: description: Shared caching is disabled for the response. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 500 title: Internal Server Error error: NEWSLETTER_INTERNAL_ERROR /api/v1/newsletter-subscriptions/confirm: post: summary: Confirm a newsletter subscription description: | Confirms the newsletter subscription using the bearer capability in the JSON body. This anonymous route accepts no Aura bearer credential. The body is limited to 8 KiB and the token to 512 bytes. Every result is `Cache-Control: no-store`. Empty 204 means the C09 confirmation, consent-state, and asynchronous Loops intent transaction committed; replaying a retained confirmed token also returns 204 without another grant. It does not mean Loops accepted or applied the later provider update. Tokens are not accepted in paths, query parameters, or GET requests. operationId: confirmNewsletterSubscription tags: - Newsletter security: [] requestBody: required: true description: The opaque confirmation capability. content: application/json: schema: $ref: "#/components/schemas/ConfirmNewsletterSubscriptionData" example: token: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" responses: "204": description: Confirmation committed, or an already confirmed token was replayed. headers: Cache-Control: description: Shared caching is disabled for the response. schema: type: string example: no-store "400": description: Malformed, oversized, or wrong-shape body, or one generic invalid, unknown, expired, or invalidated confirmation result. headers: Cache-Control: description: Shared caching is disabled for the response. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" examples: malformed_body: summary: Body does not match the token-only schema value: status: 400 title: Bad Request error: BAD_BODY_VALUE invalid_confirmation: summary: Invalid or unavailable confirmation capability value: status: 400 title: Bad Request error: NEWSLETTER_CONFIRMATION_INVALID "503": description: Newsletter confirmation persistence is temporarily unavailable. headers: Cache-Control: description: Shared caching is disabled for the response. schema: type: string example: no-store content: application/problem+json: schema: $ref: "#/components/schemas/ApiError" example: status: 503 title: Service Unavailable error: NEWSLETTER_TEMPORARILY_UNAVAILABLE "500": description: Internal confirmation-state failure. headers: Cache-Control: description: Shared caching is disabled for the response. schema: type: string example: no-store 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: | Delegated Aura access tokens require `listing-sources:read`; missing scope returns `403 FORBIDDEN`. Gets one ListingSource for an authenticated administrator. `listingSourceId` must be a valid ListingSource ID. 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 responses: "200": description: ListingSource returned successfully. content: application/json: schema: $ref: "#/components/schemas/ListingSourceData" "400": description: Bad request - invalid ListingSource ID. 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: | Delegated Aura access tokens require `listing-sources:write`; missing scope returns `403 FORBIDDEN`. Updates one ListingSource for an authenticated administrator. `listingSourceId` must be a valid ListingSource ID. Omitted members remain unchanged; `null` clears only `url`, `image`, and `referralConfiguration`. `name` and `ingestionConfiguration` are non-nullable and cannot be cleared. Supplying `ingestionConfiguration` replaces the whole configuration list and its methods must match the ListingSource ingestion methods. A WooCommerce entry may omit `webhookSecret` only when WooCommerce is already configured; omission preserves the existing secret, a string rotates it, and `null` is invalid. 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 example: ls_6rd827eqfefsva9teecmwa3ate 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 clear_values: summary: Clear nullable ListingSource values value: 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: ls_6rd827eqfefsva9teecmwa3ate listingSourceSlugId: antiques-and-more-550e8400-e29b-41d4-a716-446655440000 "400": description: Bad request - invalid ListingSource ID, 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 ID value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: listingSourceId type: PATH detail: must be a valid ListingSource ID 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: | Delegated Aura access tokens require `listing-sources:write`; missing scope returns `403 FORBIDDEN`. 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 responses: "204": description: ListingSource removed. The response body is empty. headers: Cache-Control: schema: type: string example: no-store "400": description: Invalid ListingSource ID (`INVALID_OBJECT_ID`). 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/clients/{clientId}: get: summary: Get OAuth client metadata for consent description: | Reads the authoritative registered metadata for one OAuth client before the user begins or completes consent. This is a read only: it does not record consent, issue an authorization code or token, or grant privileges. Any signed-in ordinary user can read a registered client without the ADMIN role, client ownership, an existing client token, or a source partnership. Only a Cognito access JWT is accepted through `BearerAuth`; Cognito ID tokens, Aura opaque access tokens, and client-secret credentials are rejected. Centralized account restrictions continue to apply. Redirect URIs and allowed scopes are returned exactly as registered; `scope: []` means the client has no allowed scopes and does not imply user consent. `/api/v1/oauth/authorize` remains authoritative and revalidates registration, exact redirect URI, requested-scope subset, and S256 PKCE before issuing a code. Every success and error response uses `Cache-Control: no-store`. operationId: getOAuthConsentClient tags: - OAuth security: - BearerAuth: [] parameters: - name: clientId in: path required: true description: Canonical OAuthClient ID (`oc_` TypeID). schema: type: string example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 responses: "200": description: Secret-free registered consent metadata retrieved successfully. headers: Cache-Control: description: Success responses are never stored. schema: type: string example: no-store content: application/json: schema: $ref: "#/components/schemas/OAuthClientConsentMetadataData" example: client_id: oc_7b0gwc1x49e0mvbt3tfexfqgb5 client_name: Aura Historia WooCommerce tos_uri: https://integration.example/terms policy_uri: https://integration.example/privacy client_uri: https://integration.example logo_uri: https://integration.example/logo.png redirect_uris: - https://stage.aura-historia.com/api/oauth/client/redirect-broker/woocommerce scope: - product-listings:write "400": description: Malformed or non-canonical OAuthClient ID (`INVALID_OBJECT_ID`); the problem source has `field` `clientId` and `type` `PATH`. 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_OBJECT_ID source: field: clientId type: PATH detail: must be a valid OAuthClient ID "401": description: Missing, invalid, expired, or unsupported bearer credentials, including ID tokens, opaque Aura tokens, and client-secret credentials (`INVALID_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 "404": description: Well-formed OAuthClient ID has no current registration (`OAUTH_CLIENT_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 OAuth metadata read failure (`OAUTH_INTERNAL_ERROR`); internal details are not returned. 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 metadata reader failure (`OAUTH_TEMPORARILY_UNAVAILABLE`) or temporary authentication dependency failure (`AUTH_TEMPORARILY_UNAVAILABLE`); no database or credential details are returned. 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/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: Canonical OAuthClient ID. schema: type: string example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 - 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 type: 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 type: 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 type: 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 type: 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 type: 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 type: 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 type: QUERY detail: relative URL without a base invalid_client_id: summary: Invalid OAuth client identifier value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: client_id type: QUERY detail: must be a valid OAuthClient ID "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 description: Canonical OAuthClient ID. example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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 OAuthClient ID, `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 type: 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 type: 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 type: BODY detail: relative URL without a base invalid_client_id: summary: Invalid OAuth client identifier value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: client_id type: BODY detail: must be a valid OAuthClient ID "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 type: PATH detail: Missing path parameter thirdPartyCode invalid_third_party_code: summary: Invalid third-party exchange code value: status: 400 title: Bad Request error: BAD_QUERY_PARAMETER_VALUE source: field: thirdPartyCode type: PATH detail: Path parameter 'thirdPartyCode' is invalid. 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 description: Canonical OAuthClient ID. example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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 OAuthClient ID, 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 type: BODY detail: Missing form field token invalid_client_id: summary: Invalid OAuth client identifier value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: client_id type: BODY detail: must be a valid OAuthClient ID "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 description: Canonical OAuthClient ID. example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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: oc_7b0gwc1x49e0mvbt3tfexfqgb5 sub: usr_4hzreqwxgvef69j056bszwwm17 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 OAuthClient ID, 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 type: BODY detail: Missing form field token invalid_client_id: summary: Invalid OAuth client identifier value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: client_id type: BODY detail: must be a valid OAuthClient ID "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 OAuthClient ID ascending order. Page sizes are clamped to 1–100, with a default of 21. The `searchAfter` value is a JSON-encoded `[created RFC3339 timestamp, OAuthClient ID]` cursor; it is omitted on the terminal page and must be sent with the same filters. `clientId` is an exact OAuthClient ID 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 OAuthClient ID filter. schema: type: string example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 - 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, OAuthClient ID]` cursor returned by the previous page. schema: type: string example: '["2026-09-04T12:00:00Z","oc_7b0gwc1x49e0mvbt3tfexfqgb5"]' - 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: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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" - oc_7b0gwc1x49e0mvbt3tfexfqgb5 "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 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" 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/oc_7b0gwc1x49e0mvbt3tfexfqgb5 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: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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: Canonical OAuthClient ID. schema: type: string example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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 OAuthClient 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" example: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: clientId type: PATH detail: must be a valid OAuthClient ID "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: Canonical OAuthClient ID. schema: type: string example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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_OBJECT_ID source: field: clientId type: PATH detail: must be a valid OAuthClient ID 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: Canonical OAuthClient ID. schema: type: string example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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 OAuthClient 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" example: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: clientId type: PATH detail: must be a valid OAuthClient ID "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: get: summary: Search public ListingSources description: Anonymous, literal partial-name search and browse of every persisted ListingSource. Optional valid credentials never widen the public representation; invalid supplied credentials are rejected. Query values are NFC/Unicode-whitespace canonicalized. One scalar and punctuation-only input return a terminal empty collection. operationId: searchPublicListingSources tags: - ListingSources security: - {} - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: query in: query schema: type: string maxLength: 256 description: Optional contiguous literal text. Only `query`, `size`, and `searchAfter` are accepted once; unknown or repeated parameters return 400. - name: size in: query schema: type: integer minimum: 1 maximum: 50 default: 21 - name: searchAfter in: query schema: type: string description: Opaque URL-safe base64 continuation from this endpoint. Omit on the first page and restart after a query or size change. responses: "200": description: Anonymous successful 200 responses without Authorization use `public, max-age=0, s-maxage=300, stale-if-error=0`; any request with Authorization and all errors use `private, no-store`. headers: Cache-Control: schema: { type: string } example: public, max-age=0, s-maxage=300, stale-if-error=0 content: application/json: schema: $ref: "#/components/schemas/PublicListingSourceSearchCollectionData" "400": description: Invalid query parameter or continuation. Cache-Control is no-store. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } "401": description: Invalid supplied credential. Cache-Control is no-store. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } "500": description: Invalid persisted public read model. Cache-Control is no-store. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } "503": description: Temporary public-read failure. Cache-Control is no-store. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } /api/v1/listing-sources/by-slug/{listingSourceSlugId}: get: summary: Get a public ListingSource by immutable slug description: Anonymous exact immutable-slug lookup. This route accepts no query parameters. Optional valid credentials never widen the public representation; invalid supplied credentials are rejected. operationId: getPublicListingSourceBySlug tags: - ListingSources security: - {} - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listingSourceSlugId in: path required: true schema: type: string description: Exact persisted immutable ListingSource slug. responses: "200": description: Anonymous successful 200 responses without Authorization use `public, max-age=0, s-maxage=300, stale-if-error=0`; any request with Authorization and all errors use `private, no-store`. headers: Cache-Control: schema: { type: string } example: public, max-age=0, s-maxage=300, stale-if-error=0 content: application/json: schema: $ref: "#/components/schemas/PublicListingSourceData" "400": description: Invalid slug or nonempty query string. Cache-Control is no-store. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } "401": description: Invalid supplied credential. Cache-Control is no-store. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } "404": description: LISTING_SOURCE_NOT_FOUND. Cache-Control is no-store. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } "500": description: Invalid persisted public read model. Cache-Control is no-store. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } "503": description: Temporary public-read failure. Cache-Control is no-store. content: application/problem+json: schema: { $ref: "#/components/schemas/ApiError" } /api/v1/me/listing-sources: get: summary: List caller-administered ListingSources description: Delegated Aura access tokens require `listing-sources:read`; missing scope returns `403 FORBIDDEN`. 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 description: Delegated Aura access tokens require `partnership-applications:read`; missing scope returns `403 FORBIDDEN`. 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 description: Delegated Aura access tokens require `partnership-applications:write`; missing scope returns `403 FORBIDDEN`. 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 description: Delegated Aura access tokens require `partnership-applications:read`; missing scope returns `403 FORBIDDEN`. operationId: getOwnPartnershipApplication tags: - PartnershipApplications parameters: - name: partnershipApplicationId in: path required: true schema: type: string 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 description: Delegated Aura access tokens require `partnership-applications:write`; missing scope returns `403 FORBIDDEN`. operationId: deleteOwnPartnershipApplication tags: - PartnershipApplications parameters: - name: partnershipApplicationId in: path required: true schema: type: string 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: | Delegated Aura access tokens require `admin-overview:read`; missing scope returns `403 FORBIDDEN`. 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: | Delegated Aura access tokens require `partnerships:read`; missing scope returns `403 FORBIDDEN`. Lists active Partnerships for authenticated administrators. Results use bounded keyset cursor pagination in fixed `created` descending, then Partnership ID 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 ID]` 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 object-ID 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 ID filter. schema: type: string example: pty_4k4snxc3pkecc9yw66heemzyp0 - name: memberUserId in: query required: false description: Optional exact member User ID filter. schema: type: string example: usr_5kgk59yyskf6fb0gyc9fxn5gwk - name: listingSourceId in: query required: false description: Optional exact ListingSource grant ID filter. schema: type: string example: ls_0geqhfpd80f6ga8zv98d2vcd1v - name: searchAfter in: query required: false description: JSON-encoded `[created RFC3339 timestamp, Partnership ID]` cursor returned by the previous page. schema: type: string example: '["2026-09-04T12:00:00Z","psh_3q65ndcw7wfxqswjtrv1kfhwd1"]' - 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: psh_3q65ndcw7wfxqswjtrv1kfhwd1 party: partyId: pty_4k4snxc3pkecc9yw66heemzyp0 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" - psh_3q65ndcw7wfxqswjtrv1kfhwd1 "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: | Delegated Aura access tokens require `partnerships:read`; missing scope returns `403 FORBIDDEN`. 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 ID 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 ID. schema: type: string example: psh_3q65ndcw7wfxqswjtrv1kfhwd1 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: psh_3q65ndcw7wfxqswjtrv1kfhwd1 party: partyId: pty_4k4snxc3pkecc9yw66heemzyp0 partySlugId: safe-party name: Safe Party memberUserIds: - usr_5kgk59yyskf6fb0gyc9fxn5gwk - usr_7hqr3y7dfze74sfm30pg9t3nvp listingSourceIds: - ls_1jy6kc0gpnecws80wddqee2rtq - ls_25b9fry680eh9txe84nex6vk2a memberCount: 2 listingSourceGrantCount: 2 created: "2026-09-04T12:00:00Z" updated: "2026-09-04T13:00:00Z" "400": description: Bad request - invalid Partnership 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" example: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: partnershipId type: PATH detail: must be a valid Partnership ID "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: | Delegated Aura access tokens require `partnerships:write`; missing scope returns `403 FORBIDDEN`. 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 ID. schema: type: string 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 ID (`INVALID_OBJECT_ID`). "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: | Delegated Aura access tokens require `partnerships:write`; missing scope returns `403 FORBIDDEN`. 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 ID. schema: type: string example: psh_3q65ndcw7wfxqswjtrv1kfhwd1 - name: listingSourceId in: path required: true description: Existing ListingSource ID to grant to the Partnership. schema: type: string example: ls_1jy6kc0gpnecws80wddqee2rtq 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 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" examples: invalidPartnershipId: value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: partnershipId type: PATH detail: must be a valid Partnership ID invalidListingSourceId: value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: listingSourceId type: PATH detail: must be a valid ListingSource ID "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: | Delegated Aura access tokens require `partnerships:write`; missing scope returns `403 FORBIDDEN`. 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 ID. schema: type: string example: psh_3q65ndcw7wfxqswjtrv1kfhwd1 - name: listingSourceId in: path required: true description: Existing ListingSource ID whose Partnership grant should be revoked. schema: type: string example: ls_1jy6kc0gpnecws80wddqee2rtq 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 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" examples: invalidPartnershipId: value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: partnershipId type: PATH detail: must be a valid Partnership ID invalidListingSourceId: value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: listingSourceId type: PATH detail: must be a valid ListingSource ID "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: | Delegated Aura access tokens require `partnerships:write`; missing scope returns `403 FORBIDDEN`. 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 ID. schema: type: string example: psh_3q65ndcw7wfxqswjtrv1kfhwd1 - name: userId in: path required: true description: Existing User ID to add as a Partnership member. schema: type: string example: usr_5kgk59yyskf6fb0gyc9fxn5gwk 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 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" examples: invalidPartnershipId: value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: partnershipId type: PATH detail: must be a valid Partnership ID invalidUserId: value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: userId type: PATH detail: must be a valid User ID "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: | Delegated Aura access tokens require `partnerships:write`; missing scope returns `403 FORBIDDEN`. 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 ID. schema: type: string example: psh_3q65ndcw7wfxqswjtrv1kfhwd1 - name: userId in: path required: true description: Existing User ID to remove from the Partnership. schema: type: string example: usr_5kgk59yyskf6fb0gyc9fxn5gwk 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 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" examples: invalidPartnershipId: value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: partnershipId type: PATH detail: must be a valid Partnership ID invalidUserId: value: status: 400 title: Bad Request error: INVALID_OBJECT_ID source: field: userId type: PATH detail: must be a valid User ID "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: | Delegated Aura access tokens require `partnership-applications:read`; missing scope returns `403 FORBIDDEN`. 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, PartnershipApplication ID]` `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 ID filter. schema: type: string example: usr_5pvhvpxyhve6ts31n9x8c2y513 - 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 ID filter. schema: type: string example: ls_7h03ebvas3fhj9qb5pr5s22029 - 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, PartnershipApplication ID]` cursor returned by the previous page. schema: type: string example: '["2026-09-04T12:00:00Z","pa_0ckcsff4zne3qr4dsrvqk7fckd"]' - 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: pa_0ckcsff4zne3qr4dsrvqk7fckd applicantUserId: usr_5pvhvpxyhve6ts31n9x8c2y513 state: SUBMITTED proposal: type: EXISTING_LISTING_SOURCE listingSourceId: ls_7h03ebvas3fhj9qb5pr5s22029 approvedPartnershipId: null approvedListingSourceId: null created: "2026-09-04T12:00:00Z" updated: "2026-09-04T12:00:00Z" size: 21 searchAfter: - "2026-09-04T12:00:00Z" - pa_0ckcsff4zne3qr4dsrvqk7fckd "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: "Delegated Aura access tokens require `partnership-applications:read`; missing scope returns `403 FORBIDDEN`. 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 PartnershipApplication ID for the PartnershipApplication to retrieve. schema: type: string example: pa_0ckcsff4zne3qr4dsrvqk7fckd 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: pa_0ckcsff4zne3qr4dsrvqk7fckd applicantUserId: usr_5pvhvpxyhve6ts31n9x8c2y513 state: APPROVED proposal: type: EXISTING_LISTING_SOURCE listingSourceId: ls_7h03ebvas3fhj9qb5pr5s22029 approvedPartnershipId: psh_4a5zxcsd5tf9wb5mgc4vreb3q7 approvedListingSourceId: ls_7h03ebvas3fhj9qb5pr5s22029 "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: | Delegated Aura access tokens require `partnership-applications:write`; missing scope returns `403 FORBIDDEN`. 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 PartnershipApplication ID for the PartnershipApplication to mark in review. schema: type: string example: pa_0ckcsff4zne3qr4dsrvqk7fckd 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: | Delegated Aura access tokens require `partnership-applications:write`; missing scope returns `403 FORBIDDEN`. 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 PartnershipApplication ID for the PartnershipApplication to decide. schema: type: string example: pa_0ckcsff4zne3qr4dsrvqk7fckd 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" /api/v1/me/addresses: post: summary: Save a private address operationId: createUserAddress parameters: - name: Idempotency-Key in: header required: true description: Opaque owner-scoped creation retry key. Reuse with the original body to recover the server-generated ID. Different keys permit equal-content bookmarks. schema: type: string minLength: 1 maxLength: 128 pattern: '^[A-Za-z0-9_.:-]+$' tags: - Users security: - BearerAuth: [] - AccessTokenAuth: [] description: Requires the current unsuspended owner. Delegated credentials require users:write. Success and errors are no-store. The server generates addressId. The body contains label/geography; exactly one valid Idempotency-Key header identifies retries within this owner. Same-key original creation content returns the same entry's current view (201), including after edits. Changed creation content or a deleted entry conflicts. Saving does not synchronously geocode or select a preferred address. responses: '201': description: Successful private operation. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/UserAddress' '400': description: Invalid body (BAD_BODY_VALUE), including a caller-supplied addressId, or missing, repeated or invalid Idempotency-Key (BAD_HEADER_VALUE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer credential required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Current user or delegated scope is not permitted (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Address/owner not found (USER_ADDRESS_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale version or conflicting creation key (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (ADDRESS_BOOK_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Storage or transaction unavailable (ADDRESS_BOOK_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreateUserAddress' get: summary: Paginate own private addresses operationId: listUserAddresses tags: - Users security: - BearerAuth: [] - AccessTokenAuth: [] description: Requires the current unsuspended owner. Delegated credentials require users:read. Success and errors are no-store. Current geocoding state and eligible accepted results are included using bounded batch hydration. responses: '200': description: Successful private operation. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/UserAddressPage' '400': description: Invalid body, query or strict object ID. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer credential required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Current user or delegated scope is not permitted (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Address/owner not found (USER_ADDRESS_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale version or conflicting creation key (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (ADDRESS_BOOK_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Storage or transaction unavailable (ADDRESS_BOOK_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' parameters: - in: query name: after schema: $ref: '#/components/schemas/UserAddressId' - in: query name: limit schema: type: integer minimum: 1 maximum: 100 default: 25 /api/v1/me/addresses/{address_id}: get: summary: Read an own private address operationId: getUserAddress tags: - Users security: - BearerAuth: [] - AccessTokenAuth: [] description: 'Requires the current unsuspended owner. Delegated credentials require users:read. Success and errors are no-store. ' responses: '200': description: Successful private operation. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/UserAddress' '400': description: Invalid body, query or strict object ID. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer credential required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Current user or delegated scope is not permitted (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Address/owner not found (USER_ADDRESS_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale version or conflicting creation key (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (ADDRESS_BOOK_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Storage or transaction unavailable (ADDRESS_BOOK_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' parameters: - in: path name: address_id required: true schema: $ref: '#/components/schemas/UserAddressId' put: summary: Replace an own private address operationId: updateUserAddress tags: - Users security: - BearerAuth: [] - AccessTokenAuth: [] description: Requires the current unsuspended owner. Delegated credentials require users:write. Success and errors are no-store. Full replacement preserves ID. Geographic changes increment inputRevision and fence old enrichment. Label-only changes preserve resolution input. No-op writes require current version and preserve versions. responses: '200': description: Successful private operation. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/UserAddress' '400': description: Invalid body, query or strict object ID. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer credential required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Current user or delegated scope is not permitted (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Address/owner not found (USER_ADDRESS_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale version or conflicting creation key (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (ADDRESS_BOOK_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Storage or transaction unavailable (ADDRESS_BOOK_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UpdateUserAddress' parameters: - in: path name: address_id required: true schema: $ref: '#/components/schemas/UserAddressId' delete: summary: Erase an own private address operationId: deleteUserAddress tags: - Users security: - BearerAuth: [] - AccessTokenAuth: [] description: Requires the current unsuspended owner. Delegated credentials require users:write. Success and errors are no-store. Erases text/enrichment/pending binding. Deleting the preferred address clears only that preference atomically; no alternative is selected. responses: '204': description: Successful private operation. headers: Cache-Control: schema: type: string enum: - no-store '400': description: Invalid body, query or strict object ID. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer credential required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Current user or delegated scope is not permitted (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Address/owner not found (USER_ADDRESS_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale version or conflicting creation key (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (ADDRESS_BOOK_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Storage or transaction unavailable (ADDRESS_BOOK_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/UserAddressExpectedVersion' parameters: - in: path name: address_id required: true schema: $ref: '#/components/schemas/UserAddressId' /api/v1/me/browsing-destination: get: summary: Read own browsing destination operationId: getBrowsingDestination tags: - Users security: - BearerAuth: [] - AccessTokenAuth: [] description: Requires the current unsuspended owner. Delegated credentials require users:read. Success and errors are no-store. An untouched preference has version 1 and NONE. responses: '200': description: Successful private operation. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/BrowsingDestinationView' '400': description: Invalid body, query or strict object ID. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer credential required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Current user or delegated scope is not permitted (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Address/owner not found (USER_ADDRESS_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale version or conflicting creation key (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (ADDRESS_BOOK_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Storage or transaction unavailable (ADDRESS_BOOK_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' put: summary: Select or clear browsing destination operationId: setBrowsingDestination tags: - Users security: - BearerAuth: [] - AccessTokenAuth: [] description: Requires the current unsuspended owner. Delegated credentials require users:write. Success and errors are no-store. Atomic mutually exclusive preference. NONE clears. ADDRESS must belong to current owner. COUNTRY_POSTAL needs no full address. At most one address is preferred. responses: '200': description: Successful private operation. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/BrowsingDestinationView' '400': description: Invalid body, query or strict object ID. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer credential required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Current user or delegated scope is not permitted (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Address/owner not found (USER_ADDRESS_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale version or conflicting creation key (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (ADDRESS_BOOK_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Storage or transaction unavailable (ADDRESS_BOOK_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SetBrowsingDestination' /api/v1/party-locations: get: summary: Discover public Party locations operationId: discoverPartyLocations tags: [Parties] security: [] description: >- Location-oriented discovery of eligible active Party sites. Multiple matching sites of one Party intentionally appear as separate locations. Country/subdivision predicates need no coordinates. Radius or distance ordering requires an explicit numeric origin. Strict proximity requires PREMISES precision; approximate mode retains coarse labels. Ordered by distance when requested, then native location UUID ascending; otherwise UUID ascending. Reads current PostgreSQL locations independently of listing projection catch-up. All responses are private, no-store. Management evidence, addresses and revisions stay private. parameters: - name: geography in: query required: true style: deepObject explode: true schema: $ref: '#/components/schemas/GeographicSearchData' - name: size in: query schema: {type: integer, minimum: 1, maximum: 100, default: 21} - name: searchAfter in: query style: deepObject explode: true schema: $ref: '#/components/schemas/PartyLocationDiscoveryCursorData' responses: '200': description: Matching public locations. content: application/json: schema: type: object required: [items, size] properties: items: type: array items: {$ref: '#/components/schemas/PublicPartyLocationData'} size: {type: integer} searchAfter: {$ref: '#/components/schemas/PartyLocationDiscoveryCursorData'} '400': description: Invalid geographic predicates, origin or continuation (BAD_QUERY_PARAMETER_VALUE). content: application/problem+json: schema: {$ref: '#/components/schemas/ApiError'} '503': description: Location read unavailable (PARTY_TEMPORARILY_UNAVAILABLE). content: application/problem+json: schema: {$ref: '#/components/schemas/ApiError'} /api/v1/parties/{party_id}/locations: post: operationId: createPartyLocation summary: Create a Party location tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: party_id in: path required: true schema: type: string pattern: ^pty_ description: Strict Party TypeID. - name: Idempotency-Key in: header required: true schema: type: string minLength: 1 maxLength: 128 pattern: ^[A-Za-z0-9_.:-]+$ description: Exactly one key. Stable per Party and trusted caller identity; matching retries converge, conflicting command reuse returns 409. responses: '201': description: Created location; matching retries replay the original creation result. headers: Cache-Control: schema: type: string enum: - no-store Location: schema: type: string description: Protected PartyLocation detail path. content: application/json: schema: $ref: '#/components/schemas/PartyLocationData' '400': description: Invalid body/header/path/query (BAD_BODY_VALUE, BAD_HEADER_VALUE, INVALID_OBJECT_ID or BAD_QUERY_PARAMETER_VALUE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Party or eligible location not found (PARTY_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence/transaction unavailable (PARTY_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (PARTY_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing credential scope or Party-location management authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale or exhausted revision, retired-site correction or conflicting idempotency reuse (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' description: Delegated credentials require parties:write. Use cases require administrator/internal authority, membership in this Party's active partnership, or an explicit location management grant. Non-administrator users must be unsuspended. Membership removal or partnership dissolution removes derived authority; independent explicit grants survive. Listing-write scope does not confer access. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CreatePartyLocationData' get: operationId: listPartyLocations summary: List protected Party locations tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: party_id in: path required: true schema: type: string pattern: ^pty_ description: Strict Party TypeID. - name: after in: query schema: $ref: '#/components/schemas/PartyLocationId' description: Exclusive ascending UUID keyset continuation; use previous next in the same Party/view. - name: limit in: query schema: type: integer minimum: 1 maximum: 100 default: 25 responses: '200': description: Successful PartyLocation operation. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/PartyLocationsPage' '400': description: Invalid body/header/path/query (BAD_BODY_VALUE, BAD_HEADER_VALUE, INVALID_OBJECT_ID or BAD_QUERY_PARAMETER_VALUE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Party or eligible location not found (PARTY_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence/transaction unavailable (PARTY_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (PARTY_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing credential scope or Party-location management authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' description: Delegated credentials require parties:read. Use cases require administrator/internal authority, membership in this Party's active partnership, or an explicit location management grant. Non-administrator users must be unsuspended. Membership removal or partnership dissolution removes derived authority; independent explicit grants survive. Listing-write scope does not confer access. /api/v1/parties/{party_id}/locations/{location_id}: get: operationId: getPartyLocation summary: Get a protected Party location tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: party_id in: path required: true schema: type: string pattern: ^pty_ description: Strict Party TypeID. - name: location_id in: path required: true schema: $ref: '#/components/schemas/PartyLocationId' responses: '200': description: Successful PartyLocation operation. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/PartyLocationData' '400': description: Invalid body/header/path/query (BAD_BODY_VALUE, BAD_HEADER_VALUE, INVALID_OBJECT_ID or BAD_QUERY_PARAMETER_VALUE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Party or eligible location not found (PARTY_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence/transaction unavailable (PARTY_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (PARTY_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing credential scope or Party-location management authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' description: Delegated credentials require parties:read. Use cases require administrator/internal authority, membership in this Party's active partnership, or an explicit location management grant. Non-administrator users must be unsuspended. Membership removal or partnership dissolution removes derived authority; independent explicit grants survive. Listing-write scope does not confer access. patch: operationId: correctPartyLocation summary: Correct the same identified site tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: party_id in: path required: true schema: type: string pattern: ^pty_ description: Strict Party TypeID. - name: location_id in: path required: true schema: $ref: '#/components/schemas/PartyLocationId' responses: '200': description: Successful PartyLocation operation. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/PartyLocationData' '400': description: Invalid body/header/path/query (BAD_BODY_VALUE, BAD_HEADER_VALUE, INVALID_OBJECT_ID or BAD_QUERY_PARAMETER_VALUE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Party or eligible location not found (PARTY_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence/transaction unavailable (PARTY_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (PARTY_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing credential scope or Party-location management authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale or exhausted revision, retired-site correction or conflicting idempotency reuse (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' description: Delegated credentials require parties:write. Use cases require administrator/internal authority, membership in this Party's active partnership, or an explicit location management grant. Non-administrator users must be unsuspended. Membership removal or partnership dissolution removes derived authority; independent explicit grants survive. Listing-write scope does not confer access. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/CorrectPartyLocationData' /api/v1/parties/{party_id}/locations/{location_id}/lifecycle: put: operationId: setPartyLocationLifecycle summary: Retire or explicitly restore a Party location tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: party_id in: path required: true schema: type: string pattern: ^pty_ description: Strict Party TypeID. - name: location_id in: path required: true schema: $ref: '#/components/schemas/PartyLocationId' responses: '200': description: Successful PartyLocation operation. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/PartyLocationData' '400': description: Invalid body/header/path/query (BAD_BODY_VALUE, BAD_HEADER_VALUE, INVALID_OBJECT_ID or BAD_QUERY_PARAMETER_VALUE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Party or eligible location not found (PARTY_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence/transaction unavailable (PARTY_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (PARTY_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing credential scope or Party-location management authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale or exhausted revision, retired-site correction or conflicting idempotency reuse (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' description: Delegated credentials require parties:write. Use cases require administrator/internal authority, membership in this Party's active partnership, or an explicit location management grant. Non-administrator users must be unsuspended. Membership removal or partnership dissolution removes derived authority; independent explicit grants survive. Listing-write scope does not confer access. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/SetPartyLocationLifecycleData' /api/v1/admin/parties/{party_id}/location-managers/{user_id}: put: operationId: setPartyLocationManagementGrant summary: Grant or revoke Party location management tags: - Party security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: party_id in: path required: true schema: type: string pattern: ^pty_ description: Strict Party TypeID. - name: user_id in: path required: true schema: type: string pattern: ^usr_ responses: '400': description: Invalid body/header/path/query (BAD_BODY_VALUE, BAD_HEADER_VALUE, INVALID_OBJECT_ID or BAD_QUERY_PARAMETER_VALUE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Party or eligible location not found (PARTY_NOT_FOUND). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence/transaction unavailable (PARTY_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (PARTY_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing credential scope or administrator authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale or exhausted revision, retired-site correction or conflicting idempotency reuse (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '204': description: Grant state applied. headers: Cache-Control: schema: type: string enum: - no-store description: Administrator/internal callers only, including parties:write for delegated credentials. An ordinary location manager cannot grant privileges. Revocation serializes against protected location operations. requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/PartyLocationManagementGrantData' /api/v1/listing-sources/{listing_source_id}/location-default: get: summary: Get source location default operationId: getSourceLocationDefault tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listing_source_id in: path required: true schema: type: string description: Strict ls_ UUIDv7 ListingSource TypeID. description: Requires an unsuspended administrator, or membership in the operator Party active partnership with this exact source grant. Trusted service/system use cases are also supported. Delegated callers additionally need listing-sources:read. Assignment does not mutate Party locations or attribute a listing seller. Protected reads retain private/retired references for traceability but return no location address/point. All responses are no-store. Current-revision no-ops preserve revisions; stale retries conflict. responses: '200': description: Protected default configuration with evidence and expected-revision token, without a copied location address or point. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/SourceLocationDefaultData' '400': description: Invalid body/path (BAD_BODY_VALUE or INVALID_OBJECT_ID). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing scope or current source management authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Source (LISTING_SOURCE_NOT_FOUND) or operator-owned location (PARTY_NOT_FOUND) does not exist. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale/exhausted revision, retired selection or applicability change without an assignment (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (LISTING_SOURCE_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence or transaction unavailable (LISTING_SOURCE_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' put: summary: Assign or replace source location default operationId: assignSourceLocationDefault tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listing_source_id in: path required: true schema: type: string description: Strict ls_ UUIDv7 ListingSource TypeID. description: Requires an unsuspended administrator, or membership in the operator Party active partnership with this exact source grant. Trusted service/system use cases are also supported. Delegated callers additionally need listing-sources:write. Assignment does not mutate Party locations or attribute a listing seller. Protected reads retain private/retired references for traceability but return no location address/point. All responses are no-store. Current-revision no-ops preserve revisions; stale retries conflict. responses: '200': description: Protected default configuration with evidence and expected-revision token, without a copied location address or point. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/SourceLocationDefaultData' '400': description: Invalid body/path (BAD_BODY_VALUE or INVALID_OBJECT_ID). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing scope or current source management authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Source (LISTING_SOURCE_NOT_FOUND) or operator-owned location (PARTY_NOT_FOUND) does not exist. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale/exhausted revision, retired selection or applicability change without an assignment (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (LISTING_SOURCE_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence or transaction unavailable (LISTING_SOURCE_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AssignSourceLocationDefaultData' patch: summary: Change source location applicability operationId: changeSourceLocationDefaultApplicability tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listing_source_id in: path required: true schema: type: string description: Strict ls_ UUIDv7 ListingSource TypeID. description: Requires an unsuspended administrator, or membership in the operator Party active partnership with this exact source grant. Trusted service/system use cases are also supported. Delegated callers additionally need listing-sources:write. Assignment does not mutate Party locations or attribute a listing seller. Protected reads retain private/retired references for traceability but return no location address/point. All responses are no-store. Current-revision no-ops preserve revisions; stale retries conflict. responses: '200': description: Protected default configuration with evidence and expected-revision token, without a copied location address or point. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/SourceLocationDefaultData' '400': description: Invalid body/path (BAD_BODY_VALUE or INVALID_OBJECT_ID). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing scope or current source management authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Source (LISTING_SOURCE_NOT_FOUND) or operator-owned location (PARTY_NOT_FOUND) does not exist. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale/exhausted revision, retired selection or applicability change without an assignment (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (LISTING_SOURCE_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence or transaction unavailable (LISTING_SOURCE_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ChangeSourceLocationDefaultApplicabilityData' delete: summary: Clear source location default operationId: clearSourceLocationDefault tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listing_source_id in: path required: true schema: type: string description: Strict ls_ UUIDv7 ListingSource TypeID. description: Requires an unsuspended administrator, or membership in the operator Party active partnership with this exact source grant. Trusted service/system use cases are also supported. Delegated callers additionally need listing-sources:write. Assignment does not mutate Party locations or attribute a listing seller. Protected reads retain private/retired references for traceability but return no location address/point. All responses are no-store. Current-revision no-ops preserve revisions; stale retries conflict. responses: '200': description: Protected default configuration with evidence and expected-revision token, without a copied location address or point. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/SourceLocationDefaultData' '400': description: Invalid body/path (BAD_BODY_VALUE or INVALID_OBJECT_ID). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing scope or current source management authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Source (LISTING_SOURCE_NOT_FOUND) or operator-owned location (PARTY_NOT_FOUND) does not exist. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale/exhausted revision, retired selection or applicability change without an assignment (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (LISTING_SOURCE_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence or transaction unavailable (LISTING_SOURCE_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ClearSourceLocationDefaultData' /api/v1/admin/listing-sources/{listing_source_id}/location-default: get: summary: Get source location default as administrator operationId: adminGetSourceLocationDefault tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listing_source_id in: path required: true schema: type: string description: Strict ls_ UUIDv7 ListingSource TypeID. description: Requires an unsuspended administrator. Operator-partnership membership or a source grant alone does not authorize this admin route. Trusted service/system use cases are also supported. Delegated callers additionally need listing-sources:read. Assignment does not mutate Party locations or attribute a listing seller. Protected reads retain private/retired references for traceability but return no location address/point. All responses are no-store. Current-revision no-ops preserve revisions; stale retries conflict. responses: '200': description: Protected default configuration with evidence and expected-revision token, without a copied location address or point. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/SourceLocationDefaultData' '400': description: Invalid body/path (BAD_BODY_VALUE or INVALID_OBJECT_ID). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing scope or current unsuspended administrator authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Source (LISTING_SOURCE_NOT_FOUND) or operator-owned location (PARTY_NOT_FOUND) does not exist. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale/exhausted revision, retired selection or applicability change without an assignment (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (LISTING_SOURCE_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence or transaction unavailable (LISTING_SOURCE_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' put: summary: Assign or replace source location default as administrator operationId: adminAssignSourceLocationDefault tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listing_source_id in: path required: true schema: type: string description: Strict ls_ UUIDv7 ListingSource TypeID. description: Requires an unsuspended administrator. Operator-partnership membership or a source grant alone does not authorize this admin route. Trusted service/system use cases are also supported. Delegated callers additionally need listing-sources:write. Assignment does not mutate Party locations or attribute a listing seller. Protected reads retain private/retired references for traceability but return no location address/point. All responses are no-store. Current-revision no-ops preserve revisions; stale retries conflict. responses: '200': description: Protected default configuration with evidence and expected-revision token, without a copied location address or point. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/SourceLocationDefaultData' '400': description: Invalid body/path (BAD_BODY_VALUE or INVALID_OBJECT_ID). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing scope or current unsuspended administrator authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Source (LISTING_SOURCE_NOT_FOUND) or operator-owned location (PARTY_NOT_FOUND) does not exist. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale/exhausted revision, retired selection or applicability change without an assignment (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (LISTING_SOURCE_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence or transaction unavailable (LISTING_SOURCE_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/AssignSourceLocationDefaultData' patch: summary: Change source location applicability as administrator operationId: adminChangeSourceLocationDefaultApplicability tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listing_source_id in: path required: true schema: type: string description: Strict ls_ UUIDv7 ListingSource TypeID. description: Requires an unsuspended administrator. Operator-partnership membership or a source grant alone does not authorize this admin route. Trusted service/system use cases are also supported. Delegated callers additionally need listing-sources:write. Assignment does not mutate Party locations or attribute a listing seller. Protected reads retain private/retired references for traceability but return no location address/point. All responses are no-store. Current-revision no-ops preserve revisions; stale retries conflict. responses: '200': description: Protected default configuration with evidence and expected-revision token, without a copied location address or point. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/SourceLocationDefaultData' '400': description: Invalid body/path (BAD_BODY_VALUE or INVALID_OBJECT_ID). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing scope or current unsuspended administrator authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Source (LISTING_SOURCE_NOT_FOUND) or operator-owned location (PARTY_NOT_FOUND) does not exist. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale/exhausted revision, retired selection or applicability change without an assignment (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (LISTING_SOURCE_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence or transaction unavailable (LISTING_SOURCE_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ChangeSourceLocationDefaultApplicabilityData' delete: summary: Clear source location default as administrator operationId: adminClearSourceLocationDefault tags: - ListingSources security: - BearerAuth: [] - AccessTokenAuth: [] parameters: - name: listing_source_id in: path required: true schema: type: string description: Strict ls_ UUIDv7 ListingSource TypeID. description: Requires an unsuspended administrator. Operator-partnership membership or a source grant alone does not authorize this admin route. Trusted service/system use cases are also supported. Delegated callers additionally need listing-sources:write. Assignment does not mutate Party locations or attribute a listing seller. Protected reads retain private/retired references for traceability but return no location address/point. All responses are no-store. Current-revision no-ops preserve revisions; stale retries conflict. responses: '200': description: Protected default configuration with evidence and expected-revision token, without a copied location address or point. headers: Cache-Control: schema: type: string enum: - no-store content: application/json: schema: $ref: '#/components/schemas/SourceLocationDefaultData' '400': description: Invalid body/path (BAD_BODY_VALUE or INVALID_OBJECT_ID). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '401': description: Bearer authentication required (INVALID_CREDENTIALS). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '403': description: Missing scope or current unsuspended administrator authority (FORBIDDEN). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '404': description: Source (LISTING_SOURCE_NOT_FOUND) or operator-owned location (PARTY_NOT_FOUND) does not exist. headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '409': description: Stale/exhausted revision, retired selection or applicability change without an assignment (CONFLICT). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '500': description: Invalid persisted state or internal failure (LISTING_SOURCE_INTERNAL_ERROR). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' '503': description: Persistence or transaction unavailable (LISTING_SOURCE_TEMPORARILY_UNAVAILABLE). headers: Cache-Control: schema: type: string enum: - no-store content: application/problem+json: schema: $ref: '#/components/schemas/ApiError' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/ClearSourceLocationDefaultData' 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. " parameters: AsyncProductListingIdempotencyKey: name: Idempotency-Key in: header required: false description: One value only; native duplicates and comma-joined values are invalid (`400 BAD_HEADER_VALUE`) before publication. Use 1–128 visible ASCII bytes excluding comma. Supply the same key with the unchanged ordered batch for transport retries. If absent, a key is generated and returned on evaluated reports; it cannot be recovered if that response is lost. schema: type: string minLength: 1 maxLength: 128 pattern: '^[\x21-\x2B\x2D-\x7E]{1,128}$' example: catalog-sync-2026-09-29 ProductListingIdPath: name: productListingId in: path required: true description: Strict `pl_` UUIDv7 ProductListing TypeID. schema: { type: string } headers: AsyncProductListingIdempotencyKey: description: Effective key, echoed on every evaluated report (including non-2xx); not guaranteed on pre-evaluation ApiError responses. CORS-exposed for browser clients. schema: type: string minLength: 1 maxLength: 128 pattern: '^[\x21-\x2B\x2D-\x7E]{1,128}$' example: catalog-sync-2026-09-29 schemas: ActorData: oneOf: - type: string enum: - SYSTEM description: Backend-initiated actor marker. - type: string description: User ID of the user who performed the change. description: "Audit actor encoded as a plain string. Values are either the literal `SYSTEM` or the User ID 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 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 source: $ref: "#/components/schemas/ProductListingSourceData" sourceListingId: type: string auctionId: type: string description: Indexed Auction TypeID association. Omitted when this listing has no Auction association; search and similar results do not hydrate current Auction metadata. title: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true displayPrice: allOf: - $ref: "#/components/schemas/ProductListingPriceData" 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 geography: description: Advertised inventory geography on geographic search summaries; selected by the backend from eligible source evidence. allOf: [{$ref: '#/components/schemas/ListingGeographyData'}] ProductListingSummaryPriceValuationData: oneOf: - type: object required: - type - fxRateId - capturedAt properties: type: type: string enum: - CURRENT fxRateId: type: string capturedAt: type: string format: date-time - type: object required: - type - fxRateId - observedAt properties: type: type: string enum: - SALE_OBSERVATION fxRateId: type: string 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 - lot - created - updated properties: productListingId: type: string 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 source: $ref: "#/components/schemas/ProductListingSourceData" sourceListingId: type: string productTitle: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true description: Original normalized title. Truncation ellipses remain stable; legacy constructor trailing whitespace is presented trimmed. 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: allOf: - $ref: "#/components/schemas/AuctionSummaryData" nullable: true description: Current parent Auction presentation from the joined PostgreSQL detail read. Null when the listing has no parent Auction. lot: allOf: - $ref: "#/components/schemas/ProductListingLotData" nullable: true description: Listing-owned lot facts. Null when the listing has no lot facts. created: type: string format: date-time updated: type: string format: date-time ProductListingLotData: type: object additionalProperties: false required: [ lotNumber, cataloguePosition, biddingOpens, scheduledCloses, reportedClosedAt, ] description: Listing-owned lot facts. These facts may exist without a parent Auction. properties: lotNumber: type: string nullable: true cataloguePosition: type: integer format: int64 minimum: 1 maximum: 4294967295 nullable: true biddingOpens: type: string format: date-time nullable: true scheduledCloses: type: string format: date-time nullable: true reportedClosedAt: type: string format: date-time nullable: true ProductListingPricingData: type: object properties: price: allOf: - $ref: "#/components/schemas/ProductListingPriceData" 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 capturedAt: type: string format: date-time - type: object required: - type - fxRateId - capturedAt - observedAt properties: type: type: string enum: - SALE_OBSERVATION fxRateId: type: string 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: 0 maximum: 100 description: Number of items returned in the current page. 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 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: fx_0cwktgjtwnf4tb6hf2f8zm0x5k searchAfter: - 2999 - pl_5xdb465tg1enarx2knf04mxbxj 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 ID for this product. Empty when none exist. default: [] items: type: string 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`, 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 (valid synthetic object 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: object-ID fields are replaced with valid synthetic IDs using their documented prefixes, the title becomes a language-specific placeholder, 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 description: "The ID of the saved search filter that matched this product. Present only when `matched` is `true`; omitted otherwise. " example: sf_4hyqf0gyxveazss7nby5hve5x2 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 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 ProductListingPriceData: oneOf: - type: object required: - type - currency - amount additionalProperties: false properties: type: type: string enum: [MONETARY] currency: $ref: "#/components/schemas/CurrencyData" amount: type: integer minimum: 0 - type: object required: - type additionalProperties: false properties: type: type: string enum: [ON_REQUEST] ProductListingHistoryPriceDisplayData: allOf: - $ref: "#/components/schemas/PriceData" - type: object required: - fxRateId - capturedAt properties: fxRateId: type: string description: Immutable FX snapshot selected at or before the entry timestamp. capturedAt: type: string format: date-time description: Capture time of that snapshot, never after the history entry timestamp. description: HalfUp display amount in requested currency minor units (JPY/KRW 0 decimals, others 2). Omitted when the snapshot or a required quote is unavailable, including in a complete legacy 19-currency snapshot. ProductListingHistoryEstimateData: allOf: - $ref: "#/components/schemas/PriceData" - type: object properties: display: $ref: "#/components/schemas/ProductListingHistoryPriceDisplayData" description: Unchanged source estimate with optional historical display projection. ProductListingHistoryMainPriceData: oneOf: - type: object required: - type - currency - amount additionalProperties: false properties: type: type: string enum: [MONETARY] currency: $ref: "#/components/schemas/CurrencyData" amount: type: integer minimum: 0 display: $ref: "#/components/schemas/ProductListingHistoryPriceDisplayData" - type: object required: - type additionalProperties: false properties: type: type: string enum: [ON_REQUEST] description: Unchanged source main price; only monetary values can have historical display projections. ProductListingHistoryPricingData: type: object properties: price: allOf: - $ref: "#/components/schemas/ProductListingHistoryMainPriceData" nullable: true priceEstimateMin: allOf: - $ref: "#/components/schemas/ProductListingHistoryEstimateData" nullable: true priceEstimateMax: allOf: - $ref: "#/components/schemas/ProductListingHistoryEstimateData" nullable: true 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 description: Unique internal ProductListing identifier eventId: type: string 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 - ZAR - SEK - DKK - NOK - KRW - INR - TWD - HUF - RON - MXN - THB 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 - ZAR: South African Rand - SEK: Swedish Krona - DKK: Danish Krone - NOK: Norwegian Krone - KRW: South Korean Won - INR: Indian Rupee - TWD: New Taiwan Dollar - HUF: Hungarian Forint - RON: Romanian Leu - MXN: Mexican Peso - THB: Thai Baht " 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 ProductListingAuctionData: type: object additionalProperties: false description: Partner ProductListing nested write patch. `auctionId` must identify an existing Auction for the same ListingSource. Omit it to preserve membership, send null to clear membership, or send a value to set it. Omit a lot/timing leaf to preserve it, send null to clear a clearable leaf, or send a value to set it. properties: auctionId: type: string nullable: true description: Existing same-ListingSource Aura Auction TypeID. Omit to preserve membership; send null to clear it. example: auc_01jgfjjz4ne2g0000000000000 lotNumber: type: string maxLength: 128 nullable: true description: Omit to preserve the lot label; send null to clear it; send a value to set it. example: "42A" cataloguePosition: type: integer format: int64 minimum: 1 maximum: 4294967295 nullable: true description: Omit to preserve the catalogue position; send null to clear it; send a value to set it. example: 7 timing: allOf: - $ref: "#/components/schemas/ProductListingAuctionTimesData" nullable: true ProductListingAuctionFactsData: type: object additionalProperties: false required: [ lotNumber, cataloguePosition, biddingOpens, scheduledCloses, reportedClosedAt, ] description: Immutable historical listing-owned Auction and lot facts. Their presence has no separate meaning. They never expose a source Auction key or shared Auction metadata. properties: auctionId: type: string description: Resolved Aura Auction TypeID. Omitted when no resolved membership is present. lotNumber: type: string nullable: true description: Optional source-assigned lot label. cataloguePosition: type: integer format: int64 minimum: 1 maximum: 4294967295 nullable: true description: Optional one-based source catalogue position. biddingOpens: type: string format: date-time nullable: true scheduledCloses: type: string format: date-time nullable: true reportedClosedAt: type: string format: date-time nullable: true ProductListingAuctionTimesData: type: object additionalProperties: false description: Omit each leaf to preserve it; send null to clear it; values are RFC3339 instants. properties: biddingOpens: { type: string, format: date-time, nullable: true } scheduledCloses: { type: string, format: date-time, nullable: true } reportedClosedAt: { type: string, format: date-time, nullable: true } 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 sourceListingId: type: string title: description: Normalized discovery title, with legacy constructor trailing whitespace presented trimmed without rewriting the stored event. allOf: - $ref: "#/components/schemas/LocalizedTextData" description: $ref: "#/components/schemas/LocalizedTextData" pricing: $ref: "#/components/schemas/ProductListingHistoryPricingData" availability: allOf: - $ref: "#/components/schemas/ListingAvailabilityData" nullable: true url: type: string format: uri imageCount: type: integer format: int64 minimum: 0 auction: allOf: - $ref: "#/components/schemas/ProductListingAuctionFactsData" nullable: true 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/ProductListingMainPriceHistoryChangeData" - $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. ProductListingMainPriceHistoryChangeData: type: object required: - type - previous - current properties: type: type: string enum: - MAIN_PRICE_CHANGED previous: allOf: - $ref: "#/components/schemas/ProductListingHistoryMainPriceData" nullable: true current: allOf: - $ref: "#/components/schemas/ProductListingHistoryMainPriceData" nullable: true ProductListingPriceHistoryChangeData: type: object required: - type - previous - current properties: type: type: string enum: - MINIMUM_ESTIMATE_CHANGED - MAXIMUM_ESTIMATE_CHANGED previous: allOf: - $ref: "#/components/schemas/ProductListingHistoryEstimateData" nullable: true current: allOf: - $ref: "#/components/schemas/ProductListingHistoryEstimateData" 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: allOf: - $ref: "#/components/schemas/ProductListingAuctionFactsData" nullable: true current: allOf: - $ref: "#/components/schemas/ProductListingAuctionFactsData" nullable: true 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 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 - type properties: field: type: string description: Name of the field that caused the error example: listingSourceId type: 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 uniqueItems: true description: ProductListing IDs to exclude. listingSourceId: type: array items: &id002 type: string uniqueItems: true description: ListingSource IDs to include. excludeListingSourceId: type: array items: &id003 type: string uniqueItems: true description: ListingSource IDs to exclude. nullable: true auctionId: type: array items: type: string uniqueItems: true maxItems: 100 nullable: true description: Exact resolved Auction membership IDs. Omit to preserve; `null` clears. 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 lotBiddingOpens: allOf: &id009 - $ref: "#/components/schemas/RangeQueryDateTime" nullable: true lotScheduledCloses: allOf: &id010 - $ref: "#/components/schemas/RangeQueryDateTime" nullable: true geography: $ref: '#/components/schemas/GeographicSearchData' 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`, `auctionId`, 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. auctionId: type: array items: type: string uniqueItems: true maxItems: 100 description: Exact resolved Auction membership IDs. Values use strict `auc_` TypeIDs and OR together. 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 lotBiddingOpens: allOf: *id009 nullable: true lotScheduledCloses: allOf: *id010 nullable: true geography: nullable: true allOf: [{$ref: '#/components/schemas/GeographicSearchData'}] description: Omitted leaves the private snapshot unchanged; null clears; an object replaces the complete geography predicate. 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 description: Unique identifier of the user who owns this search filter example: usr_5pvhvpxyhve6ts31n9x8c2y513 userSearchFilterId: type: string description: Unique identifier for this search filter example: sf_4hyqf0gyxveazss7nby5hve5x2 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 description: Unique identifier of the user who owns the matched search filter. example: usr_5pvhvpxyhve6ts31n9x8c2y513 userSearchFilterId: type: string description: Unique identifier of the saved search filter that matched the product. example: sf_4hyqf0gyxveazss7nby5hve5x2 userSearchFilterName: type: string description: Saved-search name captured with the match, when available. example: Golden Accessories productListingId: type: string description: Unique internal identifier of the matched product. example: pl_6zydvb8xqqfcntt760grgm41t4 originEventId: type: string description: Product event that produced this match. example: evt_08nrsmex2je2dvsyh01v2kh78a 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 search filters returned 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: - auctions:read - auctions:write - listing-sources:read - parties:read - parties:write - partnership-applications:read - partnership-applications:write - partnerships:read - partnerships:write - admin-overview:read - search-filters:read - notifications:read - notifications:write - product-listings:write - listing-sources: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, AccessToken ID]` 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: 0 maximum: 100 description: Number of items returned in the current page. example: 21 searchAfter: type: array minItems: 2 maxItems: 2 items: type: string description: "`[created RFC3339 timestamp, AccessToken ID]` cursor for the next page, omitted when no more results are available." nullable: true example: - "2026-09-04T12:00:00Z" - at_4ck23tcv0meqqrbyjaka24ra6g 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 description: Explicit target user who owns the access token. example: usr_5pvhvpxyhve6ts31n9x8c2y513 accessTokenId: type: string description: Unique identifier of the access token. example: at_4ck23tcv0meqqrbyjaka24ra6g 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" OwnAccessTokenData: type: object description: "Secret-free metadata for an access token owned by the authenticated user. List, get, and update responses omit the bearer token entirely; the plaintext is only returned by the create operation. " required: - userId - accessTokenId - name - scopes - origin properties: userId: type: string description: User who owns the access token. example: usr_5pvhvpxyhve6ts31n9x8c2y513 accessTokenId: type: string description: Unique identifier of the access token. example: at_4ck23tcv0meqqrbyjaka24ra6g name: type: string maxLength: 128 description: User-defined display name of the access token. example: Partner product sync scopes: type: array items: $ref: "#/components/schemas/AccessTokenScopeData" uniqueItems: true description: Granted access-token scopes. An empty scope set is returned as an empty array. example: - product-listings:write origin: type: string description: Origin recorded by the access-token model. example: User expires: type: string format: date-time description: Optional RFC3339 expiration timestamp; omitted when the token does not expire and returned for expired and current tokens. example: "2026-06-30T00:00:00Z" CreatedAccessTokenData: type: object description: "Minimal access-token creation result. The plaintext bearer token is returned only once. Subsequent list, get, and update operations return secret-free metadata without any token value. " required: - userId - accessTokenId - accessToken properties: userId: type: string description: User who owns the access token. example: usr_5pvhvpxyhve6ts31n9x8c2y513 accessTokenId: type: string description: Unique identifier of the created access token. example: at_4ck23tcv0meqqrbyjaka24ra6g accessToken: type: string description: Plaintext bearer token. Store it securely; it cannot be retrieved again. example: aurahistoria_abcdefghijk_abcdefghijklmnopqrstuvwxyz1234567 PostAccessTokenData: type: object description: "Request body for creating an Aura Historia access token. `scopes` is required, including when the requested scope set is empty. `expires` is optional. " required: - name - scopes properties: name: type: string maxLength: 128 description: User-defined display name of the new access token. example: Partner product sync scopes: type: array items: $ref: "#/components/schemas/AccessTokenScopeData" uniqueItems: true description: Required scopes granted to the access token. Send an empty array for no scopes. example: - product-listings:write expires: type: string format: date-time nullable: true description: Optional RFC3339 expiration timestamp. 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 description: Unique identifier of the access token to update. example: at_4ck23tcv0meqqrbyjaka24ra6g 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" OAuthClientConsentMetadataData: type: object additionalProperties: false description: "Minimal secret-free OAuth client metadata returned to an authenticated user before consent. `client_id`, URLs, redirect URIs, and allowed scopes come from the persisted registration. Redirect URIs are returned as registered and must not be treated as a wildcard allowlist. `scope` contains allowed scopes only, not scopes the user has granted; an empty set is `[]`. This schema intentionally excludes client secrets, secret hashes or short-token material, bearer tokens, authorization codes, verifiers, timestamps, and audit data. " required: - client_id - client_name - tos_uri - policy_uri - client_uri - logo_uri - redirect_uris - scope properties: client_id: type: string description: Canonical OAuthClient ID (`oc_` TypeID). example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 client_name: type: string description: Display name stored for the OAuth client. example: Aura Historia WooCommerce tos_uri: type: string format: uri description: Terms-of-service URL stored for the OAuth client. example: https://integration.example/terms policy_uri: type: string format: uri description: Privacy-policy URL stored for the OAuth client. example: https://integration.example/privacy client_uri: type: string format: uri description: Homepage URL stored for the OAuth client. example: https://integration.example logo_uri: type: string format: uri description: Logo URL stored for the OAuth client. example: https://integration.example/logo.png redirect_uris: type: array uniqueItems: true description: Exact registered redirect URLs, including their registered query strings and other URI components. items: type: string format: uri example: - https://stage.aura-historia.com/api/oauth/client/redirect-broker/woocommerce scope: type: array uniqueItems: true description: Allowed OAuth scopes; this does not mean that the user has granted them. Empty registrations return `[]`. items: $ref: "#/components/schemas/AccessTokenScopeData" example: - product-listings:write 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 description: Canonical OAuthClient ID. example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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, OAuthClient ID]` 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: 0 maximum: 100 description: Number of items returned in the current page. example: 21 searchAfter: type: array minItems: 2 maxItems: 2 items: type: string description: "`[created RFC3339 timestamp, OAuthClient ID]` cursor for the next page, omitted when no more results are available." nullable: true example: - "2026-09-04T12:00:00Z" - oc_7b0gwc1x49e0mvbt3tfexfqgb5 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 description: Canonical OAuthClient ID. example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 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 description: OAuthClient ID that issued the token. Present only when `active` is `true` and the token was issued via the OAuth flow. example: oc_7b0gwc1x49e0mvbt3tfexfqgb5 sub: type: string description: Subject — the Aura Historia user ID who authorized the token. Present only when `active` is `true`. example: usr_4hzreqwxgvef69j056bszwwm17 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 description: Canonical identifier of the product to add to the watchlist. example: pl_5xdb465tg1enarx2knf04mxbxj 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 description: Identifier of the user who owns the entry. example: usr_5pvhvpxyhve6ts31n9x8c2y513 productListingId: type: string description: Canonical identifier of the watched product. example: pl_5xdb465tg1enarx2knf04mxbxj 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 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 ID 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: 0 maximum: 100 description: Number of items returned in the current page. example: 21 searchAfter: type: string description: Party ID cursor for the next page, omitted when no more results are available. example: pty_71m2jxjctefykb6crqka16yf6z 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 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 personalized ProductListing details for persisted search-filter matches. required: - items - size properties: items: type: array items: $ref: "#/components/schemas/PersonalizedProductListingDetailsData" description: Persisted matches in ascending match-creation order. size: type: integer minimum: 0 maximum: 100 description: Number of items returned in the current page. example: 21 searchAfter: type: array minItems: 2 maxItems: 2 items: type: string description: >- Next-page cursor, present only when another page is available. It contains `[RFC3339 persisted match creation timestamp, canonical ProductListing ID]`; serialize the returned array as the `searchAfter` query parameter for the next request. example: - "2026-08-05T12:30:00Z" - pl_6zydvb8xqqfcntt760grgm41t4 total: type: integer minimum: 0 description: Total number of persisted matches, when available. 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 ID 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: 0 maximum: 100 description: Number of items returned in the current page. example: 21 searchAfter: type: string description: User ID cursor for the next page, omitted when more results are unavailable. example: usr_681w9znhe8eawrhebxd1046d44 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 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 - marketingEmailConsent - tier - role properties: userId: type: string 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. marketingEmailConsent: type: boolean readOnly: true description: Whether the current account email has an active grant for email marketing. 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 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 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`. `marketingEmailConsent` is read-only and cannot be changed through this payload. The canonical account email is read-only. " properties: firstName: type: string maxLength: 64 nullable: true description: New first name (truncated to 64 Unicode characters). example: Ada lastName: type: string maxLength: 64 nullable: true description: New last name (truncated to 64 Unicode characters). 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. `marketingEmailConsent` is read-only and cannot be changed through this payload. The canonical account email is read-only. " properties: firstName: type: string maxLength: 64 nullable: true description: New first name (truncated to 64 Unicode characters) example: Jane lastName: type: string maxLength: 64 nullable: true description: New last name (truncated to 64 Unicode 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 requesting double opt-in confirmation for a newsletter subscription. `email` is required; `firstName`, `lastName`, `language`, and `currency` 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 ConfirmNewsletterSubscriptionData: type: object additionalProperties: false required: - token properties: token: type: string minLength: 1 maxLength: 512 description: Opaque confirmation capability returned only in the confirmation email fragment. example: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" 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 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 listingSourceId: type: string 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/ProductListingPriceData" nullable: true description: Previous asking-price assertion. Null when unavailable. newPrice: allOf: - $ref: "#/components/schemas/ProductListingPriceData" nullable: true description: New asking-price assertion. 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 userSearchFilterId: type: string userSearchFilterName: type: string listingSourceId: type: string 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 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 seen: type: boolean NotificationCollectionData: type: object required: - items - size properties: items: type: array items: $ref: "#/components/schemas/NotificationData" size: type: integer minimum: 0 maximum: 100 description: Number of items returned in the current page. searchAfter: type: array nullable: true minItems: 2 maxItems: 2 items: type: string description: Opaque `[created RFC3339 timestamp, Notification ID]` cursor for the next page; omitted when no further page exists. CreateProductListingData: type: object additionalProperties: false description: "Data for creating a single product via the partner batch-create endpoint. " required: - sourceListingId - 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: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true description: Optional localized title. Omit or send `null` when no title is available. description: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true description: Optional localized description. Omit or send `null` when no description is available. price: allOf: - $ref: "#/components/schemas/ProductListingPriceData" nullable: true description: Optional asking-price assertion 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 auction: allOf: - $ref: "#/components/schemas/ProductListingAuctionData" description: Omit for no Auction or lot changes, or send an asserted nested Auction/lot-facts patch. `null` is invalid. `auctionId` must identify an existing Auction for this ListingSource. AsyncProductListingBatchReport: type: object additionalProperties: false description: Admission report, not a listing creation or update result. For every evaluated batch, acceptedCount plus the number of failures equals the original array length. required: [submissionId, acceptedCount, failures] properties: submissionId: type: string description: Opaque correlation identifier only; not a status resource or batch deduplication token. example: plis1_example acceptedCount: type: integer minimum: 0 maximum: 100 description: Number of publisher-confirmed admitted entries; never includes unconfirmed sends. failures: type: array maxItems: 100 description: Exactly one per non-confirmed original entry, sorted by original zero-based index. Empty for fully admitted or empty batches. items: $ref: "#/components/schemas/AsyncProductListingBatchFailure" AsyncProductListingBatchFailure: type: object additionalProperties: false required: [index, error, retryable] properties: index: type: integer minimum: 0 maximum: 99 description: Original zero-based batch position, authoritative even when sourceListingId cannot be echoed. sourceListingId: type: string description: Included only if present, valid, and safe to echo; omitted otherwise. error: type: string pattern: "^[A-Z][A-Z0-9_]*$" description: Bounded safe code, never raw exception or payload text. Includes BAD_BODY_VALUE, PRODUCT_LISTING_INGESTION_PAYLOAD_TOO_LARGE, ENQUEUE_FAILED (definitely unsuccessful send), ENQUEUE_UNCONFIRMED (possibly queued), ENQUEUE_NOT_ATTEMPTED (budget before send), ENQUEUE_BLOCKED (eligible FIFO successor held), PRODUCT_LISTING_INGESTION_INTERNAL_ERROR, and applicable narrower item-validation codes. example: ENQUEUE_UNCONFIRMED retryable: type: boolean description: Whether unchanged-whole-batch retry is appropriate; never permission to resubmit a compacted subset with the same key. Unconfirmed is retryable; validation and size errors are not. Blocked inherits its predecessor's retryability; deterministic configuration errors need repair. 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 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: ls_6rd827eqfefsva9teecmwa3ate 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 additionalProperties: false description: "Data for updating one ProductListing, synchronously or via async submission. Omitted fields remain unchanged. `availability: null` clears the current assertion and `price: null` clears price. Omit `auction` to preserve its context; `auction: null`, `url: null`, and `images: null` are invalid. 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 on the synchronous route and downstream on the async route. example: baroque-violin-001 price: allOf: - $ref: "#/components/schemas/ProductListingPriceData" nullable: true description: Omit to leave unchanged. Send null to clear the current asking-price assertion. 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 auction: allOf: - $ref: "#/components/schemas/ProductListingAuctionData" description: Omit to leave unchanged. Send an object to apply nested lot/timing leaf patches. `auctionId` must identify an existing Auction for this ListingSource; omit it to preserve membership, send null to clear it, or send an ID to set it. `null` for `auction` is invalid. UpsertProductListingData: type: object additionalProperties: false description: "Data for synchronously upserting one ProductListing. Only `sourceListingId` is required. Availability, price, and each price estimate are tri-state: omitted preserves an existing value, `null` clears it, and a concrete value replaces it. The nested Auction/lot-facts patch is omitted to preserve stored facts or supplied as a leaf patch; `null` is invalid. Within an asserted patch, lot/timing leaves omit to preserve, use `null` to clear, or provide a value to set. `auctionId` must identify an existing Auction for this ListingSource; omit it to preserve membership, send `null` to clear it, or send an ID to set it. 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/ProductListingPriceData" nullable: true description: Optional current asking-price assertion. Omit to leave it unchanged; send null to clear it; send a tagged monetary or on-request value to set it. On creation, omitted and null both mean no assertion. 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 auction: allOf: - $ref: "#/components/schemas/ProductListingAuctionData" description: Omit to preserve the current context or send an object to apply nested leaf patches. `auctionId` must identify an existing Auction for this ListingSource; omit it to preserve membership, send null to clear it, or send an ID to set it. `null` for `auction` is invalid. 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 WooCommerce product-description field. Accepted but ignored; it is not mapped to Aura's canonical description and is omitted from persisted source evidence. example: "

Hayde yallah test beschreibung

" short_description: type: string nullable: true description: Optional WooCommerce short-description field. Accepted but ignored; it is not mapped to Aura's canonical description and is omitted from persisted source evidence. 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 current raw-values schema `1` with `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: 0 maximum: 100 description: Number of items returned in the current page. 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" - psh_3q65ndcw7wfxqswjtrv1kfhwd1 AdminPartnershipDetailsData: type: object description: "Administrator Partnership detail. Member and ListingSource reference arrays are ordered by ID and capped at 100 entries; count fields are complete." required: - partnershipId - party - memberUserIds - listingSourceIds - memberCount - listingSourceGrantCount - created - updated properties: partnershipId: type: string description: Stable Partnership identifier. party: $ref: "#/components/schemas/PartnershipPartySummaryData" memberUserIds: type: array maxItems: 100 uniqueItems: true description: Current member User IDs in deterministic ascending ID order. At most 100 references are returned. items: type: string listingSourceIds: type: array maxItems: 100 uniqueItems: true description: Current ListingSource grant IDs in deterministic ascending ID order. At most 100 references are returned. items: type: string 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 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 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 state: $ref: "#/components/schemas/PartnershipApplicationStateData" proposal: $ref: "#/components/schemas/PartnershipApplicationProposalData" AdminPartnershipApplicationData: type: object required: - id - applicantUserId - state - proposal - approvedPartnershipId - approvedListingSourceId properties: id: type: string applicantUserId: type: string state: $ref: "#/components/schemas/PartnershipApplicationStateData" proposal: $ref: "#/components/schemas/PartnershipApplicationProposalData" approvedPartnershipId: type: string nullable: true approvedListingSourceId: type: string 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 applicantUserId: type: string state: $ref: "#/components/schemas/PartnershipApplicationStateData" proposal: $ref: "#/components/schemas/PartnershipApplicationProposalData" approvedPartnershipId: type: string nullable: true approvedListingSourceId: type: string 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: 0 maximum: 100 description: Number of items returned in the current page. searchAfter: type: array minItems: 2 maxItems: 2 items: type: string description: JSON cursor containing the sort timestamp followed by the PartnershipApplication ID. total: type: integer format: int64 minimum: 0 nullable: true ListingSourceReferenceData: type: object required: - listingSourceId - listingSourceSlugId properties: listingSourceId: type: string listingSourceSlugId: type: string example: ada-antiques PublicListingSourceOperatorData: type: object required: - name properties: name: type: string description: Original display name of the ListingSource operator Party. The operator is not a seller resource. PublicListingSourceData: type: object description: Allowlisted public ListingSource card used by both public collection search and exact slug lookup. It omits Party identifiers/contact, ingestion/provider/referral configuration, timestamps, versions, and search metadata. required: - listingSourceId - listingSourceSlugId - name - operator properties: listingSourceId: type: string description: Canonical `ls_` TypeID. listingSourceSlugId: type: string description: Immutable navigation slug. name: type: string operator: $ref: "#/components/schemas/PublicListingSourceOperatorData" url: type: string format: uri description: Optional http/https presentation URL, omitted when absent. image: type: string format: uri description: Optional http/https presentation image URL, omitted when absent. PublicListingSourceSearchCollectionData: type: object required: - items - size properties: items: type: array items: $ref: "#/components/schemas/PublicListingSourceData" size: type: integer minimum: 0 maximum: 50 description: Number of items returned in the current page. searchAfter: type: string description: Opaque continuation, omitted on a terminal page. ListingSourceData: type: object required: - listingSourceId - listingSourceSlugId - name - operator - ingestionMethods - created - updated properties: listingSourceId: type: string 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 listingSourceSlugId: type: string name: type: string ListingSourceSearchCollectionData: type: object description: "Cursor-paginated safe ListingSource summaries for the admin search endpoint. `searchAfter` is a ListingSource ID 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: 0 maximum: 100 description: Number of items returned in the current page. example: 21 searchAfter: type: string description: ListingSource ID cursor for the next page, omitted when no more results are available. example: ls_11j55nbvjfe7ds34s7gya899a9 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 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 PutWoocommerceListingSourceIngestionConfigurationData: type: object additionalProperties: false required: - webhookSecret properties: webhookSecret: type: string writeOnly: true description: Required nonblank provider secret. Preserved exactly for HMAC verification and never returned. currency: allOf: - $ref: "#/components/schemas/CurrencyData" nullable: true language: allOf: - $ref: "#/components/schemas/LanguageData" nullable: true PutShopifyListingSourceIngestionConfigurationData: type: object additionalProperties: false required: - domain properties: domain: type: string description: Valid Shopify store domain. Must be unique across ListingSources. currency: allOf: - $ref: "#/components/schemas/CurrencyData" nullable: true language: allOf: - $ref: "#/components/schemas/LanguageData" nullable: true CreateListingIngestionConfigurationData: oneOf: - type: object additionalProperties: false required: - type - ingestionMethod properties: type: type: string enum: - UNCONFIGURED ingestionMethod: $ref: "#/components/schemas/ListingIngestionMethodData" - type: object additionalProperties: false 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 additionalProperties: false required: - type - domain properties: type: type: string enum: - SHOPIFY domain: type: string currency: type: string language: type: string - type: object additionalProperties: false required: - type - webhookSecret properties: type: type: string enum: - WOOCOMMERCE webhookSecret: type: string writeOnly: true description: Required nonblank WooCommerce secret. Never returned or logged. currency: type: string language: type: string - type: object additionalProperties: false 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 partySlugId: type: string name: type: string ListingSourceOperatorInputData: oneOf: - type: object required: - type - partyId properties: type: type: string enum: - EXISTING partyId: type: string - 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 additionalProperties: false 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/CreateListingIngestionConfigurationData" url: type: string format: uri image: type: string format: uri referralConfiguration: $ref: "#/components/schemas/ReferralConfigurationData" UpdateListingIngestionConfigurationData: oneOf: - type: object additionalProperties: false required: - type properties: type: type: string enum: [WEB_CRAWL] fallbackCurrency: type: string - type: object additionalProperties: false required: - type - domain properties: type: type: string enum: [SHOPIFY] domain: type: string currency: type: string language: type: string - type: object additionalProperties: false required: - type properties: type: type: string enum: [WOOCOMMERCE] webhookSecret: type: string writeOnly: true description: Optional only when WooCommerce is already configured. Omission preserves the existing secret; a string rotates it. Null is invalid, and when adding WooCommerce a secret is required. currency: type: string language: type: string - type: object additionalProperties: false required: - type properties: type: type: string enum: [PARTNER_API] UpdateListingSourceData: type: object additionalProperties: false description: Omitted fields are unchanged. `null` clears only `url`, `image`, and `referralConfiguration`. Supplying ingestionConfiguration replaces the whole configuration list; a WooCommerce secret may be omitted only when preserving an existing WooCommerce configuration. 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/UpdateListingIngestionConfigurationData" 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 - 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 CreateAuctionData: type: object additionalProperties: false required: [listingSourceId, sourceAuctionId] properties: listingSourceId: type: string description: Strict ListingSource TypeID. sourceAuctionId: type: string maxLength: 512 description: 1–512 UTF-8 bytes after outer Unicode-whitespace trimming; case and internal whitespace are preserved. name: $ref: "#/components/schemas/LocalizedTextData" catalogueUrl: type: string format: uri format: $ref: "#/components/schemas/AuctionFormatData" schedule: $ref: "#/components/schemas/AuctionScheduleInputData" reportedStatus: $ref: "#/components/schemas/AuctionReportedStatusData" reportedLotCount: type: integer format: int64 minimum: 0 maximum: 4294967295 UpdateAuctionData: type: object additionalProperties: false required: [expectedVersion] description: Omitted members are unchanged. Nullable members explicitly clear their value. properties: expectedVersion: type: integer format: int64 minimum: 1 name: allOf: [{ $ref: "#/components/schemas/LocalizedTextData" }] nullable: true catalogueUrl: type: string format: uri nullable: true format: allOf: [{ $ref: "#/components/schemas/AuctionFormatData" }] nullable: true schedule: $ref: "#/components/schemas/AuctionSchedulePatchData" reportedStatus: allOf: [{ $ref: "#/components/schemas/AuctionReportedStatusData" }] nullable: true reportedLotCount: type: integer format: int64 minimum: 0 maximum: 4294967295 nullable: true PublicAuctionDirectoryData: type: object additionalProperties: false required: [items, pageSize] properties: items: type: array items: $ref: "#/components/schemas/PublicAuctionDirectoryItemData" pageSize: type: integer format: int64 minimum: 1 maximum: 100 searchAfter: allOf: - $ref: "#/components/schemas/PublicAuctionDirectoryCursorData" nullable: true description: Omitted on the terminal page. Send the complete JSON value as the `searchAfter` query parameter. PublicAuctionDirectoryItemData: type: object additionalProperties: false required: [ auctionId, listingSource, name, format, schedule, reportedStatus, created, ] properties: auctionId: type: string description: Strict `auc_` UUIDv7 TypeID. listingSource: $ref: "#/components/schemas/PublicAuctionSourceData" name: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true format: allOf: - $ref: "#/components/schemas/AuctionFormatData" nullable: true schedule: $ref: "#/components/schemas/AuctionScheduleData" reportedStatus: allOf: - $ref: "#/components/schemas/AuctionReportedStatusData" nullable: true created: type: string format: date-time PublicAuctionData: type: object additionalProperties: false required: [ auctionId, listingSource, name, catalogueUrl, viewUrl, format, schedule, reportedStatus, reportedLotCount, visibleListingCount, ] properties: auctionId: type: string description: Strict `auc_` UUIDv7 TypeID. listingSource: $ref: "#/components/schemas/PublicAuctionSourceData" name: allOf: - $ref: "#/components/schemas/LocalizedTextData" nullable: true catalogueUrl: type: string format: uri nullable: true viewUrl: type: string format: uri nullable: true format: allOf: - $ref: "#/components/schemas/AuctionFormatData" nullable: true schedule: $ref: "#/components/schemas/AuctionScheduleData" reportedStatus: allOf: - $ref: "#/components/schemas/AuctionReportedStatusData" nullable: true reportedLotCount: type: integer format: int64 minimum: 0 nullable: true visibleListingCount: type: integer format: int64 minimum: 0 PublicAuctionSourceData: type: object additionalProperties: false required: [listingSourceId, name, slugId] properties: listingSourceId: type: string name: type: string slugId: type: string AdminAuctionSearchCollectionData: type: object additionalProperties: false required: [items, size] properties: items: type: array items: { $ref: "#/components/schemas/AuctionAdminData" } size: type: integer description: Number of items returned on this page. searchAfter: type: string description: Opaque cursor, omitted on the terminal page. PublicAuctionDirectoryCursorData: type: object additionalProperties: false required: [created, auctionId, scope] properties: created: type: string format: date-time auctionId: type: string scheduled: type: string format: date-time nullable: true description: Present for scheduled sorting; null if the selected role instant is missing. Omitted for created sorting. scope: $ref: "#/components/schemas/PublicAuctionDirectoryCursorScopeData" PublicAuctionDirectoryCursorScopeData: type: object additionalProperties: false required: [listingSourceId, format, reportedStatus, timeRole, from, to] properties: listingSourceId: type: string nullable: true format: allOf: - $ref: "#/components/schemas/AuctionFormatData" nullable: true reportedStatus: allOf: - $ref: "#/components/schemas/AuctionReportedStatusData" nullable: true timeRole: type: string enum: [BIDDING_OPENS, LIVE_STARTS, LOTS_BEGIN_CLOSING, SCHEDULED_END] nullable: true sort: type: string enum: [scheduled] description: Omitted for the default created ordering. order: type: string enum: [asc, desc] description: Omitted for the default created/descending ordering. from: type: string format: date-time nullable: true to: type: string format: date-time nullable: true PublicAuctionCatalogueData: type: object additionalProperties: false required: [items, pageSize] properties: items: type: array items: $ref: "#/components/schemas/PersonalizedProductListingDetailsData" pageSize: type: integer format: int64 minimum: 1 maximum: 100 searchAfter: allOf: - $ref: "#/components/schemas/PublicAuctionCatalogueCursorData" nullable: true description: Omitted on the terminal page. Send the complete JSON value as the `searchAfter` query parameter. PublicAuctionCatalogueCursorData: type: object additionalProperties: false required: [auctionId, cataloguePosition, productListingId] properties: auctionId: type: string cataloguePosition: type: integer format: int64 minimum: 1 nullable: true productListingId: type: string AuctionAdminData: type: object required: [ auctionId, listingSourceId, sourceAuctionId, name, catalogueUrl, format, schedule, reportedStatus, reportedLotCount, expectedVersion, created, updated, ] properties: auctionId: { type: string, description: Strict `auc_` UUIDv7 TypeID. } listingSourceId: { type: string } sourceAuctionId: { type: string } name: allOf: [{ $ref: "#/components/schemas/LocalizedTextData" }] nullable: true catalogueUrl: { type: string, format: uri, nullable: true } format: allOf: [{ $ref: "#/components/schemas/AuctionFormatData" }] nullable: true schedule: { $ref: "#/components/schemas/AuctionScheduleData" } reportedStatus: allOf: [{ $ref: "#/components/schemas/AuctionReportedStatusData" }] nullable: true reportedLotCount: { type: integer, format: int64, nullable: true } expectedVersion: { type: integer, format: int64, minimum: 1 } created: { type: string, format: date-time } updated: { type: string, format: date-time } AuctionFormatData: type: string enum: [LIVE, TIMED] AuctionReportedStatusData: type: string enum: [SCHEDULED, IN_PROGRESS, ENDED, POSTPONED, CANCELLED] AuctionSummaryData: type: object additionalProperties: false required: [auctionId, schedule] description: Safe current parent Auction presentation included by PostgreSQL full-detail ProductListing reads. properties: auctionId: type: string description: Strict `auc_` UUIDv7 TypeID. name: allOf: [{ $ref: "#/components/schemas/LocalizedTextData" }] nullable: true format: allOf: [{ $ref: "#/components/schemas/AuctionFormatData" }] nullable: true reportedStatus: allOf: [{ $ref: "#/components/schemas/AuctionReportedStatusData" }] nullable: true schedule: $ref: "#/components/schemas/AuctionScheduleData" AuctionScheduleData: type: object additionalProperties: false required: [biddingOpens, liveStarts, lotsBeginClosing, scheduledEnd] properties: biddingOpens: { type: string, format: date-time, nullable: true } liveStarts: { type: string, format: date-time, nullable: true } lotsBeginClosing: { type: string, format: date-time, nullable: true } scheduledEnd: { type: string, format: date-time, nullable: true } AuctionScheduleInputData: type: object additionalProperties: false description: Sparse Auction create schedule. Omitted roles remain absent. properties: biddingOpens: { type: string, format: date-time, nullable: true } liveStarts: { type: string, format: date-time, nullable: true } lotsBeginClosing: { type: string, format: date-time, nullable: true } scheduledEnd: { type: string, format: date-time, nullable: true } AuctionSchedulePatchData: type: object additionalProperties: false description: Omitted roles are unchanged; `null` clears an asserted RFC3339 instant. properties: biddingOpens: { type: string, format: date-time, nullable: true } liveStarts: { type: string, format: date-time, nullable: true } lotsBeginClosing: { type: string, format: date-time, nullable: true } scheduledEnd: { type: string, format: date-time, nullable: true } ProductListingSourceData: type: object description: ListingSource presentation hydrated for a ProductListing response. required: - listingSourceId - name - slugId properties: listingSourceId: type: string name: type: string slugId: type: string UserAddressId: type: string pattern: ^uaddr_[0-7][0-9a-hjkmnp-tv-z]{25}$ description: Strict server-generated UUIDv7 UserAddress TypeID, returned on creation. No bare UUID or owner input. UserAddressLabel: type: string minLength: 1 maxLength: 128 description: Nonblank private label, at most 128 UTF-8 bytes, without control characters. UserAddressGeography: allOf: - $ref: '#/components/schemas/PartyLocationGeography' - type: object required: - addressText properties: addressText: type: string nullable: false minLength: 1 maxLength: 4096 description: Verbatim free-text geographic assertions. addressText is required and non-null; no parsed street or PlaceId. Provider-derived values remain separate. CreateUserAddress: type: object additionalProperties: false required: - label - geography properties: label: $ref: '#/components/schemas/UserAddressLabel' geography: $ref: '#/components/schemas/UserAddressGeography' UpdateUserAddress: type: object additionalProperties: false required: - expectedVersion - label - geography properties: expectedVersion: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 label: $ref: '#/components/schemas/UserAddressLabel' geography: $ref: '#/components/schemas/UserAddressGeography' UserAddressExpectedVersion: type: object additionalProperties: false required: - expectedVersion properties: expectedVersion: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 UserAddress: type: object additionalProperties: false required: - addressId - label - geography - inputRevision - version - preferred - resolution properties: addressId: $ref: '#/components/schemas/UserAddressId' label: $ref: '#/components/schemas/UserAddressLabel' geography: $ref: '#/components/schemas/UserAddressGeography' inputRevision: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 version: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 preferred: type: boolean resolution: $ref: '#/components/schemas/UserAddressResolution' UserAddressPage: type: object additionalProperties: false required: - items - next properties: items: type: array items: $ref: '#/components/schemas/UserAddress' maxItems: 100 next: allOf: - $ref: '#/components/schemas/UserAddressId' nullable: true BrowsingDestination: oneOf: - type: object additionalProperties: false required: - kind properties: kind: type: string enum: - NONE - type: object additionalProperties: false required: - kind - addressId properties: kind: type: string enum: - ADDRESS addressId: $ref: '#/components/schemas/UserAddressId' - type: object additionalProperties: false required: - kind - country properties: kind: type: string enum: - COUNTRY_POSTAL country: type: string pattern: ^[A-Z]{2}$ description: Current ISO alpha-2 assertion; never inferred from language/currency/IP. postalCode: type: string nullable: true minLength: 1 maxLength: 64 description: Optional verbatim postal assertion, at most 64 UTF-8 bytes. description: Exclusive browsing selector. An ADDRESS reference must belong to the current user. No shipping, billing or residence assertion. BrowsingDestinationView: type: object additionalProperties: false required: - version - destination properties: version: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 destination: $ref: '#/components/schemas/BrowsingDestination' SetBrowsingDestination: type: object additionalProperties: false required: - expectedVersion - destination properties: expectedVersion: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 destination: $ref: '#/components/schemas/BrowsingDestination' UserAddressDerivedGeography: type: object additionalProperties: false required: - geography - position properties: geography: allOf: - $ref: '#/components/schemas/PartyLocationGeography' nullable: true position: allOf: - $ref: '#/components/schemas/PartyLocationPosition' nullable: true UserAddressResolution: type: object additionalProperties: false description: Current backend geocoding state, returned with address CRUD/list reads. A unique compatible premises result can supply a point automatically. Coarse results can supply administrative geography only. Expired or revoked evidence is UNAVAILABLE and suppressed. This is browsing enrichment, not address or delivery verification. required: - status - derived - attribution properties: status: type: string enum: - PENDING - ACCEPTED - AMBIGUOUS - NO_MATCH - FAILED - BLOCKED - UNAVAILABLE derived: allOf: - $ref: '#/components/schemas/UserAddressDerivedGeography' nullable: true attribution: allOf: - $ref: '#/components/schemas/UserAddressAttribution' nullable: true UserAddressAttribution: type: object additionalProperties: false required: - provider - attributionUrl description: Display the attribution when showing accepted provider-derived geography. properties: provider: type: string attributionUrl: type: string format: uri SourceLocationDefaultEvidence: type: object additionalProperties: false required: - reference - scope properties: reference: type: string minLength: 1 maxLength: 2048 description: Nonblank opaque source-scoped observation, at most 2048 UTF-8 bytes, without control or line-separator characters. Never fetched. scope: type: string enum: - ASSOCIATED_BUSINESS - ADVERTISED_INVENTORY SourceLocationDefaultAssignment: type: object additionalProperties: false required: - partyLocationId properties: partyLocationId: $ref: '#/components/schemas/PartyLocationId' businessAssociation: allOf: - $ref: '#/components/schemas/SourceLocationDefaultEvidence' nullable: true description: Explicit associated-business evidence; scope must be ASSOCIATED_BUSINESS. advertisedInventory: allOf: - $ref: '#/components/schemas/SourceLocationDefaultEvidence' nullable: true description: Explicit inventory evidence; scope must be ADVERTISED_INVENTORY. Business, legal or correspondence evidence alone never establishes this meaning. SourceLocationDefaultData: type: object additionalProperties: false required: - listingSourceId - revision - assignment - eligibility properties: listingSourceId: type: string pattern: ^ls_[0-7][0-9a-hjkmnp-tv-z]{25}$ revision: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 description: Expected source-default revision; an untouched source has revision 1. Stale writes conflict, including no-ops. assignment: allOf: - $ref: '#/components/schemas/SourceLocationDefaultAssignment' nullable: true eligibility: type: string enum: - ACTIVE - RETIRED nullable: true description: Current eligibility of the retained reference, null if no assignment. Retirement preserves the reference but disables inherited geography. AssignSourceLocationDefaultData: type: object additionalProperties: false required: - expectedRevision - partyLocationId properties: expectedRevision: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 description: Expected source-default revision; an untouched source has revision 1. Stale writes conflict, including no-ops. partyLocationId: $ref: '#/components/schemas/PartyLocationId' businessAssociation: allOf: - $ref: '#/components/schemas/SourceLocationDefaultEvidence' nullable: true description: Explicit associated-business evidence; scope must be ASSOCIATED_BUSINESS. advertisedInventory: allOf: - $ref: '#/components/schemas/SourceLocationDefaultEvidence' nullable: true description: Explicit inventory evidence; scope must be ADVERTISED_INVENTORY. Business, legal or correspondence evidence alone never establishes this meaning. description: Replaces the one reference and both applicability meanings atomically. Omitted/null evidence means absent, including on replacement. No automatic location selection. ChangeSourceLocationDefaultApplicabilityData: type: object additionalProperties: false required: - expectedRevision properties: expectedRevision: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 description: Expected source-default revision; an untouched source has revision 1. Stale writes conflict, including no-ops. businessAssociation: allOf: - $ref: '#/components/schemas/SourceLocationDefaultEvidence' nullable: true description: Omitted retains the assertion; null removes it; set requires ASSOCIATED_BUSINESS scope. advertisedInventory: allOf: - $ref: '#/components/schemas/SourceLocationDefaultEvidence' nullable: true description: Omitted retains the assertion; null removes it; set requires ADVERTISED_INVENTORY scope. description: Changes applicability on the existing, active reference without changing its location ID. ClearSourceLocationDefaultData: type: object additionalProperties: false required: - expectedRevision properties: expectedRevision: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 description: Expected source-default revision; an untouched source has revision 1. Stale writes conflict, including no-ops. PartyLocationId: type: string pattern: ^ploc_[0-7][0-9a-hjkmnp-tv-z]{25}$ description: Strict UUIDv7 PartyLocation TypeID; bare UUIDs and foreign prefixes are rejected. PartyLocationGeography: type: object additionalProperties: false required: - referenceRelease properties: referenceRelease: type: string enum: - iso-codes-4.20.1 addressText: type: string maxLength: 4096 description: Verbatim free-text assertion; at most 4096 UTF-8 bytes. No geocoding or postal inference. nullable: true country: type: string pattern: ^[A-Z]{2}$ description: Exact current ISO alpha-2 country in the named immutable release. nullable: true subdivision: type: string description: Complete ISO 3166-2 code in the named release; must agree with country when supplied. nullable: true postalCode: type: string maxLength: 64 description: Single-line verbatim postal assertion, at most 64 UTF-8 bytes. nullable: true description: Partial caller assertions. At least one geographic field must be present and nonblank. Coordinates are separate. No synchronous geocoding. PartyLocationPosition: type: object additionalProperties: false required: - latitudeDegrees - longitudeDegrees - precision properties: latitudeDegrees: type: number minimum: -90 maximum: 90 longitudeDegrees: type: number minimum: -180 maximum: 180 precision: type: string enum: - PREMISES - STREET - LOCALITY - REPRESENTATIVE accuracyMetres: type: number minimum: 0 nullable: true description: Finite caller-asserted WGS84 coordinates with explicit precision. Not geocoder-derived evidence; (0,0) is valid. PartyLocationEvidence: type: object additionalProperties: false required: - reference - scope properties: reference: type: string minLength: 1 maxLength: 2048 description: Opaque nonblank single-line evidence/source reference; never fetched. At most 2048 UTF-8 bytes. observedAt: type: string format: date-time nullable: true scope: type: string enum: - SITE_DESCRIPTION - REGISTERED_ADDRESS - CORRESPONDENCE_ADDRESS description: Declared assertion scope; never caller identity, authorization or trust. ExpectedPartyLocation: type: object additionalProperties: false required: - id - revision properties: id: $ref: '#/components/schemas/PartyLocationId' revision: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 CreatePartyLocationData: type: object additionalProperties: false required: - label properties: label: type: string minLength: 1 maxLength: 255 description: Trimmed nonblank single-line label, at most 255 UTF-8 bytes. Never establishes site identity. roles: type: array maxItems: 4 uniqueItems: true items: type: string enum: - BUSINESS_PREMISES - WAREHOUSE - REGISTERED_ADDRESS - CORRESPONDENCE description: Business use of a site. No assertion about listing stock, dispatch or pickup. default: [] geography: allOf: - $ref: '#/components/schemas/PartyLocationGeography' nullable: true position: allOf: - $ref: '#/components/schemas/PartyLocationPosition' nullable: true evidence: allOf: - $ref: '#/components/schemas/PartyLocationEvidence' nullable: true relocates: allOf: - $ref: '#/components/schemas/ExpectedPartyLocation' nullable: true description: Create an independently identified site with exact public geography. Optional relocates creates a new identity and retires the referenced active site atomically; its Party and revision must match. CorrectPartyLocationData: type: object additionalProperties: false required: - expectedRevision - correction properties: label: type: string minLength: 1 maxLength: 255 description: Trimmed nonblank single-line label, at most 255 UTF-8 bytes. Never establishes site identity. roles: type: array maxItems: 4 uniqueItems: true items: type: string enum: - BUSINESS_PREMISES - WAREHOUSE - REGISTERED_ADDRESS - CORRESPONDENCE description: Business use of a site. No assertion about listing stock, dispatch or pickup. geography: allOf: - $ref: '#/components/schemas/PartyLocationGeography' nullable: true position: allOf: - $ref: '#/components/schemas/PartyLocationPosition' nullable: true evidence: allOf: - $ref: '#/components/schemas/PartyLocationEvidence' nullable: true expectedRevision: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 correction: type: string enum: - SAME_SITE description: Same physical site correction only. Omitted members remain unchanged. Geography/position/evidence null clears; non-null geographic objects replace the complete partial assertion. Label and roles reject null. Roles [] clears the set. No-op at the current revision emits no change signal. Relocation uses create with relocates. SetPartyLocationLifecycleData: type: object additionalProperties: false required: - expectedRevision - lifecycle properties: expectedRevision: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 lifecycle: type: string enum: - ACTIVE - RETIRED description: Explicit retirement (RETIRED) or restore (ACTIVE), with CAS. Retains references/history and never auto-selects replacements. PartyLocationData: type: object additionalProperties: false required: - id - partyId - label - roles - lifecycle - revision - inputRevision properties: id: $ref: '#/components/schemas/PartyLocationId' partyId: type: string pattern: ^pty_ label: type: string minLength: 1 maxLength: 255 description: Trimmed nonblank single-line label, at most 255 UTF-8 bytes. Never establishes site identity. roles: type: array maxItems: 4 uniqueItems: true items: type: string enum: - BUSINESS_PREMISES - WAREHOUSE - REGISTERED_ADDRESS - CORRESPONDENCE description: Business use of a site. No assertion about listing stock, dispatch or pickup. geography: $ref: '#/components/schemas/PartyLocationGeography' position: $ref: '#/components/schemas/PartyLocationPosition' lifecycle: type: string enum: - ACTIVE - RETIRED revision: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 inputRevision: type: integer format: int64 minimum: 1 maximum: 9223372036854775807 evidence: $ref: '#/components/schemas/PartyLocationEvidence' PartyLocationsPage: type: object additionalProperties: false required: - items - next properties: items: type: array items: $ref: '#/components/schemas/PartyLocationData' next: allOf: - $ref: '#/components/schemas/PartyLocationId' nullable: true description: Last returned ID when another row exists; supply as after. Null when exhausted. PartyLocationManagementGrantData: type: object additionalProperties: false required: - granted properties: granted: type: boolean description: true grants explicit management/private reads; false removes only the explicit grant. Active partnership membership independently authorizes access. Same-state commands are no-ops. GeographicOriginData: type: object additionalProperties: false required: [latitude, longitude] description: Explicit finite numeric WGS 84 coordinates in degrees; no text, address or preference resolution. properties: latitude: {type: number, minimum: -90, maximum: 90} longitude: {type: number, minimum: -180, maximum: 180} GeographicRadiusData: type: object additionalProperties: false required: [amount, unit] description: >- Finite positive radius in the supplied unit, equivalent to at most 20000 kilometres. The supplied amount and unit are preserved in private saved-filter snapshots. properties: amount: {type: number, minimum: 0, exclusiveMinimum: true, example: 25} unit: type: string enum: [KILOMETERS, MILES, METERS, YARDS, FEET, INCHES, CENTIMETERS, MILLIMETERS, NAUTICAL_MILES] example: KILOMETERS example: {amount: 25, unit: KILOMETERS} GeographicSearchData: type: object additionalProperties: false description: >- At least one country, subdivision, radius or distance order is required. Sets are OR; distinct geographic predicates are AND. Subdivisions must belong to a supplied country when countries is nonempty. Absence excludes a result. Radius and ordering require an explicit numeric origin; an origin or approximate mode without proximity is invalid. Strict proximity admits PREMISES only; approximate also admits STREET, LOCALITY and REPRESENTATIVE. Distance ordering is incompatible with the existing sort. Saved filters retain a private immutable snapshot until replaced. properties: countries: type: array maxItems: 100 uniqueItems: true items: {type: string, pattern: '^[A-Z]{2}$'} example: [DE] subdivisions: type: array maxItems: 100 uniqueItems: true items: {type: string} example: [DE-BY] origin: {$ref: '#/components/schemas/GeographicOriginData'} radius: {$ref: '#/components/schemas/GeographicRadiusData'} mode: {type: string, enum: [STRICT, APPROXIMATE], default: STRICT} distanceOrder: {type: string, enum: [ASC, DESC]} ListingGeographyData: type: object required: [locationId, basis, referenceRelease] description: >- Public advertised-inventory geography selected by the backend. Distance is to recorded evidence, not verified object position or journey distance. Display is rounded to 0.1 km for premises, 1 km for street and 10 km for locality/representative evidence. properties: locationId: {type: string} basis: {type: string, enum: [SOURCE_DEFAULT_DERIVED]} referenceRelease: {type: string} country: {type: string} subdivision: {type: string} point: {$ref: '#/components/schemas/GeographicOriginData'} precision: {type: string, enum: [PREMISES, STREET, LOCALITY, REPRESENTATIVE]} distanceKm: {type: number, minimum: 0} PartyLocationDiscoveryCursorData: type: object additionalProperties: false required: [locationId] description: Repeat with the same query. distanceKm is required exactly when ordering by distance; it is an unrounded continuation value. properties: locationId: {type: string} distanceKm: {type: number, minimum: 0} PublicPartyLocationData: type: object required: [partyId, partyName, locationId, label, basis] properties: partyId: {type: string} partyName: {type: string} locationId: {type: string} label: {type: string} basis: {type: string, enum: [CALLER_ASSERTED, ACCEPTED_DERIVED, MIXED_ASSERTED_AND_DERIVED]} country: {type: string} subdivision: {type: string} point: {$ref: '#/components/schemas/GeographicOriginData'} precision: {type: string, enum: [PREMISES, STREET, LOCALITY, REPRESENTATIVE]} distanceKm: {type: number, minimum: 0, description: Rounded display distance following evidence precision.} x-tagGroups: - name: Operations tags: - Operations - name: Discovery tags: - ProductListings - ListingSources - Auctions - name: User Experience tags: - Search Filters - Watchlist - Notifications - User Account - Billing - Newsletter - name: Partner & Platform Integrations tags: - Partnerships - PartnershipApplications - OAuth tags: - name: Operations description: Public process liveness and service readiness probes. - name: Auctions description: Public Auction discovery, catalogue reads, and administrator Auction management. - 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, persisted 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.