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

# Pricepally ecommerce agent

> Build a grocery storefront assistant, product search, add to cart, and order status tracking with the Pricepally API and React Native demo

This recipe walks end-to-end:

1. Run the **pricepally-api** reference backend
2. Create the agent in the dashboard
3. Ground answers with a **knowledge base** (delivery, payments, seasons)
4. Wire a **Skill** for **search products**, **add to cart**, and **track order status**
5. Deploy the **Mobile app** profile and embed in **React Native**

<Note>
  The reference backend lives at `pricepally-api/` in the Chatropic repo (port **8084**). The reference mobile shell is `examples/react-native-demo/`. Swap in your own API base URL, the dashboard action setup is the same.
</Note>

## What you are building

```mermaid theme={null}
flowchart LR
    User[Signed-in shopper] --> RNApp[React Native app]
    RNApp -->|JWT login| API[pricepally-api :8084]
    RNApp -->|POST /api/agent/session| SessionStore[Agent session store]
    RNApp --> Widget[Chatropic mobile widget]
    Widget -->|endUserId| Agent[Chatropic agent]
    Agent --> KB[Knowledge base]
    Agent --> Actions[HTTP actions]
    Actions -->|BFF token + X-End-User-Id| BFF["/api/agent/* BFF"]
    BFF --> SessionStore
    BFF --> API
    API --> DB[(Postgres)]
```

| User intent                     | Agent behavior                           |
| ------------------------------- | ---------------------------------------- |
| "Do you have ginger?"           | Calls `search_products` with `q=ginger`  |
| "Add onions to my cart"         | Resolves product id, calls `add_to_cart` |
| "Where is order 00A49291044D1?" | Calls `get_order_status` with `order_id` |
| Delivery / payment FAQ          | Answers from **Documents** (no action)   |

***

## API reference (pricepally-api)

All protected routes require `Authorization: Bearer <jwt>` from `POST /api/auth/login`.

| Capability                 | Method | Endpoint                            | Notes                                                                                         |
| -------------------------- | ------ | ----------------------------------- | --------------------------------------------------------------------------------------------- |
| Login                      | POST   | `/api/auth/login`                   | Returns JWT + user                                                                            |
| **Search / list products** | GET    | `/api/products?q={{query}}`         | Optional `q` filters by product name (case-insensitive). Omit `q` to return the full catalog. |
| **Track order status**     | GET    | `/api/orders?order_id={{order_id}}` | Optional `order_id` filters to one order. Omit to list all orders for the user.               |
| **Add to cart**            | POST   | `/api/cart/items`                   | JSON body. Use `Accept: application/json` for Chatropic actions. Mobile app uses SSE.         |
| Live cart (mobile)         | GET    | `/api/cart/stream`                  | SSE, used by the RN cart tab, not typical for HTTP actions                                    |

### Agent BFF routes (signed-in users)

When each shopper has their own JWT, point Chatropic actions at **`/api/agent/*`** instead of calling protected routes with a shared token. The BFF validates a **static service token**, reads **`X-End-User-Id`**, looks up that user's Pricepally JWT from the session store, and proxies the request.

| Capability       | Method | BFF endpoint                                                               |
| ---------------- | ------ | -------------------------------------------------------------------------- |
| Search products  | GET    | `/api/agent/products?q={{query}}`                                          |
| Add to cart      | POST   | `/api/agent/cart/items`                                                    |
| Order status     | GET    | `/api/agent/orders?order_id={{order_id}}`                                  |
| Register session | POST   | `/api/agent/session`: called by your app after login (`{ userId, token }`) |
| Clear session    | DELETE | `/api/agent/session`: called on logout (`{ userId }`)                      |

BFF headers (on every agent action):

```
Authorization: Bearer {{token.pricepally_bff}}
X-End-User-Id: {{end_user_id}}
```

<Warning>
  There is **no** separate `/api/products/search` route. Product search is implemented as **`GET /api/products` with the `q` query parameter**. Order lookup uses **`GET /api/orders` with `order_id`**: there is no `GET /api/orders/:id` path segment.
</Warning>

***

## Prerequisites

