API reference

Two calls and an MCP server, all with an API key. Send JSON, get a signed JSON answer. Every example uses test.authorizedretailers.ai, a demo brand the registry runs, so you can try it as written.

Overview

Base URLhttps://authorizedretailers.ai. Paths carry no version: a request without one gets the current spec. The older /v0/ form of every endpoint still answers, the same way.
AuthenticationAn API key, sent as Authorization: Bearer <key>, never in a URL. Get one. A missing or invalid key is a 401. Also send a User-Agent that names your agent (see limits).
FormatJSON in, JSON out. Requests up to 8 KB.
AnswersSigned by the registry (Ed25519), fresh for 15 minutes. See trusting an answer.
EndpointsPOST /verify: is this seller authorized for this product? POST /resolve: which brand is this product? Plus the MCP server at /mcp.

API keys

The verify and resolve calls and the MCP server need an API key, so we know who is using the registry and can reach you about it. Get an API key, and your console in the app opens with your keys, their usage and your settings. Come back with Sign in, then Agent developer. We ask for:

  • your work email. If you don't have one, say so on the form and tick the box to be contacted, and we'll get in touch;
  • your name and organization, as the agent's operator;
  • the agent you're building and what it's for.
  • You sign in with an emailed link and an authenticator app, as brand accounts do. Lost your phone? Email hello@authorizedretailers.ai and we'll reset it.
  • Send the key as Authorization: Bearer <key> on every call, and never in a URL or the request body.
  • A key is shown once, when you create it. Keep it in your agent's secrets, not in code or a repository.
  • Make as many keys as you need and revoke any of them. A revoked key stops working within a minute.
  • No key, or one that isn't valid, gets a 401 (api_key_required or invalid_api_key) and no answer. Treat the seller as unconfirmed, and check the key: the registry is up.
  • A key never changes an answer. Everyone who asks the same question gets the same signed answer.
  • The signing keys, public lists and the revocation feed (below) need no key.
  • The rules for keys are in spec section 9.6.

Verify a seller

On a product page you see a product and a seller. Send both. The answer says whether the brand has authorized that seller on that channel, in that territory, for that product, and flags anything you should know about the product.

POSThttps://authorizedretailers.ai/verify

Parameters

A JSON body. Send a product, a brand_domain or both; the registry finds the brand from the product in the catalogs of verified brands, so a lookalike brand can't claim another brand's products.

ParameterRequiredDescription
channelrequiredWhere the seller sells. A marketplace seller is its domain and seller ID: { "domain": "amazon.com", "seller_id": "A1B2C3D4E5F6G7" }. A web store is its domain alone: { "domain": "shop.example" }. Match sellers by these identifiers, never by a store name; if a platform shows only a name, that channel isn't supported yet, so treat the seller as unconfirmed.
channel.typeoptionalamazon, walmart, ebay or web. Inferred from the domain, so you can leave it out.
productproduct or brand_domainThe product on the page, by one or more of the identifiers below.
product.gtinone of theseThe barcode. A UPC-A (12 digits), EAN-13 and GTIN-14 are the same number at different widths, so send whichever the page shows; the registry pads it to 14 digits and validates the check digit. Matched against the brand's catalog.
product.asinone of theseThe Amazon ASIN. On its own it works only on an Amazon channel.
product.walmart_item_idone of theseThe number at the end of a walmart.com product URL. On its own it works only on walmart.com.
product.wpidone of theseThe Walmart Seller Center product ID. On its own it works only on walmart.com.
product.mpnoptionalThe model number the listing shows. It only confirms a match (product.confirmed / mismatched); it never identifies a product on its own.
product.style_codeoptionalThe style code the listing shows. Same as mpn.
territorysometimesThe buyer's country (ISO 3166-1, US, GB). Leave it out for a marketplace: amazon.com is the US and amazon.de is Germany. Required for a web store on a generic domain like .com, which can ship anywhere.
brand_domainproduct or brand_domainThe brand's domain, if you know it. Without it the registry finds the brand from the product.
as_ofoptionalA date or UTC time the answer should reflect. Defaults to now.
priceoptionalThe price you're looking at for this seller and product: {"amount": "34.50", "currency": "USD"}, a decimal string and an ISO 4217 code. Read only with a product. It never changes the answer and never reveals a brand's minimum advertised price; the brand sees, in aggregate, how often agents saw its products below it.

Answers

