Commerce
REST reference

Commerce API

Native commerce (Indonesia): the product catalog and orders with their payment links. Amounts are whole rupiah (IDR).

List products

GET/v1/products

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

Parameters
limitinteger

Page size (1–100).

starting_afterstring

Cursor: the `id` of the last item from the previous page.

qstring

Case-insensitive match on name, description, category or a variant SKU.

statusstring

One of: active, draft, archived, all.

Request
cURL
curl "https://api.asks.app/v1/products" \
  -H "Authorization: Bearer $ASKS_API_KEY"
Response 200
JSON
{
  "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

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

Request
cURL
curl "https://api.asks.app/v1/products/<id>" \
  -H "Authorization: Bearer $ASKS_API_KEY"
Response 200
JSON
{
  "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

GET/v1/orders

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

Parameters
limitinteger

Page size (1–100).

starting_afterstring

Cursor: the `id` of the last item from the previous page.

statusstring

One of: pending, processing, shipped, completed, cancelled.

payment_statusstring

One of: unpaid, paid, expired, cancelled, refunded, partially_refunded.

customer_idstring
conversation_idstring
created_afterstring

Created at or after.

created_beforestring

Created before.

qstring

Order number, customer name/phone/email, tracking number or address contains.

Request
cURL
curl "https://api.asks.app/v1/orders" \
  -H "Authorization: Bearer $ASKS_API_KEY"
Response 200
JSON
{
  "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

POST/v1/orders

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

Request body
itemsarray of object

Catalog lines, priced from the catalog.

custom_itemsarray of object

Lines outside the catalog (no stock).

customer_idstring
conversation_idstring
customerobject

Customer details on the order; each overrides the linked customer's.

shippingobject
shipping_addressobject
discountinteger

Whole rupiah off the subtotal (never below zero).

taxinteger

Whole rupiah (IDR).

payment_methodstringrequired

Must be set up in the store's commerce settings. One of: xendit_link, manual_transfer.

notesstring

Internal notes.

voucher_codestring

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

Request
cURL
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"
  }'
Response 201
JSON
{
  "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

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

Parameters
includestring

`timeline` adds the event log (created, paid, expired, status changes…). One of: timeline.

Request
cURL
curl "https://api.asks.app/v1/orders/<id>" \
  -H "Authorization: Bearer $ASKS_API_KEY"
Response 200
JSON
{
  "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": {}
    }
  ]
}
Chat on WhatsApp