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.

"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.

order.created
order.updated

upsert by externalId

references

renders

Your store

Order sync

KObject
ecomm-order

Customer timeline

Conversation
custom.orderId

KView

Diagram source
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 instead, so it installs everywhere.

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:

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} put/v1/klasses/{name}/{id}

For the initial import of past orders, use bulk creation with importedAt set, 100 orders per job.

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:

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:

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 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.

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.

  • 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.