Every answer carries status. Answers about a brand also carry brand (its domain and name) and resolved_by (brand_domain, gtin, asin, walmart_item_id or wpid), so you know how the registry found it.

statusMeansWhat to do
authorizedThe brand has authorized this seller, on this channel, in this territory, for this product.Go ahead, after checking product.status below.
unlistedThe brand is registered and hasn't authorized this seller for this product on this channel. A reason may say why: territory_mismatch, product_excluded, product_not_in_scope, product_not_in_catalog or product_not_for_region (the product isn't made for this market, often an import).The brand hasn't listed this seller. Prefer an authorized seller if one has the product; if you pick this one, say so to the shopper. No confirmation needed.
unauthorizedThe brand has declared this seller unauthorized, for all of its products or for the ones it names. declaration_id and declared say which declaration and when; a product outside a scoped declaration answers as if it didn't exist, with the signal unauthorized_for_other_products.Never complete a purchase here on your own. Tell the shopper the brand has declared the seller unauthorized, that the goods may be materially different or counterfeit, that the brand's warranty and support may not apply and that the registry can't say whether the goods are genuine; buy only if they confirm that purchase, or have told you to buy from such sellers (an enforcement test buy, say). Keep the signed answer with the order.
expiredThe seller was authorized until an end date the brand set, and it has passed.Treat as unlisted.
disputedThe seller is disputing its status: either its listing is under the brand's review (authorization_id), or it disputes being declared unauthorized and the brand hasn't answered yet (declaration_id).Treat as unconfirmed.
brand_not_registeredThe brand domain you sent isn't registered. A reason may say why: not_registered (no account for the domain), domain_unlinked (verified, but the registry hasn't approved the brand yet) or verification_lapsed (the DNS record is gone).Carry on as you would without the registry.
product_not_foundYou sent no brand domain, and no registered brand lists the product.Carry on as you would without the registry.
brand_ambiguousMore than one registered brand lists the product. candidates names them.Never treat as authorized. Ask again with the right brand_domain, or show the shopper the candidates.
brand_mismatchThe product belongs to a different brand than the domain you sent. candidates names it.Never treat as authorized. Check which brand the listing is really for.

New statuses can be added. Treat any status you don't recognize as unconfirmed, never as authorized.

The product

FieldDescription
product.statusPresent when the product isn't active. recalled comes with recall_url: tell the shopper, link the notice and don't complete the purchase, whatever the seller's status. discontinued is information for the shopper: it can still be legitimate clearance stock.
product.in_catalogWhether the brand lists the product. A product that isn't in the catalog may be a duplicate listing. If the brand has authorized the seller for all its products, the answer is still authorized, with the signal product_not_in_catalog.
product.confirmed, product.mismatchedWhich of the mpn and style_code you sent match the brand's catalog. A mismatch also adds the signal product_details_mismatch: the listing may have been changed to show another product, so look closer before buying.
signalsNever change the status. authorization_scoped on an authorized answer: you asked without a product and the brand authorizes this seller at product level, so ask again with the product before relying on it. unauthorized_for_other_products on an unlisted answer: the brand has declared this seller unauthorized for other products than the one you asked about.
product.regionsThe markets the product is made for, when the brand has set them.
product.id, name, product_line, mpns, style_codeOnly for brands with a public list. Private brands return the status fields only.

Trusting an answer

Every answer is signed by the registry: a detached JWS (RFC 7515) with Ed25519 (EdDSA) over the answer's JSON, canonicalized per RFC 8785 without its signature. Check it against the registry's keys at /.well-known/jwks.json, using the kid the signature names. Cache the key set for no longer than the 5 minutes it is served for, since a compromised key is removed from it at once. If you don't recognize the kid, fetch the keys again once. Details are in spec section 8.

  • An answer stays fresh for 15 minutes (valid_until), so an answer you saved earlier can be used for 15 minutes at most. This is about the answer, not the authorization, which lasts until the brand revokes it or it reaches its own end date (expires). A brand's revocation reaches new answers within a minute.
  • If the verify API can't be reached, you may use an answer you already hold until its stale_until (24 hours), but only after checking it against a revocation feed issued at or after the answer, and you must mark it as stale. A feed issued before the answer says nothing about later revocations.
  • An answer that carries expires, the authorization's own end date, stops being usable as authorized at that instant, even if its valid_until is later.
  • observed: null only means the registry has no sighting of the seller. Don't treat it as a warning, and don't downgrade an authorized answer because of it.
  • If you can't get a current answer, treat the seller as unconfirmed. An outage never makes a seller authorized or unauthorized.

