# OpenStock API (v1.1.0)

## Introduction

**The only API in the world plugged into the backend of 200k top stores that serves generated merchant offers directly, in a single call.** Simple to implement, with significant coverage of the US merchant market.

Generate or claim merchant-backed discounts. Inspect the reliability of public codes. Add delivery and shopper context. **Three batched endpoints. One bearer key.**

Offers at partnered merchants are generated at the store on request, not scraped or hunted for: Checkmate has a deep partnership network in ecommerce, plugged directly into the checkout of thousands of partnered stores and working with publishers including NBCUniversal.

There are two kinds of discount-code API. An **information API** tells you a code exists somewhere and leaves the shopper to find out at checkout whether it still works. A **generation API** creates the code. OpenStock is the second kind, and every code in every response says which it is (`source`: `generated`, `merchant_issued` or `observed`). We are not aware of another cross-merchant shopping API that generates a discount code at the store on request.

It is served from Cloudflare’s edge in 300+ cities — the same network that fronts chatgpt.com — over auto-scaling storage, designed for an assistant with a billion monthly users, and checked with a real request every five minutes at https://status.openstock.sh.

| Start here | What you can inspect |
| --- | --- |
| **Generate a Brooklinen offer** | Resolve a real store, request an offer, inspect source, eligibility and cashback |
| **Evaluate a public code** | Recent success score, last successful checkout and lifetime attempts |
| **Add delivery and shopper context** | Shipping provenance and merchant-level purchases versus visits |

## Security and compliance

OpenStock is built by Checkmate, a SOC 2 compliant company, with compliance monitored by Drata.

- **Encrypted in transit:** connect to the production API at https://api.openstock.sh over HTTPS.
- **Authenticated access:** every merchant and shopper endpoint requires a bearer API key. Keys are stored as SHA-256 hashes and can be revoked. Keep your key server-side and send it in the `Authorization` header.
- **Hashed shopper identity:** shopper-history requests accept a SHA-256 digest of the lowercase, trimmed email address. Never send raw emails; use history only within your authorized integration.

## Your first live offer

Set `OPENSTOCK_API_KEY` in your environment, then copy and run this Python 3 quickstart. It uses only the standard library, resolves Brooklinen by domain and passes the returned merchant ID into the offer request. Keep your key server-side.

```bash
python3 - <<'PY'
import json, os, uuid, urllib.error, urllib.request

BASE_URL = "https://api.openstock.sh"
api_key = os.environ.get("OPENSTOCK_API_KEY")
if not api_key:
    raise SystemExit("Set OPENSTOCK_API_KEY to your partner key before running.")

def post(path, body):
    request = urllib.request.Request(
        BASE_URL + path, data=json.dumps(body).encode(),
        headers={"Authorization": "Bearer " + api_key,
                 "Content-Type": "application/json"}, method="POST")
    try:
        with urllib.request.urlopen(request, timeout=30) as response:
            print("Request ID:", response.headers.get("X-Request-Id"))
            return json.load(response)
    except urllib.error.HTTPError as error:
        request_id = error.headers.get("X-Request-Id", "unknown")
        raise SystemExit(f"HTTP {error.code} (request {request_id}): {error.read().decode()}")

search = post("/v1/merchants/search", {"queries": [
    {"query_id": "brooklinen", "type": "domain", "value": "brooklinen.com"}]})
merchant = search["results"][0]["items"][0]
if merchant["id"] is None:
    raise SystemExit("Merchant did not resolve; inspect the search response.")
print("Merchant and shipping:", json.dumps(merchant, indent=2))

# Keep this body and key to retry the same shopping session.
body = {"queries": [{"query_id": "brooklinen", "merchant_id": merchant["id"],
                     "idempotency_key": str(uuid.uuid4()), "limit": 3}]}
print("Offer request (save for retry):", json.dumps(body))
offers = post("/v1/merchants/codes", body)
print(json.dumps(offers, indent=2))
PY
```

**Inspect the result:** `source: "generated"` identifies a code minted or claimed through the merchant integration. Read `single_use`, `conditions`, `description` and expiry alongside it. A null `code` is a codeless offer: the discount applies through `activation_url`, so send the shopper there rather than showing a code.

**Replay issuance:** call the codes endpoint again with the saved request body. Keep the same merchant and `idempotency_key` and you get the same code back rather than a second one. Start a new key for a new intended issuance; include your partner and session identity in your production key namespace.

**Name the shopper:** add `shopper_name` (a first name, never an email or phone) and a code minted for that request carries it, `CHECKMATE-RORY-4O8HHBUN-CN`, so it reads as the shopper’s own. Only a single word of letters is used; anything else is ignored. The name applies at first issuance: a replay returns the code already minted.

## Interpreting the response