<Steps>
  <Step title="Start Postgres">
    From the Chatropic repo root:

    ```bash theme={null}
    docker compose up -d postgres
    ```
  </Step>

  <Step title="Run pricepally-api">
    ```bash theme={null}
    cd pricepally-api
    cp .env.example .env
    go mod tidy
    go run ./cmd/server
    ```

    Verify: `curl http://localhost:8084/health`, `{"status":"ok"}`

    On first boot the API runs migrations and seeds from `seed/pricepally_seed.json`.
  </Step>

  <Step title="Demo credentials">
    | Field    | Value                                    |
    | -------- | ---------------------------------------- |
    | Email    | `ada@pricepally.demo`                    |
    | Password | `Demo1234!`                              |
    | Catalog  | Ugu, Onions, Ginger, Okro (Naira prices) |
    | Orders   | 4 seeded orders (Delivered / Processing) |
  </Step>

  <Step title="Obtain a JWT for action testing">
    ```bash theme={null}
    curl -s -X POST http://localhost:8084/api/auth/login \
      -H "Content-Type: application/json" \
      -d '{"email":"ada@pricepally.demo","password":"Demo1234!"}'
    ```

    Copy `token` from the response, use it when testing actions in the dashboard.
  </Step>

  <Step title="Chatropic dashboard">
    Open the playground dashboard (default `http://localhost:3000`) and sign in to your workspace.
  </Step>
</Steps>

***

## Step 1, Create the agent

