SHOP DESKDevelopers
shopdeskbd.com

Build a COD confirmation tool

Find the order by phone, confirm or cancel it, schedule the call-back.

Most orders in Bangladesh are cash on delivery, and most merchants call the customer before shipping to confirm. This guide builds that loop over the API: pull the queue, find an order by the number that is calling, check the customer's history, and record what they said.

You need a key with orders:write (which implies orders:read) and customers:read. The customer history in step 3 needs orders:read as well, which orders:write already covers.

1. Pull the queue

Two lists make up a call agent's day: new orders nobody has confirmed yet, and call-backs that are now due.

New orders are status=pending. Due call-backs are orders in follow_up whose follow-up time has passed — status=follow_up with the current time as follow_up_before. That is the same list the dashboard's overdue follow-up count and the MCP follow_up_due queue use:

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS "https://api.shopdeskbd.com/ext/v1/orders?status=follow_up&follow_up_before=2026-10-02T12%3A00%3A00.000Z&expand=customer" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY"

follow_up_before and follow_up_after only ever match orders that have a follow-up scheduled, so combine them for a window ("today") or use one alone for "overdue". Keep status=follow_up in the query: an order that has since been confirmed or cancelled keeps its old follow_up_at, and the status is what takes it off the list. Every order carries its follow_up_at, follow_up_note and assignee ({ id, name }, or null), and assignee_id narrows the list to one agent's orders. Assigning orders is done in the dashboard; the API reads the assignment.

2. Find the order by phone

When a customer calls in, all you have is their number, in whatever form your phone system shows it. search matches order numbers, customer names and customer phones, and a phone matches in any Bangladeshi form: +8801712345678, 8801712345678, 01712345678 and +880 1712-345678 all find the same orders, because phones are stored as 01XXXXXXXXX and the search also tries the term rewritten that way. Order numbers and names are matched as typed, so 20260929-0001 still finds ORD-20260929-0001.

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS "https://api.shopdeskbd.com/ext/v1/orders?search=%2B8801712345678&status=pending&expand=customer" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY"

The same rule applies to GET /customers?search=, which also matches name and email. To look up exactly one number, use GET /customers?phone= instead: it matches the phone only, in any Bangladeshi form, so an email such as 01712345678@gmail.com never gets in the way.

3. Check the customer's history

Before you confirm a ৳5,000 COD order, it helps to know whether this customer usually keeps what they order. Add expand=stats to the customer read:

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS "https://api.shopdeskbd.com/ext/v1/customers/REPLACE_WITH_CUSTOMER_ID?expand=stats" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY"
FieldWhat it counts
orders_countOrders that are not cancelled, returned or spam — the count the dashboard's customer page shows.
total_spentThe total of their delivered orders, delivery and tax included.
last_order_atWhen their latest order was placed (spam excluded).
returned_countOrders that came back whole.

4. Record the outcome

The customer confirmed. Move the order to confirmed with the status route. The same transition rules as the dashboard apply:

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X PATCH "https://api.shopdeskbd.com/ext/v1/orders/REPLACE_WITH_ORDER_ID/status" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: REPLACE_WITH_A_UUID" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "confirmed",
  "note": "Confirmed on the phone."
}'

The customer cancelled. Use POST /orders/{id}/cancel, which releases the reserved stock.

No answer, or "call me after 4pm". Move the order to follow_up with the status route (as for a confirmation, with "status": "follow_up"), then set the call-back time with PATCH /orders/{id}:

export SHOPDESK_API_KEY="shopdesk_sk_YOUR_API_KEY"

curl -sS -X PATCH "https://api.shopdeskbd.com/ext/v1/orders/REPLACE_WITH_ORDER_ID" \
  -H "Authorization: Bearer $SHOPDESK_API_KEY" \
  -H "Idempotency-Key: REPLACE_WITH_A_UUID" \
  -H "Content-Type: application/json" \
  -d '{
  "follow_up_at": "2026-10-02T10:00:00.000Z",
  "follow_up_note": "No answer at 11am — try after 4pm."
}'

PATCH /orders/{id} changes the order note, follow_up_at and follow_up_note, and nothing else. It works on an order in any status, runs the same code as the dashboard's order-details edit, and cannot touch prices, lines or stock. Send null to clear a field and leave out a field to keep it. Send at least one field.

Both pending and confirmed orders can move to follow_up. Once the order is confirmed or cancelled, its status takes it off the call list; the old follow_up_at stays on the order, as it does in the dashboard. Send {"follow_up_at": null, "follow_up_note": null} if you want it cleared too.

5. Stay in sync

If other staff work the same orders in the dashboard, subscribe to order.updated. It fires when the status moves (data.previous.status) and when the note, follow-up or delivery address changes. On those changes data.previous holds the old note, follow_up_at, follow_up_note or shipping_address, named as in data.object. See the event catalog.

With the store MCP server

The same loop runs from an AI assistant connected to the store MCP server with a key holding orders:write and customers:read. Each step above is one tool call:

StepTool
Pull the queuelist_orders_needing_attention with kind: "unconfirmed" (pending orders) or "follow_up_due" (orders in follow_up whose call-back time has passed), oldest first
Find the order by phonefind_orders with the number in any form, an order number or a name
Check the historyget_customer_summary with the phone or customer_id: the stats and the latest five orders
Record the outcomerecord_call_outcome

record_call_outcome takes the order id, an outcome and an optional note:

outcomeWhat it does
confirmedMoves the order to confirmed.
cancelledCancels it through the cancel route; reserved stock is released.
call_backNeeds call_back_at. Moves the order to follow_up and sets the call-back time, with note as the reason.
no_answerMoves the order to follow_up; sets the call-back time if you send call_back_at.

The note also goes on the order timeline with the status change. The status rules are the dashboard's: an order that cannot move to the new status is refused before anything changes. Repeating the same outcome changes nothing more, and an idempotency_key makes a retried call return the first answer. The result lists the steps taken and the order as it now stands.

update_order_details edits the note and call-back directly, like PATCH /orders/{id}. The order note it sets replaces the whole note, and notes these tools show are shortened to 500 characters — so an assistant should never rebuild a note from a value it read. Notes, addresses and names come back in labelled fields with links and markup removed; see MCP tools.