| Field | Meaning for your integration |
| --- | --- |
| `source` + `single_use` | Merchant integration, merchant-configured offer, or observed public code; actual redemption policy is explicit |
| `probability` | Public-code reliability score from recent checkout activity; merchant-backed offers use 1, subject to eligibility |
| `conditions` + `description` | Check minimum spend, product/customer restrictions and subscription terms; tags may not capture every restriction |
| `cashback` + `activation_url` | An additional offer when available, including when no code clears your threshold |
| `estimated_shipping.basis` | Distinguishes merchant order data from a processing estimate or network default |
| history `type` | `purchased` records an observed order; `visited` records browsing; purchases are listed first |

## Explore the examples

Open **Merchants → Batch merchant offer codes** and choose Generated offer — Brooklinen, Generated offer with the shopper’s name, Observed public code, Cashback with no qualifying code, or Unknown merchant. Search also includes measured delivery, a network estimate and a miss. Shoppers includes a purchase-and-visit example plus an empty response.

The named examples are complete responses with real merchant IDs, so every request in them can be run live.

[Full text guide](https://api.openstock.sh/llms-full.txt) · [OpenAPI 3.1](https://api.openstock.sh/openapi.json) · [Live demo](https://openstock.sh/demo) · [Service status](https://status.openstock.sh)

## Generated, not found

At partnered merchants OpenStock is integrated with the store’s checkout and creates a new single-use code at the moment of the request: the code did not exist before you asked, and it applies by construction, subject to the eligibility the merchant configured. For the rest of the network it does what an information API does, with one addition a feed cannot make: it measures whether each public code still works and tells you.

| | A coupon or deals feed | OpenStock |
| --- | --- | --- |
| Where a code comes from | Scraped or syndicated from the web, some time ago | Created at the store for this request (`source: "generated"`), configured by the merchant with Checkmate (`merchant_issued`), or observed in the wild (`observed`) |
| Does it work? | Unknown until checkout; “verified” means someone saw it work once | `generated` and `merchant_issued` apply by construction (`probability: 1`, subject to `conditions`); `observed` codes carry their measured recent success rate |
| What the assistant can say | “Try this code” | “Here is your code” |
| Single-use and abuse | Public codes leak, get shared, and get killed by the merchant | Generated codes are single-use and tied to the request by `idempotency_key`; the merchant sets the discount |
| When there is no code | Nothing | Cashback on the order through `activation_url` |
| Evidence in the response | None | `source`, `probability`, `partnered`, `conditions`, `last_success`, `apply_count`, `success_count` |

The distinction is in the JSON, not just in this document. Everything an ordinary deals API can return is what OpenStock labels `observed`.

## Coverage and capabilities

OpenStock is Checkmate’s partner API for a network of 284,000+ indexed merchants and 100M+ shoppers. Checkmate has a deep partnership network in ecommerce: it is plugged directly into the checkout of thousands of partnered stores and works with publishers including NBCUniversal, which is why offers at partnered merchants are generated at the store on request rather than scraped or hunted for.

- **Merchant-backed offers:** Shopify code creation and merchant pool claims, alongside configured codes and codeless offers. Every item reports its source and redemption policy.
- **Observed codes:** ranked by recent checkout reliability, with lifetime attempts and last-success evidence when available. Set min_probability to the bar you want to show a shopper; suppressed_code_count tells you how many fell below it.
- **Delivery:** median and quartiles in days. `measured_delivery`: observed order-to-delivery times for this merchant. `processing_plus_transit_estimate`: the merchant’s observed order-to-dispatch time plus an assumed 2 days in transit. `network_default`: we have no order data for this merchant yet, so this is what most US stores in the network deliver in (3–5 days); phrase it as "typically", not as this merchant’s figure. A null sample_size is unknown, not zero. Null estimated_shipping means delivery does not apply or the merchant did not resolve.
- **Shopper history:** the merchants a shopper has bought from and browsed, purchases first.

## Shopper identity

For a live lookup, SHA-256 hash the authorized shopper’s email after trimming whitespace and lowercasing it. Send the 64-character hex digest as email_sha256; never send the raw email. Use history only within your authorized integration. The example below shows both activity types; download the [fixture](https://openstock.sh/examples/shopper-history.json) or [ask the live demo](https://openstock.sh/demo?q=Has%20this%20shopper%20been%20to%20Brooklinen%20or%20Nike%3F) in a browser.

```python
import hashlib
email_sha256 = hashlib.sha256(email.strip().lower().encode("utf-8")).hexdigest()
```

## Request and result conventions

- Authenticate resource requests with `Authorization: Bearer $OPENSTOCK_API_KEY`. Documentation, /health and the API root are public.
- Send a queries array: 1–50 for search, 1–20 for codes and history. Results preserve query order and echo query_id, which must be unique within a batch; a duplicate is a 400 that names the position.
- **Domains:** a `domain` query accepts a bare domain or any URL on it — scheme, `www.`, port, path and query are ignored, so pass the product URL you have. `domains` in a result are hostnames only.
- **Search miss:** items contains one object with id: null, name: null, domains: [], popularity_rank: null and estimated_shipping: null.
- **Codes miss:** items: [], activation_url: null, cashback: null, partnered: false and suppressed_code_count: 0. Empty items with a non-null activation_url means the store resolved but no code cleared your threshold; the shopper still earns cashback through the link.
- **History miss:** items: [].
- **Errors:** request-validation and malformed-JSON failures use 400; missing or invalid credentials use 401. The documented JSON error envelope is {"error":{"code":"…","message":"…"}}. Missing resource data is represented inside a 200 batch, not a 404.
- **Tracing:** every response includes X-Request-Id. Search includes X-OpenStock-Cache: hit, partial or miss.
- **CORS:** `/v1` answers preflight and allows any origin, so a browser-hosted tool can call it directly. Keep the key server-side all the same.
- **Retries:** use bounded backoff for transport failures and transient server errors, preserving idempotency_key. Fix a 400 or 401 before retrying. A timeout may happen after issuance; reuse the key rather than issuing again.
- **Limits:** no hard rate limit is currently enforced by this API. Throughput is agreed per partner; batch requests and tell us your expected peaks.
- **Versioning:** /v1 is the contract. Fields can be added within a major version; clients should tolerate additional fields.

## Endpoint reference and complete examples

The request and response examples below are the same named examples served in /openapi.json and the interactive reference. They are schema-checked and complete; no response fields are omitted.

### POST /v1/merchants/search

**Batch merchant search** · operationId `searchMerchants`

Search for merchants by name (fuzzy) or domain (exact match). Up to 50 queries per request. Each query carries its own `query_id` that is echoed back so callers can correlate responses. Each result contains an `items` array with matched merchants. Name queries return up to `limit` items per result (default 5, max 20). Misses return a single item with `id: null` — there are no 404s.

#### Resolve Brooklinen and inspect delivery

Brooklinen resolved by domain, with measured delivery times and popularity rank. Run the request for current values.

Complete request:

```bash
curl -sS -X POST https://api.openstock.sh/v1/merchants/search \
  -H "Authorization: Bearer $OPENSTOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"queries":[{"query_id":"brooklinen","type":"domain","value":"brooklinen.com","limit":1}]}'
```

Complete response example:

```json
{
  "results": [
    {
      "query_id": "brooklinen",
      "items": [
        {
          "id": "01g4s5d0h956te86ff90h6xts4",
          "name": "Brooklinen",
          "domains": [
            "brooklinen.com"
          ],
          "popularity_rank": null,
          "estimated_shipping": {
            "unit": "days",
            "median": 6,
            "q1": 3,
            "q3": 10,
            "basis": "measured_delivery",
            "sample_size": null
          }
        }
      ]
    }
  ]
}
```

#### Understand a network delivery estimate

A merchant with no order data of its own gets the network default, labelled network_default so you can phrase it as "typically".

Complete request:

```bash
curl -sS -X POST https://api.openstock.sh/v1/merchants/search \
  -H "Authorization: Bearer $OPENSTOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"queries":[{"query_id":"network-estimate","type":"domain","value":"example.invalid","limit":1}]}'
```

Complete response example:

```json
{
  "results": [
    {
      "query_id": "network-estimate",
      "items": [
        {
          "id": "example-merchant",
          "name": "Example Goods",
          "domains": [
            "example.invalid"
          ],
          "popularity_rank": null,
          "estimated_shipping": {
            "unit": "days",
            "median": 4,
            "q1": 3,
            "q3": 5,
            "basis": "network_default",
            "sample_size": null
          }
        }
      ]
    }
  ]
}
```

#### Handle an unknown merchant

Complete miss response: search returns one null-ID item, not an empty items array or a 404.

Complete request:

```bash
curl -sS -X POST https://api.openstock.sh/v1/merchants/search \
  -H "Authorization: Bearer $OPENSTOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"queries":[{"query_id":"unknown","type":"domain","value":"unknown-merchant.invalid","limit":1}]}'
```

Complete response example:

```json
{
  "results": [
    {
      "query_id": "unknown",
      "items": [
        {
          "id": null,
          "name": null,
          "domains": [],
          "popularity_rank": null,
          "estimated_shipping": null
        }
      ]
    }
  ]
}
```

Request body:

- `queries` (array of object, required) [1–50 items] — Batch of search queries (1–50). Each query returns a result with an `items` array. Name queries with `limit > 1` may yield multiple items. Misses return an `items` array with a single entry where `id` is null.
  - `query_id` (string, required) — Caller-supplied opaque identifier correlated back in results
  - `type` (`name` | `domain`, required) — "name" triggers fuzzy search; "domain" is an exact-match lookup
  - `value` (string, required) — Search term: a merchant name fragment, or for `domain` a domain or any URL on it — scheme, `www.`, path and query are ignored
  - `region` (string, optional) — Optional ISO 3166-1 alpha-2 region filter (name queries only)
  - `limit` (integer, optional) [range 1–20; default 5] — Maximum number of results to return for this query (name queries only, domain queries always return at most one result). Defaults to 5.

Response `200` — Per-query results. Each result has an `items` array. A name query with limit > 1 may produce multiple items. Misses have id: null

- `results` (array of object, required)
  - `query_id` (string, required)
  - `items` (array of object, required) [1–∞ items] — Matched merchants for this query, best first. Name queries are ordered by exact prefix match, then by `popularity_rank`, so `items[0]` is the merchant to use unless the shopper said otherwise. Always contains at least one entry; misses return a single entry with `id: null`.
    - `id` (string | null, required) — Checkmate merchant ID, or null when not found
    - `name` (string | null, required) — Merchant display name, or null when no match
    - `domains` (array of string, required) — Merchant domains
    - `popularity_rank` (integer | null, required) — Rank by shopper traffic across the Checkmate network, 1 = most visited. When several merchants share a name (there are three "Adidas"), the one with the lowest rank is almost always the one the shopper means. Null for unranked merchants.
    - `estimated_shipping` (object | null, required) — Shipping time statistics. Present for every matched merchant that ships physical goods — check `basis` to see whether it is this merchant’s own data or the network default. Null for merchants where delivery does not apply (travel, food delivery, restaurants, telecom, finance, software, streaming, tickets) and on a miss (`id: null`).
      - `unit` (`days`, required) — Unit for all shipping time values in this object
      - `median` (number, required) — Median days from order to delivery
      - `q1` (number | null, required) — First quartile (25th percentile) days to deliver, or null when unknown
      - `q3` (number | null, required) — Third quartile (75th percentile) days to deliver, or null when unknown
      - `basis` (`measured_delivery` | `processing_plus_transit_estimate` | `network_default`, required) — Where the numbers come from. `measured_delivery`: observed order-to-delivery times for this merchant. `processing_plus_transit_estimate`: the merchant’s observed order-to-dispatch time plus an assumed 2 days in transit. `network_default`: we have no order data for this merchant yet, so this is what most US stores in the network deliver in (3–5 days); phrase it as "typically", not as this merchant’s figure.
      - `sample_size` (integer | null, required) — Orders the figures were computed from, or null when the source does not report it. Treat small samples with caution.

Response `400` — Invalid request body or query cap exceeded (max 50 queries)

Invalid request body — complete response:

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "queries: Too small: expected array to have >=1 items"
  }
}
```

Malformed JSON — complete response:

```json
{
  "error": {
    "code": "MALFORMED_JSON",
    "message": "Request body must be valid JSON"
  }
}
```

Response `401` — Missing or invalid API key

Missing bearer key — complete response:

```json
{
  "error": {
    "code": "MISSING_AUTH",
    "message": "Authorization header is required. Use: Authorization: Bearer <api_key>"
  }
}
```

Invalid or revoked key — complete response:

```json
{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "The provided API key is not valid."
  }
}
```

### POST /v1/merchants/codes

**Batch merchant offer codes** · operationId `getMerchantCodes`

Request merchant-backed and observed offers for up to 20 merchants. Each result includes ranked codes, cashback when available, and an activation link. Read each item’s source, single_use, conditions and description; partnered alone does not establish code availability. A null code is a codeless offer, not a successfully issued coupon. Use min_probability and limit to filter codes; suppressed_code_count reports removed items. Empty items with a non-null activation_url means the store resolved but has no qualifying code. A null activation_url is a miss-shaped result, also returned for an unavailable per-merchant lookup. idempotency_key is required: use an opaque value unique to the partner, shopper/session and merchant, and reuse it when retrying issuance. It deduplicates code issuance; it does not freeze the entire response or extend code validity. shopper_name is optional: a first name to carry on the face of a freshly minted code (CHECKMATE-RORY-4O8HHBUN-CN); a replay returns the code already minted.

#### Generated offer — Brooklinen

A code generated at Brooklinen for this request. The example code is a placeholder; run the request with your own idempotency key for a live one, and keep that key for retries.

Complete request:

```bash
curl -sS -X POST https://api.openstock.sh/v1/merchants/codes \
  -H "Authorization: Bearer $OPENSTOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"queries":[{"query_id":"brooklinen","merchant_id":"01g4s5d0h956te86ff90h6xts4","idempotency_key":"replace-with-your-unique-shopping-session-key","limit":1}]}'
