Orders and customers

Stored orders by name or id, and what is stored for one email address.

View as Markdown
GET/orders

Requires context:read. tRPC context.order.get, MCP get_order.

Reads one order a store lookup has already stored, without contacting the store. An order known only from the orders sync is not returned. Send exactly one of orderName or orderId, and storeConnectionId when your organization has more than one store; otherwise the answer is 400.

orderId matches only the id form a store lookup stores, gid://shopify/Order/1000000001. The numeric orderId on a ticket or in a customer's synced orders matches nothing: send orderName, or put gid://shopify/Order/ in front of the number.

An order read here names no customer, so it is always unverified: no tracking, no estimated delivery and no total. Read it through its ticket to see those.

Returns null when the store is readable but has no stored order with that name or id; a refresh of a ticket that mentions it may store it. A 404 means your organization has no enabled store, no store with that storeConnectionId, or the store is outside your connection access.

x-api-key<token>

In: header

Query Parameters

orderName?string

The order name, such as #1001. Case-insensitive. Send this or orderId.

Length1 <= length <= 128
orderId?string

The order id as a store lookup stored it, such as gid://shopify/Order/1000000001, matched exactly. The numeric orderId on a ticket or a synced order matches nothing: send orderName, or put gid://shopify/Order/ in front of the number. Send this or orderName.

Length1 <= length <= 128
storeConnectionId?string

The store to read. Required when your organization has more than one store.

Match^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
Formatuuid

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/orders"
{  "storeConnectionId": "ccc7195b-c21e-4828-b08b-76e1da7e2a61",  "orderId": "string",  "orderName": "string",  "association": "mentioned",  "customerMatch": "email_match",  "verification": {    "state": "verified",    "source": "email_match",    "checkedAt": "string"  },  "createdAt": "string",  "cancelledAt": "string",  "financialStatus": "string",  "fulfillmentStatus": "string",  "fetchedAt": "string",  "total": {    "amount": "string",    "currency": "string"  },  "items": [    {      "itemId": "string",      "title": "string",      "variantTitle": "string",      "sku": "string",      "quantity": 0,      "unfulfilledQuantity": 0,      "free": true,      "discounts": [        {          "kind": "code",          "name": "string"        }      ]    }  ],  "fulfillments": [    {      "fulfillmentId": "string",      "status": "string",      "displayStatus": "string",      "deliveredAt": "string",      "estimatedDeliveryAt": "string",      "tracking": [        {          "company": "string",          "number": "string",          "url": "string"        }      ]    }  ]}
POST/orders/lookup

Requires context:lookup on an API key, which context:read does not include; a sign-in session or an MCP sign-in is refused with 403. tRPC context.order.lookup (a mutation). No MCP tool. Only owners and admins can add context:lookup to a key; the Order lookup preset holds it alone. Keep the key on your server, never in an app.

For a store's own app: send the email a shopper placed an order with and its order name. When both match, the answer is the order's status, shipments with tracking, estimated delivery and latest update, and items. Never a total, a price, an address or the shopper's details. Reads stored data only and never calls the store.

  • orderNumber is an order name: letters, digits and # _ . / -, single spaces between parts, at least one digit. Anything else, such as an email typed into it, is a 400. It matches the order name exactly, with ASCII letters in any case. Without a leading # it also tries # in front, so 10482 finds #10482. It never matches the order number alone.
  • verified lists every matching order in the stores the key reaches. A cancelled or refunded order is verified too, with its status.
  • no_match is the one answer for a wrong email, an unknown or test order, and an order outside the key's stores.
  • unavailable means no store the key reaches may have its order emails read now. Try again later.
  • shippingReadAt is null until EcomSquad has read the order's shipping. A match asks for that read; the next scheduled one, normally within 30 minutes, fills it in.
  • 429 after 5 failed lookups for one email or one order name, or 200 through one key, in the 24 hours before the call. A matched lookup counts toward none. A 429 also answers a call that waited more than a second behind your organization's other lookups. Limit each shopper on your side too.

A 400 never repeats what was sent, and no log line holds the email or the order number.