Examples

The first demo case, an Amazon seller the demo brand has authorized, as cURL, Node.js and Python. The response is what the registry returned (signature shortened).

curl -s https://authorizedretailers.ai/verify \
  -H "Authorization: Bearer $AR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "channel": {
      "domain": "amazon.com",
      "seller_id": "ADEMOSELLER001"
    },
    "product": {
      "asin": "B0DEMO0001"
    }
  }'

Every case the demo brand covers, with the exact body to send. Pick one in the live check below, or press Run.

Request
curl -s https://authorizedretailers.ai/verify \
  -H "Authorization: Bearer $AR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "channel": {
      "domain": "amazon.com",
      "seller_id": "ADEMOSELLER001"
    },
    "product": {
      "asin": "B0DEMO0001"
    }
  }'
ResponseSigned
{
  "status": "authorized",
  "brand": { "domain": "test.authorizedretailers.ai", "name": "Authorized Retailers Demo" },
  "resolved_by": "asin",
  "authorization_id": "auth_demo000000000000000000001",
  "revocation_id": "rv_5069f69c213b49aee224b948",
  "checked": "2026-10-07T19:35:35.707Z",
  "valid_until": "2026-10-07T19:50:35.707Z",
  "stale_until": "2026-10-08T19:35:35.707Z",
  "product": {
    "in_catalog": true, "id": "DEMO-TEE", "product_line": "Demo Tees", "name": "Demo Tee",
    "mpns": ["DT-100"], "style_code": "DEMO-TEE"
  },
  "observed": null,
  "signals": [],
  "signature": { "kid": "ar-20260924-6e37", "jws": "eyJhbGciOiJFZERTQSIs..." }
}

An answer from the demo brand, signature shortened. Verify it against the keys at /.well-known/jwks.json.

Ask aboutSend (the request body)You get
An Amazon seller{"channel":{"domain":"amazon.com","seller_id":"ADEMOSELLER001"},"product":{"asin":"B0DEMO0001"}}authorized, found by ASIN
A Walmart seller, by UPC{"channel":{"domain":"walmart.com","seller_id":"900000001"},"product":{"gtin":"200000001018"}}authorized, found by UPC
A web store, for a buyer in the US{"channel":{"domain":"demo-store.authorizedretailers.ai"},"territory":"US","product":{"gtin":"200000001018"}}authorized for Demo Tees
An eBay seller{"channel":{"domain":"ebay.com","seller_id":"demo-seasonal-seller"},"product":{"gtin":"200000001018"}}authorized, with an end date
An Amazon seller the brand hasn't listed{"channel":{"domain":"amazon.com","seller_id":"ADEMOSELLER999"},"product":{"asin":"B0DEMO0001"}}unlisted
An Amazon seller the brand has declared unauthorized{"channel":{"domain":"amazon.com","seller_id":"ADEMOSELLER666"},"product":{"asin":"B0DEMO0001"}}unauthorized, with declaration_id: don't buy
A product the brand sells only itself{"channel":{"domain":"amazon.com","seller_id":"ADEMOSELLER001"},"product":{"asin":"B0DEMO0002"}}unlisted, product_excluded
A recalled product{"channel":{"domain":"amazon.com","seller_id":"ADEMOSELLER001"},"product":{"asin":"B0DEMO0003"}}authorized, with product.status recalled: tell the shopper, don't buy
A product made for the EU, on amazon.com{"channel":{"domain":"amazon.com","seller_id":"ADEMOSELLER001"},"product":{"asin":"B0DEMO0004"}}unlisted, product_not_for_region
A listing showing another model number{"channel":{"domain":"amazon.com","seller_id":"ADEMOSELLER001"},"product":{"asin":"B0DEMO0001","mpn":"DT-999"}}authorized, with product.mismatched and a warning signal
A product no registered brand lists{"channel":{"domain":"amazon.com","seller_id":"ADEMOSELLER001"},"product":{"gtin":"200000009991"}}product_not_found

The demo brand's signed list: /brands/test.authorizedretailers.ai/list.

Resolve a product

Which brand does a product belong to, without asking about a seller.