```

Complete response example:

```json
{
  "results": [
    {
      "query_id": "brooklinen",
      "cashback": {
        "value_type": "percent",
        "value_amount": 3
      },
      "partnered": true,
      "activation_url": "https://api.openstock.sh/mr/01g4s5d0h956te86ff90h6xts4?ref=ck_partner_api",
      "suppressed_code_count": 0,
      "items": [
        {
          "code": "EXAMPLE-NOT-REDEEMABLE",
          "source": "generated",
          "probability": 1,
          "single_use": true,
          "value_type": "percent",
          "value_amount": 15,
          "currency": null,
          "conditions": [],
          "redirect_url": "https://api.openstock.sh/mr/01g4s5d0h956te86ff90h6xts4?code=EXAMPLE-NOT-REDEEMABLE&ref=ck_partner_api",
          "expires_at": null,
          "last_success": null,
          "description": "15% off eligible items; merchant exclusions may apply.",
          "apply_count": null,
          "success_count": null
        }
      ]
    }
  ]
}
```

#### Generated offer with the shopper’s name

The Brooklinen issuance with shopper_name set: the minted code carries the shopper’s first name so it reads as theirs. The example code is a placeholder; a replay under the same idempotency key returns the code already minted, name or not.

Complete request:

```bash
curl -sS -X POST https://api.openstock.sh/v1/merchants/codes \
  -H "Authorization: Bearer $OPENSTOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"queries":[{"query_id":"brooklinen","merchant_id":"01g4s5d0h956te86ff90h6xts4","idempotency_key":"replace-with-your-unique-shopping-session-key","shopper_name":"Rory","limit":1}]}'
