Claims API

Last updated: September 13, 2026

A claim is a marketing statement you want your review corpus to support ("keeps coffee hot for 6 hours"). Claims let an AI assistant do the analysis — does the corpus support claim X? — while BetterReviews keeps the durable state: which reviews support which claim, with the quoted sentence, and a collect_enabled flag that reserves the product's collection slot for the AI review chat.

Entitlement gating — every claims endpoint requires the claim_collection entitlement. Without it, calls return 403 entitlement_required. Current plans (Unlimited, Studio) include it. On legacy plans (no longer sold), it is available on Growth and above, and on legacy Pro and Enterprise; stores on the legacy Starter plan do not have it. A store with no active subscription — never subscribed, or cancelled — is in the free state and holds none of the gated entitlements.

Rate limit — writes (POST, PATCH, DELETE) are limited to 30 per minute per store.

Concepts

FieldWhat it is
claim_textYour own wording of the claim (≤500 chars). Stored for you; never shown to customers or sent to the review chat.
topicA short, neutral description of what you want customers to talk about (≤200 chars). This is what the review chat may use, in its own words.
ask_phrasingsUp to 5 example questions (≤240 chars each) the chat may adapt. Never used verbatim.
sourcemerchant or claude — who created the claim.
statusunverified (default) or supported. You set it; BetterReviews does not verify claims.
collect_enabledWhen true, reserves the collection slot for this product: the AI review chat may ask one short question about the topic, once per conversation, when the customer is happy and has not already covered it. At most one claim per product can be collect-enabled at a time. Chat collection is rolling out — until it ships, the flag records your intent and stats.asks / links.conversation stay at 0.

Prompt-text rules. topic and every ask_phrasings entry are validated like other merchant text that can reach the chat: no HTML or angle brackets, no triple backticks, no line-leading #, no "ignore previous instructions"-style phrasing, no email addresses or phone numbers. Violations return 422 invalid_prompt_text. claim_text and evidence_quote are only stripped of HTML and length-capped.


Create Claim

POST /api/v1/claims

{
  "product_id": "shopify-7125386199075",
  "claim_text": "Keeps coffee hot for 6 hours",
  "topic": "how long drinks stay hot",
  "ask_phrasings": ["How long did your drink stay warm?"],
  "source": "claude",
  "collect_enabled": false
}
FieldTypeRequiredDescription
product_idstringYesShopify product ID — bare numeric, shopify-{id}, or gid://shopify/Product/{id}
claim_textstringYes≤500 chars
topicstringYes≤200 chars, prompt-text rules apply
ask_phrasingsstring[]No≤5 entries, ≤240 chars each, prompt-text rules apply
sourcestringYesmerchant or claude
statusstringNounverified (default) or supported
collect_enabledbooleanNoDefault false

Response (201)

{
  "data": {
    "id": 12,
    "product_id": "7125386199075",
    "product_title": "Insulated Mug",
    "claim_text": "Keeps coffee hot for 6 hours",
    "topic": "how long drinks stay hot",
    "ask_phrasings": ["How long did your drink stay warm?"],
    "source": "claude",
    "status": "unverified",
    "collect_enabled": false,
    "created_by": "api_key:ppo_abcd:123",
    "updated_by": null,
    "archived_at": null,
    "inserted_at": "2026-09-13T10:00:00Z",
    "updated_at": "2026-09-13T10:00:00Z"
  }
}

product_id in responses is the bare Shopify product id; product_title is the synced catalog title.

Errors

  • 422 product_not_found — the product is not in this store's catalog
  • 422 invalid_prompt_texttopic / ask_phrasings failed the prompt-text rules (details in errors)
  • 422 another_claim_activecollect_enabled: true but another claim already collects for this product
  • 422 validation_error — other field errors (details in errors)

List Claims

GET /api/v1/claims

ParamDescription
product_idShopify product ID — bare numeric, shopify-{id}, or gid://shopify/Product/{id}
statusunverified or supported
collect_enabledtrue / false
include_archivedtrue to include archived claims (hidden by default)
limitPage size (default 50, max 200)
offsetPagination offset

