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:
- An
ecomm-orderKlass with typed attributes. - A sync that creates and updates KObjects from your store's order events.
- A conversation attribute that links a conversation to the order it's about.
- A KView that shows line items and tracking to agents.
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 -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:
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 })
}/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.
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:
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:
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.
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
orderStatusStrchanges todelayed. 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
externalIdmake 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.