```

Complete response example:

```json
{
  "results": [
    {
      "query_id": "brooklinen",
      "cashback": {
        "value_type": "percent",
        "value_amount": 3
      },
      "partnered": true,
      "activation_url": "https://api.openstock.sh/mr/01g4s5d0h956te86ff90h6xts4?ref=ck_partner_api",
      "suppressed_code_count": 0,
      "items": [
        {
          "code": "CHECKMATE-RORY-EXAMPLE0-CN",
          "source": "generated",
          "probability": 1,
          "single_use": true,
          "value_type": "percent",
          "value_amount": 15,
          "currency": null,
          "conditions": [],
          "redirect_url": "https://api.openstock.sh/mr/01g4s5d0h956te86ff90h6xts4?code=CHECKMATE-RORY-EXAMPLE0-CN&ref=ck_partner_api",
          "expires_at": null,
          "last_success": null,
          "description": "15% off eligible items; merchant exclusions may apply.",
          "apply_count": null,
          "success_count": null
        }
      ]
    }
  ]
}
```

#### Observed public code

A public code at Nike with its measured probability, last success and lifetime counters. Run the request for current codes.

Complete request:

```bash
curl -sS -X POST https://api.openstock.sh/v1/merchants/codes \
  -H "Authorization: Bearer $OPENSTOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"queries":[{"query_id":"nike","merchant_id":"01g4s4gvb1kdc8kz69cm5q2b4f","idempotency_key":"replace-with-your-unique-shopping-session-key","limit":3}]}'