<Steps>
  <Step title="Create workspace">
    Sign in and open (or create) a workspace for your grocery product.
  </Step>

  <Step title="Set product type">
    Use **Customer Support** so the agent can combine knowledge retrieval with HTTP actions.
  </Step>

  <Step title="Open Playground">
    In **Playground**, set the display name (for example **Pricepally Assistant**), welcome message, and suggested prompts (see [Step 6](#step-6--suggested-prompts)).
  </Step>
</Steps>

***

## Step 2, Knowledge base setup

Ground delivery, payment, and policy answers so the agent does not invent storefront rules.

<Steps>
  <Step title="Add FAQ documents">
    **Documents**: paste or upload content such as:

    ```markdown theme={null}
    ## Delivery
    Orders are delivered to Lagos and Abuja metro areas. Processing orders ship within 2 business days.

    ## Payments
    We accept card payments and bank transfer via Paystack at checkout.

    ## Seasonal produce
    Items marked "In Season" are freshest this week. "Off Season" items may have limited stock or higher prices.
    ```
  </Step>

  <Step title="Smoke test retrieval">
    **New test session**, ask *"What payment methods do you accept?"*, answer should cite your document.
  </Step>
</Steps>

***

## Step 3, Authentication for actions

`pricepally-api` protects catalog, cart, and order routes with JWT. Pick one pattern:

| Pattern                  | Best for                                                          |
| ------------------------ | ----------------------------------------------------------------- |
| **Demo static JWT**      | Playground only, one shared demo user                             |
| **Agent BFF**            | Signed-in mobile or web app, **recommended for production demos** |
| **Customer login token** | OAuth-style sign-in inside chat (no app session store)            |

<Tabs>
  <Tab title="Demo (static JWT)">
    <Steps>
      <Step title="Login and copy token">
        ```bash theme={null}
        curl -s -X POST http://localhost:8084/api/auth/login \
          -H "Content-Type: application/json" \
          -d '{"email":"ada@pricepally.demo","password":"Demo1234!"}'
        ```
      </Step>

      <Step title="Save static token">
        **Manage tokens**: new **Static** token

        | Field | Value                        |
        | ----- | ---------------------------- |
        | Name  | `pricepally_demo_jwt`        |
        | Value | Paste the `token` from login |
      </Step>

      <Step title="Use in action headers">
        On each HTTP action:

        ```
        Authorization: Bearer {{token.pricepally_demo_jwt}}
        ```

        Point actions at **`/api/products`**, **`/api/cart/items`**, **`/api/orders`** (not `/api/agent/*`).
      </Step>
    </Steps>
  </Tab>

  <Tab title="Agent BFF (signed-in app)">
    Recommended for **`examples/react-native-demo`** and any app where users sign in before chatting. The app stores each user's Pricepally JWT server-side; Chatropic calls the BFF with a **static service token** plus **`{{end_user_id}}`**.

    <Steps>
      <Step title="Set the BFF secret on pricepally-api">
        In `pricepally-api/.env`:

        ```bash theme={null}
        CHATROPIC_AGENT_BFF_TOKEN=pricepally-demo-bff-secret
        ```

        Restart `go run ./cmd/server` after changing env.
      </Step>

      <Step title="Create the static BFF token in Chatropic">
        **Manage tokens**: new **Static** token

        | Field  | Value                                                              |
        | ------ | ------------------------------------------------------------------ |
        | Name   | `pricepally_bff`                                                   |
        | Secret | Same value as `CHATROPIC_AGENT_BFF_TOKEN` in `pricepally-api/.env` |
      </Step>

      <Step title="Point actions at the BFF">
        HTTP actions call **`/api/agent/*`**, not `/api/products` directly. Use the same headers on every action:

        ```
        Authorization: Bearer {{token.pricepally_bff}}
        X-End-User-Id: {{end_user_id}}
        ```

        | Action             | BFF URL (local)                                                |
        | ------------------ | -------------------------------------------------------------- |
        | `search_products`  | `http://localhost:8084/api/agent/products?q={{query}}`         |
        | `add_to_cart`      | `http://localhost:8084/api/agent/cart/items`                   |
        | `get_order_status` | `http://localhost:8084/api/agent/orders?order_id={{order_id}}` |

        On a physical device, replace `localhost` with your machine's LAN IP (same as `EXPO_PUBLIC_PRICEPALLY_API_URL`).
      </Step>

      <Step title="Register the session after login">
        When the user signs in to your app, POST their Pricepally user id and JWT to the session route:

        ```ts theme={null}
        await fetch(`${PRICEPALLY_API_URL}/api/agent/session`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({
            userId: session.user.id,
            token: session.token,
          }),
        });
        ```

        The reference RN app does this in `lib/agent-session.ts`: called on **login**, **app restore**, and cleared on **logout**.
      </Step>

      <Step title="Pass endUserId to the mobile SDK">
        Mount the chat widget with the signed-in user's id so `{{end_user_id}}` resolves to the Pricepally user UUID (not the anonymous chat session id):

        ```tsx theme={null}
        <ChatWidgetLauncher
          tenantId={tenantId}
          agentUrl={agentUrl}
          endUserId={user.id}
          ...
        />
        ```

        See `examples/react-native-demo/App.tsx`.
      </Step>
    </Steps>

    <Note>
      The in-memory session store in `pricepally-api` is for local development only. Production should use Redis or your session database keyed by `userId`.
    </Note>
  </Tab>

  <Tab title="Customer login (OAuth)">
    Alternative when users sign in **inside chat** via OAuth instead of your app shell. Configure **Customer login** in **Manage tokens** so `Authorization: Bearer {{token.pricepally_session}}` maps to the shopper's JWT after they complete the OAuth flow.

    See the [fintech SaaS recipe, OAuth tab](/user-guides/recipes/fintech-saas#step-3--authentication-for-actions) for the same dashboard pattern. `pricepally-api` does not ship an OAuth server today, use the **Agent BFF** tab for the RN demo.
  </Tab>
</Tabs>

<Card title="Manage tokens" icon="key" href="/user-guides/actions/manage-tokens">
  Static vs customer-login tokens.
</Card>

***

## Step 4, Skill setup

Create **one skill per feature** under **Agent settings → Skills → New skill**. Each skill covers a single capability and auto-creates the linked HTTP action when saved.

### What is a Skill?

A Skill is a playbook that tells the agent *when* to activate and *how* to carry out one feature, including the API call to make. When you save a skill with a webhook step, Chatropic creates (or updates) the linked HTTP action automatically. See the [Skills guide](/user-guides/agent-management/skills) for the full editor reference.

Create the three skills below in sequence. For each one:

1. Open **Agent settings → Skills**, then **New skill**.
2. Paste the description prompt into the **Describe your skill** field.
3. Review the generated **When to use**, **Steps**, and webhook URL/headers. Edit inline if needed.
4. Click **Save skill**. The linked action appears under **Available actions**.

<Note>
  For the **Demo static JWT** pattern, replace `{{token.pricepally_bff}}` with `{{token.pricepally_demo_jwt}}` and use the direct API URLs (`/api/products`, `/api/cart/items`, `/api/orders`) instead of `/api/agent/*`.
</Note>

***

### 4a, Search products skill

**Prompt to paste:**

> When a shopper asks what produce is available, in season, or asks for the price of an item, search the Pricepally catalog. Call `GET http://localhost:8084/api/agent/products?q={{query}}` with headers `Authorization: Bearer {{token.pricepally_bff}}` and `X-End-User-Id: {{end_user_id}}`. Summarize results by name, price range in Naira, and season status. If no results, say so and suggest refining the search.

**Webhook the skill should generate:**

| Field       | Value                                                                              |
| ----------- | ---------------------------------------------------------------------------------- |
| **Method**  | GET                                                                                |
| **URL**     | `http://localhost:8084/api/agent/products?q={{query}}`                             |
| **Headers** | `Authorization: Bearer {{token.pricepally_bff}}`, `X-End-User-Id: {{end_user_id}}` |

**Sample response:**

```json theme={null}
{
  "items": [
    {
      "id": "3",
      "category": "",
      "name": "Ginger",
      "image": "https://www.pricepally.com/...",
      "price": "₦1,629 - ₦688,379"
    }
  ]
}
```

***

### 4b, Add to cart skill

**Prompt to paste:**

> When a shopper asks to add an item to their cart, add it using the Pricepally API. If the `product_id` is not already known, first search for the product to resolve it. Then call `POST http://localhost:8084/api/agent/cart/items` with headers `Authorization: Bearer {{token.pricepally_bff}}`, `X-End-User-Id: {{end_user_id}}`, `Content-Type: application/json`, and `Accept: application/json`. Send body `{"product_id":"{{product_id}}","quantity":{{quantity}}}`. Default quantity to 1 if not specified. Confirm the updated cart subtotal in Naira.

**Webhook the skill should generate:**

| Field       | Value                                                                                                                                            |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Method**  | POST                                                                                                                                             |
| **URL**     | `http://localhost:8084/api/agent/cart/items`                                                                                                     |
| **Headers** | `Authorization: Bearer {{token.pricepally_bff}}`, `X-End-User-Id: {{end_user_id}}`, `Content-Type: application/json`, `Accept: application/json` |
| **Body**    | `{"product_id":"{{product_id}}","quantity":{{quantity}}}`                                                                                        |

<Note>
  `Accept: application/json` is required. The mobile app uses `Accept: text/event-stream` for live cart sync; the agent action needs a normal JSON response.
</Note>

**Sample response:**

```json theme={null}
{
  "items": [
    {
      "id": "1",
      "name": "Onions - Red",
      "description": "Medium Bag (5kg)",
      "price": 4890,
      "quantity": 2,
      "imageUrl": "https://www.pricepally.com/..."
    }
  ],
  "subtotal": 12659
}
```

***

### 4c, Order status skill

**Prompt to paste:**

> When a shopper asks where their order is or provides an order reference, look up the delivery status. Call `GET http://localhost:8084/api/agent/orders?order_id={{order_id}}` with headers `Authorization: Bearer {{token.pricepally_bff}}` and `X-End-User-Id: {{end_user_id}}`. Report the status, order date, and main product. If the user asks to see all their orders without a reference, omit the `order_id` parameter.

**Webhook the skill should generate:**

| Field       | Value                                                                              |
| ----------- | ---------------------------------------------------------------------------------- |
| **Method**  | GET                                                                                |
| **URL**     | `http://localhost:8084/api/agent/orders?order_id={{order_id}}`                     |
| **Headers** | `Authorization: Bearer {{token.pricepally_bff}}`, `X-End-User-Id: {{end_user_id}}` |

**Sample response:**

```json theme={null}
{
  "orders": [
    {
      "id": "3",
      "orderId": "00A49291044D1",
      "status": "Delivered",
      "orderDate": "Monday, July 17, 2023",
      "orderTime": "9:14 am",
      "mainProduct": {
        "name": "Onions - Red",
        "description": "Medium Bag (5kg)",
        "price": 4890,
        "quantity": 1,
        "imageUrl": "https://www.pricepally.com/..."
      },
      "additionalItemsCount": 0
    }
  ]
}
```

***

### Validate auto-created actions

After saving all three skills, confirm each linked action is wired correctly:

1. Open **Available actions** → click each action (`search_products`, `add_to_cart`, `get_order_status`)
2. Verify the URL, method, and headers match the tables above
3. Use the action **Test** panel with sample parameters (`query: "ginger"`, `order_id: "00A49291044D1"`)
4. Use each action's **Test** panel to check resolved URLs and response bodies

<CardGroup cols={2}>
  <Card title="Skills" icon="wand-magic-sparkles" href="/user-guides/agent-management/skills">
    Create and manage skills under Agent settings.
  </Card>

  <Card title="Action chaining" icon="link" href="/user-guides/actions/action-chaining">
    Search → add to cart chain reference.
  </Card>
</CardGroup>

***

## Step 5, Agent instructions

In **AI** (or **Agent settings**), add system guidance:

```text theme={null}
You are the Pricepally shopping assistant. For product questions, call search_products.
To add items, resolve product_id first if needed, then call add_to_cart.
For order tracking, call get_order_status with the order reference the user provides.
Answer delivery and payment questions from the knowledge base. Prices are in Nigerian Naira (₦).
```

**Guardrails**: block requests for card numbers or passwords in chat; use [Escalate to human](/user-guides/actions/escalate-to-human) for delivery disputes.

***

## Step 6, Suggested prompts

**Suggested prompts** on the Content tab:

| Prompt                       |
| ---------------------------- |
| Do you have ginger?          |
| Add onions to my cart        |
| Track my order 00A49291044D1 |
| What's in season this week?  |
| Show my recent orders        |

***

## Step 7, Test in Playground

<Steps>
  <Step title="Product search">
    Ask *"Do you have ugu?"*, `search_products` with `q=ugu`, agent lists Ugu with price range.
  </Step>

  <Step title="Add to cart">
    Ask *"Add ginger to my cart"*, search then `add_to_cart` with `product_id: "3"`, confirms updated subtotal.
  </Step>

  <Step title="Order status">
    Ask *"Where is order 00B77120355E2?"*, `get_order_status`, explains **Processing** status and main item.
  </Step>

  <Step title="FAQ without action">
    Ask *"How do I pay?"*, answer from Documents, no HTTP call.
  </Step>
</Steps>

Check each action's **Test** panel for resolved URLs and response bodies, and your API's server logs for the incoming requests.

***

## Step 8, React Native embed

The repo includes a Pricepally-skinned demo that uses the same API for login, products, cart SSE, and orders.

<Steps>
  <Step title="Configure env">
    ```bash theme={null}
    cd examples/react-native-demo
    cp .env.example .env
    ```

    ```
    EXPO_PUBLIC_CHATROPIC_TENANT_ID=your-tenant-id
    EXPO_PUBLIC_AGENT_URL=http://127.0.0.1:8000
    EXPO_PUBLIC_PRICEPALLY_API_URL=http://127.0.0.1:8084
    ```
  </Step>

  <Step title="Run services">
    | Service          | Port |
    | ---------------- | ---- |
    | `customer-agent` | 8000 |
    | `pricepally-api` | 8084 |

    ```bash theme={null}
    npx expo start --ios
    ```
  </Step>

  <Step title="Deploy mobile profile">
    **Mobile app**, configure style and content, **Deploy**, toggle **Live**.
  </Step>

  <Step title="Sign in and chat">
    Use demo credentials `ada@pricepally.demo` / `Demo1234!` in the app shell, then open the chat launcher. Cart tab stays in sync via `GET /api/cart/stream` SSE.
  </Step>
</Steps>

<CardGroup cols={2}>
  <Card title="React Native deploy" icon="mobile-screen" href="/user-guides/deploy/react-native">
    Mobile app profile and tenant id.
  </Card>

  <Card title="React Native SDK" icon="code" href="/developer-guides/integration/react-native-sdk">
    `ChatWidgetLauncher`, storage, and identity props.
  </Card>
</CardGroup>

***

## Quick curl reference

```bash theme={null}
# Login
TOKEN=$(curl -s -X POST http://localhost:8084/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"ada@pricepally.demo","password":"Demo1234!"}' \
  | python3 -c 'import sys,json; print(json.load(sys.stdin)["token"])')

# Search products
curl -s "http://localhost:8084/api/products?q=ginger" \
  -H "Authorization: Bearer $TOKEN"

# Add to cart (JSON, for agents)
curl -s -X POST http://localhost:8084/api/cart/items \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{"product_id":"3","quantity":1}'

# Order status
curl -s "http://localhost:8084/api/orders?order_id=00A49291044D1" \
  -H "Authorization: Bearer $TOKEN"

# Agent BFF (after POST /api/agent/session)
BFF=pricepally-demo-bff-secret
curl -s "http://localhost:8084/api/agent/products?q=ginger" \
  -H "Authorization: Bearer $BFF" \
  -H "X-End-User-Id: <user-uuid-from-login>"
```

***

## Troubleshooting

| Symptom                                          | Fix                                                                                                                            |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------ |
| Agent says "not signed in" but user is logged in | Confirm `registerAgentSession` ran after login and `endUserId={user.id}` is passed to the SDK. Re-open chat after login.       |
| BFF returns "No active session for this user"    | `POST /api/agent/session` failed, check `pricepally-api` is running and `userId` matches the JWT `sub`.                        |
| BFF returns "Invalid agent service token"        | `pricepally_bff` token in Chatropic must match `CHATROPIC_AGENT_BFF_TOKEN` in `pricepally-api/.env`.                           |
| Actions hit wrong user                           | `{{end_user_id}}` must be the Pricepally user UUID, not the chat `demo-...` session id, pass `endUserId` from your auth state. |

***

## Related

* Backend README: `pricepally-api/README.md` in the Chatropic repo
* [Catalog & product FAQ](/user-guides/recipes/catalog-faq), generic catalog patterns
* [Fintech SaaS agent](/user-guides/recipes/fintech-saas), JWT + customer-login token patterns