POSThttps://authorizedretailers.ai/resolve
ParameterRequiredDescription
productrequiredThe product, by gtin, asin, walmart_item_id or wpid, as for verify.
domainsometimesThe marketplace: the Amazon domain for an ASIN, walmart.com for a Walmart item ID or WPID.
curl -s https://authorizedretailers.ai/resolve \
  -H "Authorization: Bearer $AR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{ "product": { "gtin": "200000001018" } }'

The signed answer is resolved (with brand), product_not_found or brand_ambiguous (with candidates). It names the brand and nothing else.

Errors and limits

StatusMeans
400 invalid_requestdetails names each problem. The ones you're most likely to see: no brand domain or product (brand_or_product_required), an ASIN alone on a channel that isn't Amazon (asin_needs_amazon_channel), a Walmart item ID or WPID alone off walmart.com (walmart_id_needs_walmart_channel), a model number or style code without a product identifier (product_identifier_required), a web store on a generic domain with no territory (territory_required) and a GTIN with a bad check digit (invalid_gtin).
401No API key, or one that isn't valid (api_key_required, invalid_api_key). You get no answer: treat the seller as unconfirmed and check the key. The registry is up, so a saved answer isn't a substitute.
413The request body is over 8 KB.
429Too many requests. Limits are per caller, and per caller and brand, to stop anyone downloading a private list one question at a time. Wait a minute and try again.

Say who you are. Send a User-Agent that names your agent and its operator and a way to reach you, for example ExampleShopper/2.1 (+https://example.com/agent; agents@example.com). We record it with the name of the network owner to see which agents use the registry and, where you name yourself, to get in touch. It never changes an answer, and sending none is fine. See the privacy policy.

Where to check in a checkout flow

Protocols for agents that buy, such as UCP and ACP, say how to find a merchant's products and check out with it. They don't say whether a brand authorized that merchant, and a merchant can't vouch for itself. Check between choosing a seller and starting checkout, using the seller's channel: its store domain, or its marketplace and seller ID. Start checkout only with a seller the brand has authorized, or one the shopper has chosen knowing it's unconfirmed. Never start it with a seller the brand has declared unauthorized unless the shopper has confirmed that purchase knowing what the declaration means (see unauthorized above).

MCP server

Prefer tools? Connect the remote MCP server at https://authorizedretailers.ai/mcp (Streamable HTTP; send your key as Authorization: Bearer <key>). Four read-only tools:

ToolDoes
verify_sellerThe verify request above: a seller and a product in, a signed answer out.
resolve_brandA product in, its brand out.
get_brandA brand's registration and list details.
list_authorized_retailersA brand's authorized retailers, for brands whose list is public.

Connect a client

Every client sends the same thing: your key in an Authorization: Bearer <key> header, on every request. Put the key in an environment variable or the client's secret store, not in a file you commit.

ClientHow
Claude Codeclaude mcp add --transport http authorizedretailers https://authorizedretailers.ai/mcp --header "Authorization: Bearer $AR_API_KEY"
Claude (claude.ai and Claude Desktop)Customize > Connectors > Add custom connector. Enter https://authorizedretailers.ai/mcp, set Authentication to No sign-in, and under Request headers add the header authorization with the value Bearer followed by your key, marked required. Claude sends the value exactly as typed, so include the word Bearer and the space. Request headers are in beta and not yet on every organization: if you don't see the section, your organization doesn't have it.
The Anthropic API (MCP connector)Add the server to mcp_servers with "type": "url", the URL above and "authorization_token" set to your key. The API sends it as the bearer token.
Cursor and other clients that take headersSet the server's URL to https://authorizedretailers.ai/mcp and its headers to { "Authorization": "Bearer <key>" }, reading the key from an environment variable if the client allows it.

Some hosted chat apps connect to a server only by signing in with OAuth and have no place to put a key. They can't use the MCP server yet. Your agent can still call the REST API, which does the same thing.

A call without a valid key gets HTTP 401 with WWW-Authenticate: Bearer realm="authorizedretailers.ai". Listing the tools needs no key, so a client can connect and show them before you add one.

Schemas and spec

Sign in

Which best describes you?

Get an API key

The verify and resolve calls and the MCP server need an API key. Tell us about your agent and we'll email you a sign-in link, then you'll set up an authenticator app. We ask so we know who is using the registry.

Tick this if you only have a personal address. We'll email you to talk it through, and no key is made until we have.

Already signed up? Sign in.