> ## Documentation Index
> Fetch the complete documentation index at: https://docs.watx.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a custom store connection with an AI assistant

> A ready-made prompt for Claude Code, Cursor, Copilot or Codex that builds your store's Watx connection — sending events and answering Watx's requests — and tells the assistant how to check its own work.

Most of the work in connecting a [custom store](/integrations/custom-store) is mapping your
own order, cart and product models onto Watx's format. An AI coding assistant working in
your store's codebase does that well if it is given the whole contract at once. This page
gives you that prompt.

## Use it

<Steps>
  <Step title="Connect the store in Watx">
    **Integrations → Custom store → Connect**. Keep the **Signing secret** from the **Send
    events** step somewhere safe.
  </Step>

  <Step title="Copy the prompt">
    On the same step, **Building it with an AI assistant? → Copy prompt** copies the prompt
    below with your own webhook URL already filled in. Or copy it from this page and replace
    `<the webhook URL shown in Watx>` yourself.
  </Step>

  <Step title="Paste it into your assistant">
    Open Claude Code, Cursor, GitHub Copilot, Codex or any other coding assistant **in your
    store's codebase**, so it can find your models and routes, and paste the prompt.
  </Step>

  <Step title="Set the secret yourself">
    Put the signing secret in your server's environment as `WATX_SECRET`. The prompt never
    contains it, and you should not paste it into the assistant.
  </Step>

  <Step title="Check the result">
    Run the `ping` command the assistant wrote: it should print `pong`, and the store
    switches to **Connected** in Watx. Place a test order and look for an outcome that
    starts with `order placed` and has no ` — check:` warnings.
  </Step>

  <Step title="Add the callback URL">
    The assistant tells you the path of the route it added. Enter its full https address
    under **Connection settings → Callback URL** and press **Send a test**.
  </Step>
</Steps>

<Warning>
  Never paste the signing secret into an AI assistant, a chat or a prompt. Prompts end up in
  other companies' logs and in shared chat histories, and anyone holding the secret can put
  orders into your workspace. If it has been pasted somewhere, rotate it under **Connection
  settings → Rotate secret**.
</Warning>

## The prompt

