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
| Field | What it is |
|---|---|
claim_text | Your own wording of the claim (≤500 chars). Stored for you; never shown to customers or sent to the review chat. |
topic | A 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_phrasings | Up to 5 example questions (≤240 chars each) the chat may adapt. Never used verbatim. |
source | merchant or claude — who created the claim. |
status | unverified (default) or supported. You set it; BetterReviews does not verify claims. |
collect_enabled | When 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
} | Field | Type | Required | Description |
|---|---|---|---|
product_id | string | Yes | Shopify product ID — bare numeric, shopify-{id}, or gid://shopify/Product/{id} |
claim_text | string | Yes | ≤500 chars |
topic | string | Yes | ≤200 chars, prompt-text rules apply |
ask_phrasings | string[] | No | ≤5 entries, ≤240 chars each, prompt-text rules apply |
source | string | Yes | merchant or claude |
status | string | No | unverified (default) or supported |
collect_enabled | boolean | No | Default 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 catalog422 invalid_prompt_text—topic/ask_phrasingsfailed the prompt-text rules (details inerrors)422 another_claim_active—collect_enabled: truebut another claim already collects for this product422 validation_error— other field errors (details inerrors)
List Claims
GET /api/v1/claims
| Param | Description |
|---|---|
product_id | Shopify product ID — bare numeric, shopify-{id}, or gid://shopify/Product/{id} |
status | unverified or supported |
collect_enabled | true / false |
include_archived | true to include archived claims (hidden by default) |
limit | Page size (default 50, max 200) |
offset | Pagination 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}
]
} | Field | Type | Required | Description |
|---|---|---|---|
review_id | integer | Yes | A review in this store |
evidence_quote | string | No | The sentence that supports the claim (≤500 chars) |
confidence | string | No | low, 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).