Developer resources

Two ways in: the drop-in widget, or the same REST API the widget uses. Everything below is served today at https://chatbot.trooply.ai.

1. What you receive on approval

2. The widget

Add two lines to any page. The element mounts a chat panel in the corner; the module is served from this domain and is updated by us.

<commerce-ai-chat
  store-public-key="store_pub_YOUR_KEY"
  api-base-url="https://chatbot.trooply.ai"
  locale="en-IN"></commerce-ai-chat>
<script type="module" src="https://chatbot.trooply.ai/widget/index.js"></script>

The widget renders product cards, citations, confirmation previews and hand-off notices from the answer contract below; it holds no secret.

3. The REST API

JSON over HTTPS. Every response carries a correlation_id; errors are problem details with a stable code such as RATE_LIMITED or VALIDATION_FAILED, and rate-limited responses say when to retry.

Start a session

POST /v1/sessions/anonymous
{ "store_public_key": "store_pub_YOUR_KEY", "locale": "en-IN" }

→ 200 { "session_token": "…", "expires_in": 3600, "role": "anonymous" }

Send the token as Authorization: Bearer … on every call that follows. Minting is limited per client address. Signed-in customer sessions (/v1/sessions/exchange, for order and return questions) are set up with you at onboarding, because they need an assertion your backend signs.

Open a conversation

POST /v1/chat/sessions
{ "locale": "en-IN" }

→ 201 { "thread_id": "thr_…" }

Send a message

POST /v1/chat/messages
{ "thread_id": "thr_…", "text": "do you have black running shoes under 4000" }

→ 200 {
  "outcome": "answer",
  "answer": {
    "answer_status": "answered",
    "response_markdown": "We found Men's Black Running Shoes for you. …",
    "citations": [ { "handle": "ev2", "concept_id": "…", "concept_version": 3, "title": "…" } ],
    "products":  [ { "product_id": "prd_…", "title": "…", "price_state": "live", "price": { … } } ],
    "clarification_question": null,
    "conflicts": [], "limitations": [], "next_actions": [],
    "handoff": null, "live_facts": [], "freshness": { … }, "versions": { … }
  },
  "events": [ … ]
}
outcomeMeaningShow the customer
answerGrounded reply; may carry citations and product cardsresponse_markdown, citations, products
clarificationThe request was ambiguousclarification_question
confirmation_requiredAn action is proposed and waits for consentThe preview, with Confirm / Cancel
handoffEvidence ran out or two policies disagree; a person takes overresponse_markdown and your support contact
deniedThe session may not do thatresponse_markdown
temporary_failureA dependency was unavailable; retry laterA retry notice

Prices and stock in products are the platform's live reads, never the model's text. price_state is live, unavailable or not_requested; render the number only when it is live.

Stream instead

POST /v1/chat/messages/stream        (same body; response is text/event-stream)

Events arrive in sequence as the turn progresses and end with message.completed, whose payload is the same answer object. GET /v1/chat/sessions/{thread_id} returns the transcript and GET /v1/chat/sessions/{thread_id}/events replays events for reconnects.

Confirm or cancel an action

POST /v1/chat/actions/confirm     { "thread_id": "thr_…", "intent_id": "int_…" }
POST /v1/chat/actions/cancel      { "thread_id": "thr_…", "intent_id": "int_…" }

A confirmation is single-use, bound to the session, and carries the arguments that were previewed. It expires; a stale one is refused with CONFIRMATION_INVALID and the customer is shown a fresh preview.

Search the catalogue directly

GET /v1/catalog/search?q=running+shoes&color=black&price_max_minor=400000&limit=10

→ 200 {
  "products": [ { "product_id": "prd_…", "title": "…",
                  "price_state": "live", "price": { "amount_minor": 359900, "currency": "INR", … },
                  "availability_state": "live", "availability": { "state": "in_stock", … } } ],
  "price_filter": { "min_minor": null, "max_minor": 400000, "currency": "INR",
                    "checked": 12, "excluded_outside_range": 3, "excluded_without_live_price": 1 },
  "warnings": []
}

Same session token. The filters category_id, color and size may repeat, and sku matches one SKU. Results come from your published catalogue only. Price and availability are read live for the first ten products. warnings carries price_and_availability_not_verified_live when any returned product was not read live.

price_min_minor and price_max_minor are in minor units (₹4,000 is 400000). They are judged only on prices read live, in your store's currency. A product whose price could not be read is left out and counted; it is never let through on an indexed price. price_filter is present only when a limit was given, and says how many close matches were checked and why any were left out.

End a session

POST /v1/sessions/revoke          { "reason": "logout" }   → 204

Connect a Shopify store

POST /v1/admin/connections/storefront
{ "store_url": "yourstore.com", "private_token": "…", "reason": "connecting our store" }

POST /v1/admin/connections/storefront/token
{ "connection_id": "conn_…", "private_token": "…", "reason": "large catalogue" }

The dashboard's Setup page makes these calls. They take a dashboard session, not the widget's, and the connector:write:store permission (operations connection.create and connection.credential).

private_token is optional when connecting. It is a Storefront API private token you create in Shopify's Headless channel, recommended for large catalogues. The second call switches a store that is already connected to reading with a token. Either way, the token is checked against your store before anything is kept, stored encrypted, and never echoed in a response or recorded in the audit trail.

4. Limits

Your plan sets turns per minute and concurrent turns per tenant; every session is also bounded on its own. A refused request returns 429 with RATE_LIMITED, the window it hit and a retry time. Bursts are refused, never queued.

5. Test your integration

Before you have a key of your own, the demonstration store answers with store_pub_tenant_a_local:

curl -s https://chatbot.trooply.ai/v1/sessions/anonymous \
  -H 'content-type: application/json' \
  -d '{"store_public_key":"store_pub_tenant_a_local"}'

Then open a conversation and ask “what is your return policy”. You should get outcome: answer with citations. That is exactly what your own store will do once its policies are published in your dashboard.