x-api-key<token>

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/orders/lookup" \  -H "Content-Type: application/json" \  -d '{    "email": "string",    "orderNumber": "string"  }'
{  "result": "verified",  "orders": [    {      "storeConnectionId": "string",      "orderName": "string",      "createdAt": "string",      "cancelledAt": "string",      "financialStatus": "string",      "fulfillmentStatus": "string",      "checkedAt": "string",      "shippingReadAt": "string",      "fulfillments": [        {          "displayStatus": "string",          "deliveredAt": "string",          "estimatedDeliveryAt": "string",          "estimatedDeliveryText": "string",          "latestUpdate": {            "status": "string",            "at": "string"          },          "tracking": [            {              "company": "string",              "number": "string",              "url": "string"            }          ]        }      ],      "items": [        {          "title": "string",          "variantTitle": "string",          "quantity": 0        }      ]    }  ]}
POST/orders/lookup-by-email

Requires context:lookupByEmail on an API key. context:lookup and context:read do not include it, and a sign-in session or an MCP sign-in is refused with 403. tRPC context.order.lookupByEmail (a mutation). No MCP tool. Only owners and admins can add context:lookupByEmail to a key. Keep the key on your server, never in an app.

For a store's own app whose server has verified that the shopper owns the email, for example through a sign-in provider. Sending the email vouches for that: anyone holding the key can read the orders of any email it sends. The answer is the order lookup's view of each order: status, shipments with tracking, estimated delivery and latest update, and items. Never a total, a price, an address or the shopper's details. Reads stored data only and never calls the store.

  • A store answers only once EcomSquad has switched it on for lookups by email.
  • verified lists the newest non-test orders placed with the email in the 90 days before the call, newest first, at most 5. A cancelled or refunded order is listed too, with its status.
  • no_match means no such order. Ask for the order number and use POST /orders/lookup.
  • unavailable means no store the key reaches is switched on with its order emails caught up. Ask for the order number; that lookup says whether it can answer.
  • An order placed in the last few minutes may not be stored yet.
  • shippingReadAt is null until EcomSquad has read the order's shipping. A match asks for that read; the next scheduled one, normally within 30 minutes, fills it in.
  • 429 after 20 calls through your key for one email, or once your key has looked up 300 different emails, in the 24 hours before the call, matched or not. Keep the answer for the shopper's session rather than calling on every screen. Repeating an email the key already looked up does not count toward the 300. A 429 also answers a call that waited more than a second behind your organization's other lookups by email.

A 400 never repeats what was sent, and no log line holds the email.

x-api-key<token>

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/orders/lookup-by-email" \  -H "Content-Type: application/json" \  -d '{    "email": "string"  }'
{  "result": "verified",  "orders": [    {      "storeConnectionId": "string",      "orderName": "string",      "createdAt": "string",      "cancelledAt": "string",      "financialStatus": "string",      "fulfillmentStatus": "string",      "checkedAt": "string",      "shippingReadAt": "string",      "fulfillments": [        {          "displayStatus": "string",          "deliveredAt": "string",          "estimatedDeliveryAt": "string",          "estimatedDeliveryText": "string",          "latestUpdate": {            "status": "string",            "at": "string"          },          "tracking": [            {              "company": "string",              "number": "string",              "url": "string"            }          ]        }      ],      "items": [        {          "title": "string",          "variantTitle": "string",          "quantity": 0        }      ]    }  ]}
POST/customers/orders/search

Requires context:read, with the same organization and connection access as get_customer. tRPC context.customer.findOrders (mutation), MCP find_customer_orders. Live Shopify search through the flexible query job; no results are stored. Send name (at least 3 letters) and/or phone (6 to 15 digits), optionally ticketId to compare with a readable ticket's sender. Phone-looking names use the phone filter. Returns matches first, at most five newest orders, all unverified, with products/variants, statuses and Shopify admin links. orderEmail is sender, different or unknown, never an address; note is set only for different. multipleCustomers flags several matching customer records. For the support person only: do not quote results to the customer until confirmed. Provider search semantics determine candidates; a local case/accent-insensitive name check removes non-name hits. A 502 means the search failed; no partial matches are returned. No store or customer data is changed.