```

Complete response example:

```json
{
  "results": [
    {
      "query_id": "nike",
      "cashback": null,
      "partnered": false,
      "activation_url": "https://api.openstock.sh/mr/01g4s4gvb1kdc8kz69cm5q2b4f?ref=ck_partner_api",
      "suppressed_code_count": 0,
      "items": [
        {
          "code": "EXAMPLE-PUBLIC-CODE",
          "source": "observed",
          "probability": 0.42,
          "single_use": false,
          "value_type": "percent",
          "value_amount": 20,
          "currency": null,
          "conditions": [
            "product_restricted"
          ],
          "redirect_url": null,
          "expires_at": null,
          "last_success": "2026-09-10T21:14:03.000Z",
          "description": "20% off selected products",
          "apply_count": 1834,
          "success_count": 611
        }
      ]
    }
  ]
}
```

#### Cashback with no qualifying code

min_probability filtered every code out, and the shopper still earns cashback through activation_url.

Complete request:

```bash
curl -sS -X POST https://api.openstock.sh/v1/merchants/codes \
  -H "Authorization: Bearer $OPENSTOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"queries":[{"query_id":"nike","merchant_id":"01g4s4gvb1kdc8kz69cm5q2b4f","idempotency_key":"replace-with-your-unique-shopping-session-key","min_probability":0.5,"limit":3}]}'