An unrecognised status or a product_id that matches no product returns an empty list, not an error.

{
  "data": [ { "id": 12, "...": "..." } ],
  "total": 1,
  "limit": 50,
  "offset": 0,
  "has_more": false
}

Get Claim

GET /api/v1/claims/:id

Returns the claim plus stats and its linked reviews. Returns 404 not_found for a claim that belongs to another store.

{
  "data": {
    "id": 12,
    "...": "...",
    "stats": {
      "asks": 4,
      "links": {"claude": 3, "conversation": 1},
      "links_total": 4
    },
    "reviews": [
      {
        "review_id": 5510,
        "rating": 5,
        "title": "Still hot at lunch",
        "excerpt": "Filled it at 7am and it was still hot at noon…",
        "status": "approved",
        "rights_status": "none",
        "source": "claude",
        "evidence_quote": "still hot at noon",
        "confidence": "high",
        "linked_at": "2026-09-13T10:05:00Z"
      }
    ]
  }
}
  • stats.asks — conversations in which the chat asked about this topic (once chat collection ships).
  • links.claude — reviews you linked through the API; links.conversation — reviews submitted by a conversation that was asked about the topic (will be linked automatically, without a quote, once chat collection ships).
  • rights_status (none / requested / cleared) tells you whether you already hold permission to quote the review in marketing.

Update Claim

PATCH /api/v1/claims/:id

Any of claim_text, topic, ask_phrasings, status, collect_enabled. Same validation as create.

Errors: 404 not_found, 422 claim_archived, 422 another_claim_active, 422 invalid_prompt_text, 422 validation_error.

To start collecting for a claim:

curl -X PATCH https://api.betterreviews.app/api/v1/claims/12 \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"collect_enabled": true}'

Archive Claim

DELETE /api/v1/claims/:id

Archives the claim (sets archived_at, turns collect_enabled off). Linked reviews are kept; the claim disappears from the default list.


Link Reviews to a Claim

POST /api/v1/claims/:id/reviews

{
  "reviews": [
    {"review_id": 5510, "evidence_quote": "still hot at noon", "confidence": "high"},
    {"review_id": 5511}
  ]
}
FieldTypeRequiredDescription
review_idintegerYesA review in this store
evidence_quotestringNoThe sentence that supports the claim (≤500 chars)
confidencestringNolow, medium, high

Up to 50 reviews per call. Re-linking a review updates its quote/confidence. One unknown or other-store review fails the whole batch.

Response (201): the full reviews list for the claim (same shape as Get Claim).

Errors: 404 not_found, 422 review_not_found (with review_id), 422 too_many_reviews, 422 invalid_reviews, 422 validation_error.

List Linked Reviews

GET /api/v1/claims/:id/reviews — the reviews array from Get Claim. 404 not_found for a claim that belongs to another store.

Unlink a Review

DELETE /api/v1/claims/:id/reviews/:review_id

Response (200): {"data": {"removed": true}}. 404 not_found when the link (or claim) does not exist.


Workflow: find and fill a claim gap

# 1. Search the corpus for evidence
curl -s -X POST https://api.betterreviews.app/api/v1/reviews/search \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"query": "stays hot for hours", "product_id": "7125386199075"}'

# 2. Record the claim and link what you found
CLAIM=$(curl -s -X POST https://api.betterreviews.app/api/v1/claims \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"product_id":"7125386199075","claim_text":"Keeps coffee hot for 6 hours","topic":"how long drinks stay hot","source":"claude"}' \
  | jq -r '.data.id')

curl -s -X POST "https://api.betterreviews.app/api/v1/claims/$CLAIM/reviews" \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"reviews":[{"review_id":5510,"evidence_quote":"still hot at noon","confidence":"high"}]}'

# 3. Not enough evidence? Reserve the topic for chat collection
curl -s -X PATCH "https://api.betterreviews.app/api/v1/claims/$CLAIM" \
  -H "X-API-Key: YOUR_API_KEY" -H "Content-Type: application/json" \
  -d '{"collect_enabled": true}'

Once a claim has enough support, request the reviewer's permission before quoting a review in ads (see rights_status).