# Show orders with custom objects

> Build an e-commerce orders integration with a Klass, KObjects synced from your store, a link from conversations to orders, and a KView that shows order details to agents.

Source: https://developer.kustomer.com/docs/guides/kobject-orders-integration

"Where is my order?" is the most common question in e-commerce support. This guide puts orders on the customer timeline so agents can answer without leaving Kustomer, and so workflows can route and automate based on order data.

You'll build:

1. An `ecomm-order` **Klass** with typed attributes.
2. A sync that creates and updates **KObjects** from your store's order events.
3. A **conversation attribute** that links a conversation to the order it's about.
4. A **KView** that shows line items and tracking to agents.

```mermaid
flowchart LR
  Store[(Your store)] -- "order.created<br/>order.updated" --> Sync[Order sync]
  Sync -- "upsert by externalId" --> KO[KObject<br/>ecomm-order]
  KO --> Cust[Customer timeline]
  Conv[Conversation<br/>custom.orderId] -. references .-> KO
  KV[KView] -. renders .-> KO
```

## 1. Define the Klass

Create the Klass and its attribute metadata. If you're packaging this as an app, put the same definition in your [app definition](https://developer.kustomer.com/docs/apps/klasses) instead, so it installs everywhere.

```bash title="cURL"
curl -X POST "https://acme.api.kustomerapp.com/v1/klasses" \
  -H "Authorization: Bearer $KUSTOMER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "ecomm-order", "displayName": "Order", "icon": "basket", "color": "#64943E" }'

curl -X PUT "https://acme.api.kustomerapp.com/v1/metadata/kobject.ecomm-order" \
  -H "Authorization: Bearer $KUSTOMER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "properties": {
      "orderNumberStr": { "displayName": "Order number" },
      "orderStatusStr": { "displayName": "Status", "enumeration": ["placed", "paid", "shipped", "delivered", "cancelled", "returned"] },
      "totalPriceNum": { "displayName": "Total" },
      "currencyStr": { "displayName": "Currency" },
      "orderedAt": { "displayName": "Ordered" },
      "shippedAt": { "displayName": "Shipped" },
      "carrierStr": { "displayName": "Carrier" },
      "trackingUrl": { "displayName": "Tracking link" },
      "adminUrl": { "displayName": "Open in store admin" }
    }
  }'
```

Attribute names end in a type suffix: `Str`, `Num`, `At`, `Url`. Put anything you'll filter, search or automate on in `custom`. Put nested data such as line items in `data`.

## 2. Sync orders

Subscribe to your store's order events and upsert one KObject per order, keyed by `externalId`:

```ts title="Node.js"
const KLASS = 'ecomm-order'

async function upsertOrder(order: StoreOrder) {
  const customerId = await customerFor(order.customer) // find or create by externalId or email
  const body = JSON.stringify({
    title: `Order #${order.number}`,
    description: `${order.items.length} item(s) · ${order.status}`,
    externalId: `order-${order.id}`,
    custom: {
      orderNumberStr: String(order.number),
      orderStatusStr: order.status,
      totalPriceNum: order.total,
      currencyStr: order.currency,
      orderedAt: order.createdAt,
      shippedAt: order.shippedAt ?? null,
      carrierStr: order.carrier ?? null,
      trackingUrl: order.trackingUrl ?? null,
      adminUrl: `https://admin.store.example/orders/${order.id}`,
    },
    data: { items: order.items.map((i) => ({ sku: i.sku, name: i.name, qty: i.quantity, price: i.price })) },
  })

  const found = await kfetch(`/v1/klasses/${KLASS}/externalId=order-${order.id}`)
  if (found.status === 404) return kfetch(`/v1/customers/${customerId}/klasses/${KLASS}`, { method: 'POST', body })
  const { data } = await found.json()
  return kfetch(`/v1/klasses/${KLASS}/${data.id}`, { method: 'PUT', body })
}
```

`POST /v1/customers/{id}/klasses/{name}` ([reference](https://developer.kustomer.com/api/core-resources/create-customer-kobject-custom-object))

`PUT /v1/klasses/{name}/{id}` ([reference](https://developer.kustomer.com/api/core-resources/update-kobject-custom-object-by-id))

For the initial import of past orders, use [bulk creation](https://developer.kustomer.com/api/core-resources/bulk-create-kobjects-custom-objects) with `importedAt` set, 100 orders per job.

## 3. Link conversations to orders

Add an `Id` attribute to conversations whose target is the order Klass. Agents (or your automation) can then pick the order a conversation is about:

```bash title="cURL"
curl -X PUT "https://acme.api.kustomerapp.com/v1/metadata/conversation" \
  -H "Authorization: Bearer $KUSTOMER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "properties": { "orderId": { "displayName": "Order", "target": "kobject.ecomm-order" } } }'
```

Set it when you can infer the order, for example from an order number in the message:

```ts title="Node.js"
const match = /#?(\d{4,})/.exec(message.preview)
if (match) {
  const order = await kfetch(`/v1/klasses/ecomm-order/externalId=order-${match[1]}`)
  if (order.ok) {
    const { data } = await order.json()
    await kfetch(`/v1/conversations/${conversationId}`, { method: 'PUT', body: JSON.stringify({ custom: { orderId: data.id } }) })
  }
}
```

Kustomer validates that `orderId` points to an existing `ecomm-order` KObject.

## 4. Show order details with a KView

Orders already appear on the timeline with the Klass icon, `title` and `description`. A [KView](https://developer.kustomer.com/docs/apps/kviews) gives agents a richer card with line items and a tracking button. KViews are defined in an app definition, with a `resource` of `kobject.ecomm-order` and a JSX `template`.

_Figure: An order KView in the Kustomer insight panel showing order number, status badge, line items table, total and a Track package button. An order KView in the insight panel._

## 5. Automate with order data

Because order attributes are typed and indexed, you can use them across Kustomer:

- **Search:** find every customer with a delivered order over $500 in the last week:

  ```json
  {
    "queryContext": "customer",
    "and": [
      { "kobject_custom_ecomm-order_orderStatusStr": { "equals": "delivered" } },
      { "kobject_custom_ecomm-order_totalPriceNum": { "gte": 500 } },
      { "kobject_updated_at": { "gte": "2026-10-14" } }
    ]
  }
  ```

- **Workflows:** trigger on KObject events for the order Klass, for example to send a proactive message when `orderStatusStr` changes to `delayed`. See [Workflows](https://developer.kustomer.com/docs/workflows).

- **Routing:** route conversations about high-value orders to a priority queue using the linked `orderId`.

## Production notes

- One Klass per object type. Don't create a Klass per store or per region; use an attribute instead.
- Upserts by `externalId` make the sync safe to replay. Replay from your store's event log after an outage.
- Each customer can have at most 120 KObjects created per minute by one client. For bursts, such as a flash sale, queue writes and use bulk jobs.