```

Complete response example:

```json
{
  "results": [
    {
      "query_id": "nike",
      "cashback": {
        "value_type": "percent",
        "value_amount": 3
      },
      "partnered": false,
      "activation_url": "https://api.openstock.sh/mr/01g4s4gvb1kdc8kz69cm5q2b4f?ref=ck_partner_api",
      "suppressed_code_count": 1,
      "items": []
    }
  ]
}
```

#### Unknown merchant

A merchant_id that matched nothing: empty items, null activation_url and cashback, partnered false.

Complete request:

```bash
curl -sS -X POST https://api.openstock.sh/v1/merchants/codes \
  -H "Authorization: Bearer $OPENSTOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"queries":[{"query_id":"unknown","merchant_id":"unknown-merchant","idempotency_key":"replace-with-your-unique-shopping-session-key"}]}'
```

Complete response example:

```json
{
  "results": [
    {
      "query_id": "unknown",
      "cashback": null,
      "partnered": false,
      "activation_url": null,
      "suppressed_code_count": 0,
      "items": []
    }
  ]
}
```

Request body:

- `queries` (array of object, required) [1–20 items] — Batch of codes queries (1–20); query_id unique within the batch
  - `query_id` (string, required) — Caller-supplied opaque identifier correlated back in results
  - `merchant_id` (string, required) — Checkmate merchant ID obtained from the search endpoint
  - `idempotency_key` (string, required) — Required opaque issuance key. Namespace it by partner, shopper/session and merchant; reuse it when retrying the same intended issuance. Do not send raw email or reuse a key across shoppers. It deduplicates issuance, not the entire response, and does not renew an expired or redeemed code.
  - `shopper_name` (string, optional) — Shopper's first name. When given, a code minted for this request carries it, e.g. CHECKMATE-RORY-4O8HHBUN-CN, so it reads as the shopper’s own. Only the first word is used and only when it is letters; anything else is ignored, as is a name the code has no room for. It applies at first issuance: a replay under the same idempotency_key returns the code already minted. Never send an email or phone.
  - `min_probability` (number, optional) [range 0–1] — Drop codes whose `probability` is below this threshold. Omit to receive every code. A value around 0.5 keeps only codes that usually work at checkout.
  - `limit` (integer, optional) [range 1–20] — Maximum number of codes to return for this query, taken from the top of the ranked list. Omit to receive up to 20.

Response `200` — Per-query results. Each result has an `items` array of codes. Misses return an empty `items` array

- `results` (array of object, required)
  - `query_id` (string, required)
  - `cashback` (object | null, required) — Cashback available through activation_url, subject to merchant eligibility and attribution. It is independent of the codes array and can remain available when no code qualifies. Null when unavailable; do not imply cashback on an ineligible or untracked order.
    - `value_type` (`percent`, required)
    - `value_amount` (number, required) — Percent of the order value returned to the shopper as cashback
  - `partnered` (boolean, required) — Whether this merchant has a direct Checkmate partnership. It does not guarantee an available code or successful issuance. Inspect each item’s source, code, single_use and eligibility. False on unknown or unavailable merchant results.
  - `activation_url` (string | null, required) — Attributed link to the merchant, set for every merchant in the network and independent of `items`. This is how an empty `items` is told apart from an unknown merchant: a non-null `activation_url` means we know the store and the link is available even though no code qualified. Null is a miss-shaped response: the merchant was unknown or the per-merchant lookup was unavailable.
  - `suppressed_code_count` (number, required) — How many codes your `min_probability` or `limit` removed from `items`. A zero-length `items` with a non-zero count here means codes exist but none cleared your threshold — not that the merchant has nothing.
  - `items` (array of object, required) — Offers sorted best-first by probability, with merchant-backed tiers ahead on ties. Inspect code, source and eligibility before selecting an item; a null code is codeless. Empty when no offer qualifies, the merchant is unknown, or its lookup is unavailable.
    - `code` (string | null, required) — Promo code string, or null for codeless offers
    - `probability` (number, required) [range 0–1] — Reliability score from 0–1. Observed public codes use recent checkout attempts, weighted toward newer activity and discounted for sparse successes. Merchant-backed offers are assigned 1; this is not evidence that a specific cart is eligible or that a null code was successfully issued. Read source, code, conditions and description together. The score usually differs from success_count / apply_count, which is a lifetime ratio.
    - `single_use` (boolean, required) — True when the code expires after one redemption
    - `source` (`generated` | `merchant_issued` | `observed`, required) — Provenance: generated means minted through the merchant integration or claimed from a merchant code pool. A retry may reuse an existing code, and some pools contain reusable codes; inspect single_use. merchant_issued is a configured merchant code or codeless offer, including a fallback when issuance is unavailable. observed is a public code scored from checkout activity. Source alone does not establish eligibility or successful redemption.
    - `value_type` (`percent` | `fixed` | `free_shipping` | `unknown`, required) — How the discount value is expressed. Minted and campaign codes carry the merchant-configured value; public codes are parsed from their description and fall back to "unknown" when the copy does not state a value.
    - `value_amount` (number | null, required) — Numeric discount amount; semantics depend on value_type (e.g. 10 = 10% or $10)
    - `currency` (string | null, required) — ISO 4217 currency code, present when value_type is "fixed"
    - `conditions` (array of string, required) — Condition tags extracted from merchant configuration or offer descriptions, e.g. minimum_subtotal, product_restricted, customer_restricted, applies_on_subscription. Tags are not exhaustive: read description and merchant terms too. An empty array does not establish unrestricted eligibility. up_to means the value is a ceiling, not the saving on every eligible purchase.
    - `redirect_url` (string | null, required) — Checkmate affiliate-attributed deep-link; null when unavailable
    - `expires_at` (number | null, required) — Unix timestamp (ms) when the code expires, or null when no expiry is reported. Null does not prove unlimited validity.
    - `last_success` (string | null, required) — ISO 8601 UTC timestamp of the most recent successful checkout with this code, or null when never observed
    - `description` (string | null, required) — Human-readable description of the offer or null
    - `apply_count` (number | null, required) — All-time number of checkout attempts with this code since we first saw it, or null when not tracked (campaign and minted codes). Lifetime volume, not recent reliability — use `probability` for that. A high count with a low `probability` is a code that used to work and has since been retired by the merchant.
    - `success_count` (number | null, required) — All-time number of those attempts that yielded a saving, or null when not tracked. Pair with `apply_count` for the lifetime ratio; pair `last_success` with `probability` for whether it works today.

Response `400` — Invalid request body, missing idempotency_key, or query cap exceeded (max 20)

Invalid request body — complete response:

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "queries: Too small: expected array to have >=1 items"
  }
}
```

