Every API here is available over the APIs.io API and to AI agents over MCP.
openapi: 3.2.0
info:
title: Aura Historia API Reference Newsletter API
description: '## Overview
The Aura Historia API powers the Aura Historia antiques platform and its partner-facing integrations. It exposes product discovery, listing source discovery, personalization, partner onboarding, and integration
workflows on top of Aura Historia''s serverless AWS platform. This reference is designed for two audiences:
- **Internal developers** building Aura Historia product-listings, operational tooling, and backoffice workflows
- **External partners and integrators** synchronizing inventory, consuming platform data, or
integrating with delegated access
## Core API domains
- **ProductListings and listing-sources** — search, retrieve, and explore product-listings, related product-listings, product
history, and listing source data
- **Personalization** — manage watchlists, saved search filters, and user notifications
- **Partner workflows** — submit partnership applications, maintain partnerships, ingest catalog data
in batches, and receive WooCommerce webhooks
- **Identity and access** — manage user accounts, Aura Historia access tokens, and OAuth 2.0
clients and token flows
- **Billing** — create Stripe checkout, billing-portal, and subscription-management sessions
## Authentication model
The API uses more than one authentication scheme depending on the route:
- **`BearerAuth`** — Cognito **access** JWT bearer tokens for authenticated user, admin, and
partner-user workflows. Cognito ID tokens are not accepted as API credentials. - **`AccessTokenAuth`** — Aura Historia opaque bearer access tokens created via
`/v1/me/access-tokens` or OAuth 2.0, primarily intended for partner automations and server-to-server
ingestion scenarios. When a route lists multiple security schemes, any one of the listed schemes may be accepted. Some read endpoints can also be called without authentication, while a valid user token may enrich
the response with personalized state such as watchlist, notification, or search-filter metadata. Aura Historia access
tokens receive only the sections their read scopes permit; see `ProductListingUserStateData`. ## MCP interface
The separate `https://mcp.aura-historia.com/v1` Streamable HTTP endpoint accepts opaque Aura access tokens only. Public calls may omit credentials; invalid supplied credentials receive HTTP 401, protected operations require their declared scopes, and insufficient grants receive HTTP 403 with the complete required scope set. Credential-free protected-resource metadata is served on the MCP host at `/.well-known/oauth-protected-resource` and `/.well-known/oauth-protected-resource/v1`. See the MCP contract for protocol and discovery details. ## Data and response conventions
- **PATCH:** an omitted request member remains unchanged. A documented nullable PATCH member
accepts `null` to clear its value; `null` for any other member returns `400 BAD_BODY_VALUE`. Use `[]`, not `null`, to replace a non-null collection with an empty collection. Empty HTTP
bodies are invalid, `{}` is a valid object-PATCH no-op, and the partner-product PATCH `[]`
remains a valid empty batch. Responses may omit absent optional values. - Product detail, watchlist, and saved-search match reads accept optional **`language`** and
**`currency`** parameters. Currency defaults to `EUR`. Detail pricing contains the seller''s source
amounts, converted display amounts, and FX valuation metadata. Active product-listings use the latest persisted
snapshot; sold product-listings use their immutable sale snapshot. Product history preserves immutable source values and accepts optional `currency` for historical FX display projections. History text is not localized. - Request-wide REST errors are returned as **`application/problem+json`** using a consistent RFC 9457-style structure
with `status`, `title`, `error`, optional `source`, and optional `detail`.'
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
tags:
- name: Newsletter
description: Newsletter subscription upsert workflows for website or product-surface sign-up flows.
paths:
/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
/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
/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
components:
schemas:
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
- 'null'
maxLength: 64
description: Optional first name to sync with the subscription
example: Ada
lastName:
type:
- string
- 'null'
maxLength: 64
description: Optional last name to sync with the subscription
example: Lovelace
language:
allOf:
- $ref: '#/components/schemas/LanguageData'
description: Optional preferred language to sync with the subscription
currency:
allOf:
- $ref: '#/components/schemas/CurrencyData'
description: Optional preferred currency to sync with the subscription
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
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'''
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
- ARS
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
- ARS: Argentine Peso (2 minor-unit decimal places)
'
default: EUR
example: EUR
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
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
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: 'Cognito access JWT bearer token. Signature, issuer, client, token use and expiry
are validated locally. Signed integer `auth_time` and `iat` claims are required;
sessions authenticated at or before the persisted local revocation cutoff are rejected.
This check shares the existing identity lookup and makes no live Cognito status request.
Revocation requested only in Cognito is not observed immediately; already-issued JWTs
can remain accepted until expiry. Use the administrator session-revocation endpoint
for immediate local enforcement. Database authentication failures return
`503 AUTH_TEMPORARILY_UNAVAILABLE`.
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 `/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.
'
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