Tickets
What a ticket is, how its orders are linked and verified, and when to refresh it.
A ticket is one support conversation together with its customer and the orders linked to it. You read tickets in OneInbox, through MCP, or with the Context API.
A conversation has a ticket when the support classifier judged its latest customer message a support request. See List tickets for the details.
Stored, not live
Reading a ticket returns what EcomSquad has already stored. It never contacts the store. A separate refresh asks the store for current order information, stores the result, and returns the ticket with any newly matched orders.
So a connected store does not by itself mean a ticket has current order
information. An order’s details is null until the orders sync or a store lookup
has stored it, and its items stay empty until a store lookup or the shipping
read has read the order.
Linked orders
Each order on a ticket says how it was linked in matchedBy:
email_lookup: the store returned the order for a lookup by the customer’s email address.order_email: the order’s own synced record carries the customer’s email address, compared exactly: ASCII only, trimmed and lowercased, with no folding of dots or+tags. No lookup is needed.second_check: the conversation names the order, and the customer wrote the order’s email address in their own reply, and it matched. This verifies that one order for reading only, never for a refund, cancellation or change.order_mention: the conversation names the order number.agent_link: an agent linked the order.
The first three verify an order. An order_mention also verifies when the
customer quotes the number and their full first and last name matches the
store’s customer record, after normalizing case, accents and whitespace.
The name comes from their From display name or their own message or signature.
A number alone stays unverified. verificationMethod records email or
order_number_and_name, or null when unverified; verified says whether the bar was met.
An unverified order
keeps its name, dates, statuses and items, but has no tracking, no estimated
delivery and no shipping updates: an empty tracking or events there means
withheld, not absent.
A verified shipment on its way carries inTransitSince, when it went in
transit. Some carriers report only milestones, such as in transit and delivered,
so a parcel can go days without a new update. Only after 14 days with none does
the ticket list a no_carrier_update unknown, whose detail is the time of the
newest update.
Email verification means the address matched, not that the sender controls it. The sender’s address comes from the email and is never proven. Neither a mention nor an email match verifies who sent a message. An order read by name or ID alone, as Get an order does, names no customer, so it is always unverified.
Refresh a ticket
A refresh asks the Shopify store again for the ticket’s orders and its customer.
It needs a key with Support context → Refresh (context:refresh) and access
to both the mailbox and the store. Read the ticket first, and refresh only when
the answer depends on order information it does not have yet:
| What the ticket shows | Refresh? |
|---|---|
No orders, and the answer needs the customer’s orders | Yes. The lookup by the customer’s email may find them. |
An order whose details is null | Yes. Nothing about that order has been stored. |
An order whose details.items is empty | Yes, if the answer needs its items. |
Orders with details, items and fulfillments | No. Answer from the stored ticket, checking checkedAt. |
curl --fail-with-body --silent --show-error --request POST \
--header "x-api-key: ${ECOMSQUAD_API_KEY:?Set ECOMSQUAD_API_KEY first}" \
"${ECOMSQUAD_API_BASE_URL}/tickets/${ECOMSQUAD_TICKET_ID}/refresh"Allow up to about 75 seconds. Keep routine reads on the GET endpoints. Answer from
the returned ticket.orders, as after a plain read. If refresh.skipped is above
zero, refresh again to continue. Refreshing twice in a row is safe: answers from
the last five minutes are reused, so a retry after a timeout does not repeat store
calls. Every field is in Refresh a ticket.
Refresh states
A refresh reports one refresh.state for the lookups it answered, with
refresh.checkedAt saying when the newest ran.
| State | Meaning | What to do |
|---|---|---|
none | This refresh answered no lookup: the ticket gave it nothing to look up, or it skipped every lookup. | If refresh.skipped is above zero, refresh again. Otherwise ask for an order number. |
fresh | The lookups deciding the answer succeeded. | Answer from the ticket, checking the timestamps. |
not_found | A lookup matched no order. | Read refresh.attempts to see which. If the ticket names no order, ask for an order number. |
outside_history | The store did not return the order’s history. | Ask the merchant to locate it. |
access_missing | No usable store is connected, or the store has not granted the access needed. | Ask your administrator to check the connection. |
provider_unavailable | The store failed, rate-limited, or timed out. | Wait, then refresh. Recent attempts are reused for five minutes. |
not_permitted | Your credentials can read the mailbox but not the store. | Ask for access to the store connection as well. |
refresh.attempts has one entry for each order name the ticket’s conversation
mentions or an agent linked. refresh.customer is the lookup by the customer’s
email address. It is shared by every conversation with that customer, so it may
have run from another one. When the conversation names an order, the order
lookups decide the state and the customer lookup is extra context. When it names
none, the customer lookup decides.
An order the customer wrote that a store answered outside_history appears in
the ticket’s unknowns as mentioned_order_outside_history, with the order name
as its detail: the store did not return that order’s history. It does not
say the order exists, so look it up in Shopify rather than tell the customer no
store has it. A number no store has stays mentioned_order_not_found.
When a store’s customer record counts more orders for the customer’s email than
that store’s email lookup returned and the synced orders hold, the ticket’s
unknowns carry customer_orders_outside_history, with how many more orders the
store counts as its detail, such as 2. The customer has orders: look them up in Shopify rather
than tell the customer they have none. It takes the place of
no_order_for_email.
A missing order can be a valid result. Explain what is missing rather than guessing. See Troubleshooting when order information is missing.