Commerce
Commerce API
Native commerce (Indonesia): the product catalog and orders with their payment links. Amounts are whole rupiah (IDR).
List products
/v1/productsCursor-paginated catalog, newest first, each product with its sellable variants: price (whole rupiah), SKU and stock. status defaults to active.
Availability: workspaces with Asks native commerce only (Indonesian stores that don't use Shopify) — elsewhere 404 with code COMMERCE_UNAVAILABLE.
Required scope: products:read.
limitintegerPage size (1–100).
starting_afterstringCursor: the `id` of the last item from the previous page.
qstringCase-insensitive match on name, description, category or a variant SKU.
statusstringOne of: active, draft, archived, all.
curl "https://api.asks.app/v1/products" \
-H "Authorization: Bearer $ASKS_API_KEY"{
"object": "list",
"data": [
{
"object": "product",
"id": "string",
"name": "string",
"description": "string",
"category": {},
"status": "active",
"images": [
"string"
],
"options": [
{
"name": null,
"values": null
}
],
"variants": [
{
"id": null,
"title": null,
"option_values": null,
"price": null,
"compare_at_price": null,
"sku": null,
"stock": null,
"reserved": null,
"available": null,
"weight_grams": null,
"image_url": null
}
],
"currency": "IDR",
"created_at": {},
"updated_at": {}
}
],
"has_more": true,
"next_cursor": {}
}Retrieve a product
/v1/products/{id}Availability: workspaces with Asks native commerce only (Indonesian stores that don't use Shopify) — elsewhere 404 with code COMMERCE_UNAVAILABLE.
Required scope: products:read.
curl "https://api.asks.app/v1/products/<id>" \
-H "Authorization: Bearer $ASKS_API_KEY"{
"object": "product",
"id": "string",
"name": "string",
"description": "string",
"category": {},
"status": "active",
"images": [
"string"
],
"options": [
{
"name": "string",
"values": [
"string"
]
}
],
"variants": [
{
"id": "string",
"title": "string",
"option_values": [
"string"
],
"price": 1,
"compare_at_price": {},
"sku": {},
"stock": {},
"reserved": 1,
"available": {},
"weight_grams": {},
"image_url": {}
}
],
"currency": "IDR",
"created_at": {},
"updated_at": {}
}List orders
/v1/ordersCursor-paginated orders, newest first — created by the AI agent, the team or the API — with items, totals and the current payment (payment). Filters combine with AND.
Availability: workspaces with Asks native commerce only (Indonesian stores that don't use Shopify) — elsewhere 404 with code COMMERCE_UNAVAILABLE.
Required scope: orders:read.
limitintegerPage size (1–100).
starting_afterstringCursor: the `id` of the last item from the previous page.
statusstringOne of: pending, processing, shipped, completed, cancelled.
payment_statusstringOne of: unpaid, paid, expired, cancelled, refunded, partially_refunded.
customer_idstringconversation_idstringcreated_afterstringCreated at or after.
created_beforestringCreated before.
qstringOrder number, customer name/phone/email, tracking number or address contains.
curl "https://api.asks.app/v1/orders" \
-H "Authorization: Bearer $ASKS_API_KEY"{
"object": "list",
"data": [
{
"object": "order",
"id": "string",
"number": "string",
"status": "pending",
"payment_status": "unpaid",
"payment_method": "xendit_link",
"needs_review": true,
"source": "ai",
"channel": {},
"customer_id": {},
"conversation_id": {},
"customer_name": {},
"customer_phone": {},
"customer_email": {},
"items": [
{
"id": null,
"product_id": null,
"variant_id": null,
"name": null,
"variant_title": null,
"sku": null,
"quantity": null,
"unit_price": null,
"line_total": null,
"image_url": null
}
],
"subtotal": 1,
"discount": 1,
"shipping_fee": 1,
"tax": 1,
"total": 1,
"currency": "IDR",
"shipping": {
"kind": "merchant",
"option_id": {},
"label": "string",
"courier": {},
"service": {},
"etd": {},
"fee": 1
},
"shipping_address": {
"recipient": {},
"phone": {},
"address": "string",
"district": {},
"city": {},
"province": {},
"postal_code": {},
"area_id": {}
},
"tracking_number": {},
"notes": {},
"cancel_reason": {},
"payment": {
"method": "xendit_link",
"status": "creating",
"url": {},
"amount": 1,
"expires_at": {},
"paid_at": {},
"channel": {},
"attempt": 1,
"bank_accounts": []
},
"voucher": {
"code": "string",
"discount": 1
},
"refunded_amount": 1,
"payment_proof": {
"status": "received",
"updated_at": {}
},
"invoice_number": {},
"invoice_url": "string",
"created_at": {},
"updated_at": {},
"paid_at": {},
"shipped_at": {},
"completed_at": {},
"cancelled_at": {}
}
],
"has_more": true,
"next_cursor": {}
}Create an order
/v1/ordersCreates a real order the way the dashboard and the AI agent do: catalog lines are priced from the catalog, stock is reserved, and the payment request is created at once — a Xendit payment link (payment.url) or a manual bank transfer (payment.bank_accounts). The answer is 201 even when the payment link could not be created: the order exists and payment_error says why. It never messages the customer — send payment.url with POST /conversations/{id}/messages. Emits order.created.
customer_id links a customer record and fills any customer field you leave out; conversation_id ties the order to a conversation, whose channel and customer are used when not given. Without either, the order's channel is api.
Customer binding: an order with no linked customer is bound to the workspace customer with the same customer.email or customer.phone (Indonesian numbers match in any form — 0812…, +62 812…, 62812…), and that customer is created when none exists (emits customer.created); with neither a phone nor an email the order stays unlinked. The order keeps its own customer snapshot (phone stored as 62… digits), and its delivery address is saved to the customer's address book.
Idempotency: the Idempotency-Key header is also the order's own key, so a retry never creates a second order: it replays the first response (cached 24 h), or answers 200 with the existing order and Idempotent-Replayed: true.
Plan: the workspace's plan must include orders and payments (403 with code PLAN_LIMIT_REACHED otherwise). Availability: workspaces with Asks native commerce only (Indonesian stores that don't use Shopify) — elsewhere 404 with code COMMERCE_UNAVAILABLE.
Required scope: orders:write.
itemsarray of objectCatalog lines, priced from the catalog.
custom_itemsarray of objectLines outside the catalog (no stock).
customer_idstringconversation_idstringcustomerobjectCustomer details on the order; each overrides the linked customer's.
shippingobjectshipping_addressobjectdiscountintegerWhole rupiah off the subtotal (never below zero).
taxintegerWhole rupiah (IDR).
payment_methodstringrequiredMust be set up in the store's commerce settings. One of: xendit_link, manual_transfer.
notesstringInternal notes.
voucher_codestringA voucher code from the store. Validated (active, dates, minimum subtotal, usage limits); its discount is added to `discount` and its use is counted with the order (given back if the order is cancelled or expires unpaid). `400` when the code can't be used.
curl -X POST "https://api.asks.app/v1/orders" \
-H "Authorization: Bearer $ASKS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{
"variant_id": "string",
"quantity": 1
}
],
"custom_items": [
{
"name": "string",
"unit_price": 1,
"quantity": 1
}
],
"customer_id": "string",
"conversation_id": "string",
"customer": {
"name": "string",
"phone": "string",
"email": "customer@example.com"
},
"shipping": {
"label": "string",
"fee": 1,
"kind": "merchant",
"courier": "string",
"service": "string",
"etd": "string"
},
"shipping_address": {
"recipient": "string",
"phone": "string",
"address": "string",
"district": "string",
"city": "string",
"province": "string",
"postal_code": "string",
"area_id": "string"
},
"discount": 0,
"tax": 0,
"payment_method": "xendit_link",
"notes": "string",
"voucher_code": "string"
}'{
"object": "order",
"id": "string",
"number": "string",
"status": "pending",
"payment_status": "unpaid",
"payment_method": "xendit_link",
"needs_review": true,
"source": "ai",
"channel": {},
"customer_id": {},
"conversation_id": {},
"customer_name": {},
"customer_phone": {},
"customer_email": {},
"items": [
{
"id": "string",
"product_id": {},
"variant_id": {},
"name": "string",
"variant_title": {},
"sku": {},
"quantity": 1,
"unit_price": 1,
"line_total": 1,
"image_url": {}
}
],
"subtotal": 1,
"discount": 1,
"shipping_fee": 1,
"tax": 1,
"total": 1,
"currency": "IDR",
"shipping": {
"kind": "merchant",
"option_id": {},
"label": "string",
"courier": {},
"service": {},
"etd": {},
"fee": 1
},
"shipping_address": {
"recipient": {},
"phone": {},
"address": "string",
"district": {},
"city": {},
"province": {},
"postal_code": {},
"area_id": {}
},
"tracking_number": {},
"notes": {},
"cancel_reason": {},
"payment": {
"method": "xendit_link",
"status": "creating",
"url": {},
"amount": 1,
"expires_at": {},
"paid_at": {},
"channel": {},
"attempt": 1,
"bank_accounts": [
{
"bank_name": "string",
"account_name": "string",
"account_number": "string"
}
]
},
"voucher": {
"code": "string",
"discount": 1
},
"refunded_amount": 1,
"payment_proof": {
"status": "received",
"updated_at": {}
},
"invoice_number": {},
"invoice_url": "string",
"created_at": {},
"updated_at": {},
"paid_at": {},
"shipped_at": {},
"completed_at": {},
"cancelled_at": {},
"payment_attempts": [
{
"id": "string",
"attempt": 1,
"method": "xendit_link",
"status": "creating",
"amount": 1,
"paid_amount": {},
"amount_mismatch": true,
"channel": {},
"expires_at": {},
"paid_at": {},
"created_at": {}
}
],
"refunds": [
{
"id": "string",
"amount": 1,
"reason": "string",
"method_note": {},
"created_at": {}
}
],
"timeline": [
{
"id": "string",
"type": "string",
"actor_type": "ai",
"actor_id": {},
"data": {},
"created_at": {}
}
],
"payment_error": {
"code": "COMMERCE_PAYMENT_UNAVAILABLE",
"message": "string"
}
}Retrieve an order
/v1/orders/{id}The order with every payment attempt (newest first; the payment link only on the newest, while it can be paid). include=timeline adds the order's event log.
Availability: workspaces with Asks native commerce only (Indonesian stores that don't use Shopify) — elsewhere 404 with code COMMERCE_UNAVAILABLE.
Required scope: orders:read.
includestring`timeline` adds the event log (created, paid, expired, status changes…). One of: timeline.
curl "https://api.asks.app/v1/orders/<id>" \
-H "Authorization: Bearer $ASKS_API_KEY"{
"object": "order",
"id": "string",
"number": "string",
"status": "pending",
"payment_status": "unpaid",
"payment_method": "xendit_link",
"needs_review": true,
"source": "ai",
"channel": {},
"customer_id": {},
"conversation_id": {},
"customer_name": {},
"customer_phone": {},
"customer_email": {},
"items": [
{
"id": "string",
"product_id": {},
"variant_id": {},
"name": "string",
"variant_title": {},
"sku": {},
"quantity": 1,
"unit_price": 1,
"line_total": 1,
"image_url": {}
}
],
"subtotal": 1,
"discount": 1,
"shipping_fee": 1,
"tax": 1,
"total": 1,
"currency": "IDR",
"shipping": {
"kind": "merchant",
"option_id": {},
"label": "string",
"courier": {},
"service": {},
"etd": {},
"fee": 1
},
"shipping_address": {
"recipient": {},
"phone": {},
"address": "string",
"district": {},
"city": {},
"province": {},
"postal_code": {},
"area_id": {}
},
"tracking_number": {},
"notes": {},
"cancel_reason": {},
"payment": {
"method": "xendit_link",
"status": "creating",
"url": {},
"amount": 1,
"expires_at": {},
"paid_at": {},
"channel": {},
"attempt": 1,
"bank_accounts": [
{
"bank_name": "string",
"account_name": "string",
"account_number": "string"
}
]
},
"voucher": {
"code": "string",
"discount": 1
},
"refunded_amount": 1,
"payment_proof": {
"status": "received",
"updated_at": {}
},
"invoice_number": {},
"invoice_url": "string",
"created_at": {},
"updated_at": {},
"paid_at": {},
"shipped_at": {},
"completed_at": {},
"cancelled_at": {},
"payment_attempts": [
{
"id": "string",
"attempt": 1,
"method": "xendit_link",
"status": "creating",
"amount": 1,
"paid_amount": {},
"amount_mismatch": true,
"channel": {},
"expires_at": {},
"paid_at": {},
"created_at": {}
}
],
"refunds": [
{
"id": "string",
"amount": 1,
"reason": "string",
"method_note": {},
"created_at": {}
}
],
"timeline": [
{
"id": "string",
"type": "string",
"actor_type": "ai",
"actor_id": {},
"data": {},
"created_at": {}
}
]
}