```text theme={null}
# Task: connect this store to Watx

Watx is the WhatsApp CRM this business uses. Connect this codebase's store to it so that:
1. Watx receives the store's orders, carts and products as they change. Watx then messages customers on WhatsApp.
2. Watx can ask the store to create a discount code, and to cancel or tag a cash-on-delivery order.

Use this codebase's own language, framework and patterns for configuration, background jobs, routing and tests. Ask me before adding a new dependency.

## Reference
- Guide: https://docs.watx.in/integrations/custom-store
- JSON Schema for every payload: https://api.watx.in/webhooks/store/schema.json
If you can fetch URLs, read both first. Everything you need is also below.

## Configuration
Read these from environment variables. Never hard-code them, never log the secret, never send it to a browser:
- WATX_WEBHOOK_URL = <the webhook URL shown in Watx>
- WATX_SECRET = the signing secret (starts with whsec_). I will set it myself.

## Part 1: send events to Watx
Write one server-side function, e.g. sendToWatx(event, data):
1. Serialise the body once: {"event": <event>, "data": <data>}.
2. Sign those exact bytes: HMAC-SHA256 with WATX_SECRET, hex digest. Send header X-Watx-Signature: sha256=<hex> and Content-Type: application/json. Never re-serialise after signing.
3. POST it to WATX_WEBHOOK_URL with a 10-second timeout.
4. Watx always answers HTTP 200 with {"received": true, "outcome": "..."}. Log the outcome. These outcomes are bugs to fix: "bad signature", "unknown event ...", "not a Watx envelope ...", and anything containing " — check: " (Watx lists the fields it could not use).
5. Never slow down or fail a checkout: send after the database transaction commits, from a background job or queue if the codebase has one. Retry network errors and 5xx responses up to 3 times with backoff. Sending the same event twice is safe; Watx de-duplicates.

Call it where these things happen:
| When | event | data |
| --- | --- | --- |
| An order is placed | order.created | Order |
| Payment is captured | order.paid | Order |
| The order ships | order.fulfilled | Order |
| The order is cancelled | order.cancelled | Order |
| The order is refunded | order.refunded | Order |
| Any other change to an order | order.updated | Order |
| A cart is created or changes, once a phone or email is known | checkout.updated | Checkout |
| A cart is emptied or deleted | checkout.deleted | {"id"} |
| A product is created or changes | product.updated | Product |
| A product is deleted | product.deleted | {"id"} |
Always send the whole current object, not only what changed.

### Data shapes
Field names are exact. Amounts are MAJOR units as decimal strings ("1499.00"). Ids are strings. Phones are international format ("+919876543210"). Times are ISO 8601 with an offset. Leave out a field you do not have rather than sending an empty string. * = required.

Order:
- id* string: your order id, the same on every event
- number string: what the customer sees, e.g. "#1042"
- created_at datetime; cancelled_at datetime (order.cancelled only)
- currency string: ISO 4217, e.g. "INR"
- total* money: the amount charged
- subtotal, discount, shipping, tax: money
- taxes_included boolean
- payment_method string: "cod" for cash on delivery, otherwise the gateway name
- payment_status string: pending | paid | partially_paid | refunded | partially_refunded | voided
- fulfillment_status string: unfulfilled | partial | fulfilled
- status_url url: the customer's order page
- customer object: name, first_name, last_name, phone, email. Without a phone nobody can be messaged.
- shipping_address object: line1*, line2, city, state, postal_code, country_code (2 letters, e.g. "IN")
- items array of {title*, quantity (integer), price (money, the UNIT price)}
- shipping_method string
- checkout_id string: the id of the cart this order came from
- discount_codes array of strings: codes used on the order

Checkout: id* string; currency; total* money; items (as above); recovery_url url: the link that reopens THIS cart, which the reminder is built around; customer {name, phone, email, accepts_marketing boolean}; created_at, updated_at datetime.

Product: id* string; title* string; description string; price* money; image_url url; url url (the product page); inventory integer or null (null = not tracked); active boolean (false removes it from Watx).

Also write:
- a command that sends "ping" with data {} and prints the outcome, which should be "pong";
- a one-off script that sends product.updated for every existing product, to fill the catalogue.

## Part 2: receive requests from Watx
Add a public HTTPS route, e.g. POST /watx/callback:
1. Read the RAW request body before any JSON parsing.
2. Verify X-Watx-Signature: HMAC-SHA256 of the raw body with WATX_SECRET, hex, optional "sha256=" prefix, constant-time comparison. Answer 401 when it does not match.
3. Parse {"id", "event", "created_at", "data"}. Ignore an id you have already handled.
4. Handle:
   - ping: nothing to do.
   - coupon.create: create a single-use discount code that is EXACTLY data.code (match it case-insensitively at checkout), data.percent_off percent off everything, valid from data.starts_at until data.expires_at, usable once (data.usage_limit), and only on orders of at least data.minimum_order when that is not null. Watx has already sent this code to the customer, so it must work. If the code already exists, treat it as done.
   - order.cancel: cancel order data.order_id. It is cash on delivery: restock, nothing to refund.
   - order.tag: add data.tags to order data.order_id (a note is fine if orders have no tags).
5. Answer 2xx when done, within 10 seconds. On failure answer 4xx or 5xx with JSON {"error": "<a sentence a shop owner can read>"}; Watx shows that sentence to the merchant.

## Tests
- The signature of a fixed body and secret equals the known HMAC-SHA256 hex digest.
- The order, cart and product mappers produce the shapes above (validate against the JSON Schema if the codebase has a validator).
- The callback route answers 401 to a wrong signature, and creates the code for a correctly signed coupon.create.

## When you are done, tell me
1. Where sendToWatx is called from, event by event.
2. The callback route's path, so I can enter its full https URL in Watx (Integrations → Custom store → Connection settings → Callback URL).
3. Which environment variables to set.

Done means: the ping prints "pong" and the store shows Connected in Watx; a test order returns an outcome starting "order placed" with no " — check: "; and Watx's "Send a test" on the Callback URL step succeeds.
```

## Why it is written this way

* **The whole format is inline.** An assistant that cannot browse still has every field.
  One that can is pointed at the [JSON Schema](/integrations/custom-store#the-json-schema)
  for exact types.
* **It has a definition of done.** Watx's reply to every event names the fields it could
  not use (` — check: …`), so the assistant can send a test event and fix its own mapping
  until the warnings are gone.
* **It keeps your checkout safe.** Events are sent after the order is saved, in the
  background, with retries — a slow or unreachable Watx never fails a customer's checkout.
* **It handles Watx's requests correctly.** It verifies the signature on the raw body,
  creates a discount code exactly as Watx sent it, and answers with a sentence Watx can show
  you when something goes wrong.

Review what the assistant writes as you would any other change to your checkout. The full
contract is on the [Custom store](/integrations/custom-store) page.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.