x-api-key<token>

In: header

Request Body

application/json

TypeScript Definitions

Use the request body type in TypeScript.

Response Body

application/json

application/json

application/json

application/json

application/json

application/json

application/json

application/json

curl -X POST "https://example.com/customers/orders/search" \  -H "Content-Type: application/json" \  -d '{}'
{  "matches": [    {      "orderName": "string",      "createdAt": "2019-08-24T14:15:22Z",      "storeConnectionId": "ccc7195b-c21e-4828-b08b-76e1da7e2a61",      "financialStatus": "string",      "fulfillmentStatus": "string",      "items": [        {          "title": "string",          "variantTitle": "string"        }      ],      "itemsTruncated": true,      "shopifyUrl": "http://example.com",      "matchedBy": "name",      "verification": "unverified",      "orderEmail": "sender",      "note": "string"    }  ],  "multipleCustomers": true}
GET/customers

Requires context:read. tRPC context.customer.get, MCP get_customer.

Reads what is stored for one email address, without contacting the store. Addresses match after trimming, and lowercasing when the address is all ASCII: dots and +tags are not folded, and an order with no order email belongs to no one. It never returns other emails: asking for an address a second check recorded answers as for any address.

storeCustomer.orders is a lifetime count, orders covers each store's synced history and matchedOrders what the store's order lookup by the email returned, so the three can differ. This is a view of an email address, not evidence about who sent a message: a reply to a sender may use tracking only from a ticket's verified orders.

An address with no conversations or synced orders you may read returns zero counts and empty lists, not an error.

x-api-key<token>

In: header

Query Parameters

email*string

The customer's email address.

Match^(?:[A-Za-z0-9_'+\-]+\.)*[A-Za-z0-9_'+\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
Formatemail

Response Body

application/json

application/json

application/json

application/json

application/json

curl -X GET "https://example.com/customers?email=user%40example.com"
{  "email": "string",  "name": "string",  "conversations": 0,  "storeCustomer": {    "name": "string",    "customerSince": "string",    "orders": 0,    "amountSpent": "string",    "currency": "string",    "countryCode": "string",    "checkedAt": "string"  },  "matchedOrders": [    {      "storeConnectionId": "ccc7195b-c21e-4828-b08b-76e1da7e2a61",      "orderId": "string",      "orderName": "string",      "association": "mentioned",      "customerMatch": "email_match",      "verification": {        "state": "verified",        "source": "email_match",        "checkedAt": "string"      },      "createdAt": "string",      "cancelledAt": "string",      "financialStatus": "string",      "fulfillmentStatus": "string",      "fetchedAt": "string",      "total": {        "amount": "string",        "currency": "string"      },      "items": [        {          "itemId": "string",          "title": "string",          "variantTitle": "string",          "sku": "string",          "quantity": 0,          "unfulfilledQuantity": 0,          "free": true,          "discounts": [            {              "kind": "code",              "name": "string"            }          ]        }      ],      "fulfillments": [        {          "fulfillmentId": "string",          "status": "string",          "displayStatus": "string",          "deliveredAt": "string",          "estimatedDeliveryAt": "string",          "tracking": [            {              "company": "string",              "number": "string",              "url": "string"            }          ]        }      ]    }  ],  "omittedForAccess": 0,  "matchedOrdersOmitted": 0,  "orders": [    {      "storeConnectionId": "ccc7195b-c21e-4828-b08b-76e1da7e2a61",      "orderId": "string",      "orderName": "string",      "createdAt": "string",      "financialStatus": "string",      "fulfillmentStatus": "string",      "cancelledAt": "string",      "total": {        "amount": "string",        "currency": "string"      },      "items": [        {          "title": "string",          "variantTitle": "string",          "sku": "string",          "quantity": 0        }      ]    }  ],  "ordersOmitted": 0,  "ordersSince": [    {      "storeConnectionId": "ccc7195b-c21e-4828-b08b-76e1da7e2a61",      "label": "string",      "ordersSince": "string"    }  ],  "totalSpend": [    {      "currency": "string",      "amount": "string"    }  ]}