Malformed JSON — complete response:

```json
{
  "error": {
    "code": "MALFORMED_JSON",
    "message": "Request body must be valid JSON"
  }
}
```

Response `401` — Missing or invalid API key

Missing bearer key — complete response:

```json
{
  "error": {
    "code": "MISSING_AUTH",
    "message": "Authorization header is required. Use: Authorization: Bearer <api_key>"
  }
}
```

Invalid or revoked key — complete response:

```json
{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "The provided API key is not valid."
  }
}
```

### POST /v1/shoppers/history

**Batch shopper purchase and browsing history** · operationId `getShopperHistory`

Look up merchant-level purchase and browsing history for authorized shoppers identified by SHA-256 hash of their normalised (lowercase-trimmed) email address. Never send raw email addresses. Up to 20 queries per request. Each result contains a flat `items` array of merchant interactions, purchases first; unknown shoppers return an empty `items` array. Use history only within your authorized integration.

#### Purchases and visits

A shopper with two purchases and a visit, purchases first. Send the SHA-256 of an authorized shopper’s normalized email for a live lookup.

Complete request:

```bash
curl -sS -X POST https://api.openstock.sh/v1/shoppers/history \
  -H "Authorization: Bearer $OPENSTOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"queries":[{"query_id":"example-shopper","email_sha256":"3a32f737ca25fb3d367dd3c8aaa91e9bf73f31445b0377a8de073eb33276dcb8"}]}'
```

Complete response example:

```json
{
  "results": [
    {
      "query_id": "example-shopper",
      "items": [
        {
          "merchant_id": "01g4s5d0h956te86ff90h6xts4",
          "merchant_name": "Brooklinen",
          "type": "purchased"
        },
        {
          "merchant_id": "01g4s5dc138qsnh4egsjys8g9c",
          "merchant_name": "Everlane",
          "type": "purchased"
        },
        {
          "merchant_id": "01g4s4gvb1kdc8kz69cm5q2b4f",
          "merchant_name": "Nike",
          "type": "visited"
        }
      ]
    }
  ]
}
```

#### Shopper not seen

A shopper we have not seen: empty items.

Complete request:

```bash
curl -sS -X POST https://api.openstock.sh/v1/shoppers/history \
  -H "Authorization: Bearer $OPENSTOCK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"queries":[{"query_id":"example-shopper","email_sha256":"3a32f737ca25fb3d367dd3c8aaa91e9bf73f31445b0377a8de073eb33276dcb8"}]}'
```

Complete response example:

```json
{
  "results": [
    {
      "query_id": "example-shopper",
      "items": []
    }
  ]
}
```

Request body:

- `queries` (array of object, required) [1–20 items] — Batch of shopper-history queries (1–20)
  - `query_id` (string, required) — Caller-supplied opaque identifier correlated back in results
  - `email_sha256` (string, required) — SHA-256 of the lowercase, trimmed email address, as 64 hex characters in either case. Never send the raw email.

Response `200` — Per-query shopper history. Each result has a flat `items` array of merchant interactions. Unknown shoppers return an empty array

- `results` (array of object, required)
  - `query_id` (string, required)
  - `items` (array of object, required) — Merchant interactions for this shopper, purchases first. Empty for a shopper we have not seen.
    - `merchant_id` (string, required) — Checkmate merchant ID
    - `merchant_name` (string, required)
    - `type` (`visited` | `purchased`, required) — "purchased" when an order was observed at the merchant; "visited" when only browsing sessions were observed. Purchased merchants are listed first.

Response `400` — Invalid request body, malformed email_sha256, or query cap exceeded (max 20)

Invalid request body — complete response:

```json
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "queries: Too small: expected array to have >=1 items"
  }
}
```

Malformed JSON — complete response:

```json
{
  "error": {
    "code": "MALFORMED_JSON",
    "message": "Request body must be valid JSON"
  }
}
```

Response `401` — Missing or invalid API key

Missing bearer key — complete response:

```json
{
  "error": {
    "code": "MISSING_AUTH",
    "message": "Authorization header is required. Use: Authorization: Bearer <api_key>"
  }
}
```

Invalid or revoked key — complete response:

```json
{
  "error": {
    "code": "INVALID_API_KEY",
    "message": "The provided API key is not valid."
  }
}
```

### GET /health

**Health check** · operationId `getHealth`

Returns the current health status of the OpenStock API.

Response `200` — Service is healthy

- `ok` (boolean, required) — Whether the service is healthy
- `environment` (string, required) — Deployment environment name

## Reliability and scale

The design point is an assistant with a billion monthly users. Nothing in the read path has a capacity we hand-provision, and every layer is one that already runs at that scale for someone else.

- **Compute.** Every request is served by Cloudflare Workers from the nearest of 300+ cities — the same network that fronts chatgpt.com. There are no servers to provision or scale; capacity follows demand. TLS termination, DDoS absorption and the WAF are Cloudflare’s.
- **Merchant catalog and API keys.** Cloudflare D1, SQLite replicated to the edge and read through the replica nearest the request.
- **Merchant records, codes and cashback.** Amazon DynamoDB with auto-scaling capacity and point-in-time recovery. Today’s ceiling is 24,000 reads per second before a limit is raised, which takes minutes, not a project.
- **Delivery estimates and shopper history.** Vertex AI Feature Store online serving, backed by Bigtable, sized for point lookups at high concurrency.
- **Generated codes.** Created at the store through Checkmate’s checkout integration. This is the one path with a real ceiling, and it is per merchant, not per partner: a store’s own platform limits how fast codes can be created there, so a generated code takes a few seconds. `idempotency_key` is what keeps it cheap — a retry or a repeated question returns the same code rather than minting another.
- **Batching.** One request covers a whole cart (50 merchants for search, 20 for codes and history) and the queries inside it run in parallel, so request volume scales with shopping sessions, not with items.
- **Edge cache.** Merchant resolution and merchant facts are cached in the Cloudflare data centre that served the request: name searches for 15 minutes, domain lookups for 6 hours, merchant profiles (name, domains, delivery statistics) for 1 hour. A merchant one shopper asked about is warm for the next shopper in that city, and a batch where nine of ten merchants are warm costs one fetch for the tenth. Codes and cashback are never cached; they are per request. The `X-OpenStock-Cache` response header on search says `hit`, `partial` or `miss`.
- **Observed under load.** From a single US East client holding 100 concurrent connections, 4,000 requests per endpoint, zero failures: codes and cashback p50 339 ms, p95 584 ms; shopper history p50 536 ms, p95 774 ms; merchant search p50 300 ms, p95 436 ms at 313 requests per second (p50 103 ms, p95 172 ms at 25 concurrent). The same search run before the edge cache measured p50 1.0 s, p95 1.4 s at 66 requests per second. At 300 concurrent connections, 9,000 requests, still zero failures.
- **Operations.** Every request is logged and traced and every response carries `X-Request-Id`. https://status.openstock.sh checks every endpoint with a real request every five minutes and opens an incident on the first failure; https://status.openstock.sh/status.json is the same as JSON. `/v1` is a stable contract: fields are added, never removed or renamed.

What we ask of a partner at that scale is what we ask at any scale: batch, send idempotency keys, and tell us your expected peaks so we can watch them land and agree a rate limit that reflects them.

## Links

- Interactive reference: https://api.openstock.sh/docs
- OpenAPI: https://api.openstock.sh/openapi.json
- Text guide: https://api.openstock.sh/llms-full.txt
- Live demo: https://openstock.sh/demo
