Errors & Troubleshooting
Where an operation can fail
A request can fail in three places. Check all of them:
- The HTTP status. For example 401 when the token is missing or expired.
- The errors array at the top level of the response. It appears when the query itself is invalid (unknown field, wrong type, bad syntax). The HTTP status is still 200.
- errorCode, errorMessage and errorData inside the mutation result. It appears when a business rule fails.
Every mutation in the Delivery API returns the same wrapper:
result
errorCode
errorMessage
errorDataresult is a single object (or a boolean for delete operations). errorCode is an integer. When the operation succeeds, the three error fields are null.
Common problems
Symptom | Likely cause | Fix |
|---|---|---|
404 when you open the endpoint in a browser | You sent a GET request. | The endpoint only accepts POST. This is expected. |
401 on every request | The token is missing, or the header is wrong. | Get a token, then send Authorization: Bearer <token>. |
401 after it worked for a while | The token expired. | Renew the token and retry. |
errors array and HTTP 200 | The query is invalid (field name, argument name or type). | Check the schema in the GraphQL Playground and fix the query. |
Mutation returns 200 but errorCode is filled in | A business rule failed. | Read errorMessage and errorData. Fix the data and retry. |
items: [] on Get Customer by Name and Phone | No customer matches. This isn't an error. | Go to Add Customer. |
Type error on orderKey in Get Order | orderKey is a list ([Guid]). | Send an array, even for one value: ["<orderKey>"]. |
Duplicate customers | Add Customer was called without searching first. | Always run Get Customer by Name and Phone first. |
Webhook isn't confirmed or events are missing | Your endpoint didn't return 2xx in time. | Check the response code and the response time of your endpoint. Expressoft retries only twice, 5 seconds apart. |
Your webhook works in sandbox but nothing arrives in production | In production, Expressoft configures webhooks. | Contact [email protected]. |
result.location is null after Add Order | Seen in a tested response. | Read the order again with Get Order if you need the location. |
Error codes
When a mutation fails, errorCode holds a number and errorMessage holds the name of the error, split in words. errorData holds details when the error has any.
The errorCodes query returns the catalogue from the API:
Query
query GetErrorCodes {
errorCodes {
deliveryOrder {
code
name
description
}
}
}cURL
curl --location 'https://sandbox.expressoft.eu/connect/api/interopgql' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <token>' \
--data '{"query": "query GetErrorCodes { errorCodes { deliveryOrder { code name description } } }"}'Response
{
"data": {
"errorCodes": {
"deliveryOrder": [
{
"code": 3,
"name": "InvalidCustomer",
"description": "customerKey does not identify an active customer. errorData is empty."
}
]
}
}
}Add Order (deliveryOrder.create)
errorCode | Name | When it is returned |
|---|---|---|
1 | InvalidLocation | locationKey and callCenterKey are both missing, or locationKey is not an active delivery location. errorData is empty; errorMessage may contain details. |
2 | InvalidCallCenter | callCenterKey is not an active location, or the location is not configured as a call center. errorData is empty; errorMessage may contain details. |
3 | InvalidCustomer | customerKey does not identify an active customer. errorData is empty. |
4 | InvalidCustomerAddress | customerAddressKey does not identify an active address (CustomerAddressType.active = true) of the customer given in customerKey. Check the address with customers.customer.addresses(publicKey) before ordering; if it is inactive, create a new one with customer.createAddress and use the returned publicKey. errorData is empty. |
5 | InvalidCustomerPaymentMethods | One or more payments[].paymentKey values do not identify an active payment method. errorData: list of the rejected paymentKey values. |
6 | InvalidTicketPayment | A meal ticket payment was sent without ticketPayments details. errorData: list of the affected paymentKey values. |
7 | InvalidTicketPaymentProvider | One or more payments[].ticketPayments[].companyKey values do not identify an active meal ticket provider. errorData: list of the rejected companyKey values. |
8 | InvalidInvoiceCustomer | invoice.clientType is Customer and invoice.clientKey does not identify an active customer. errorData: list with the rejected key. |
9 | InvalidInvoiceCustomerAddress | invoice.clientType is Customer and invoice.clientAddressKey does not identify an active customer address. errorData: list with the rejected key. |
10 | InvalidInvoiceCompany | invoice.clientType is Company and invoice.clientKey does not identify an active company. errorData: list with the rejected key. |
11 | InvalidInvoiceCompanyAddress | invoice.clientType is Company and invoice.clientAddressKey does not identify an active company address. errorData: list with the rejected key. |
12 | ProductValidationError | The ordered products could not be validated because of a temporary internal error; this is not caused by the payload. The order was NOT created and it is safe to send it again. errorData: short error message(s). |
13 | InvalidProduct | orders[].productKey (or a bundle option product that has addons) is not available for the given salePriceCategoryKey: the product is inactive or has no active price in that sale price category. A product valid for one sale price category can be invalid for another; build the menu with products(salePriceCategory: |
14 | NoTaxDefinedOnOrderProduct | orders[].productKey has no tax (VAT) defined. errorData: the affected productKey. |
15 | InvalidPromotion | orders[].promotionKey or subtotalDiscount.promotionKey does not identify an active promotion. errorData: list of the rejected promotion keys. |
16 | InvalidOrderAddonVariantProduct | orders[].addon.variants[] contains a variantKey / productKey pair not defined in the addons.variants of the ordered product (see products(...).addons). errorData: { product, variant }. |
17 | NoTaxDefinedOnOrderAddonVariantProduct | The addon variant product has no tax (VAT) defined. errorData: { product, variant }. |
18 | InvalidOrderAddonOptionProduct | orders[].addon.options[].productKey is not defined in the addons.options of the ordered product (see products(...).addons). errorData: { product, option }. |
19 | NoTaxDefinedOnOrderAddonOptionProduct | The addon option product has no tax (VAT) defined. errorData: { product, option }. |
20 | InvalidOrderBundleOptionProduct | orders[].bundle.options[] contains an optionIndex / productKey pair not defined in the bundle.options of the ordered product (see products(...).bundle). errorData: { product, option }. |
21 | NoTaxDefinedOnOrderBundleOptionProduct | The bundle option product has no tax (VAT) defined. errorData: { product, option }. |
22 | NoOrders | orders is empty, or every line has quantity 0 (lines with quantity 0 are ignored). errorData is empty. |
23 | InvalidLocationDeliveryProfile | The delivery location (locationKey) has no active delivery profile / delivery area configured (or its delivery area has no server group), or the account has no delivery area at all. errorData is empty; errorMessage may contain details. |
24 | UnavailableLocation | The delivery location (locationKey) is currently marked as unavailable for delivery orders. Retry later or choose another location. errorData is empty. |
25 | NoTaxDefinedOnDeliveryFee | deliveryFee was sent but no tax (VAT) is configured for the delivery fee. errorData is empty. |
26 | SubtotalDiscountExceedUndiscountedAmount | subtotalDiscount.value exceeds the amount eligible to be discounted. errorData: { eligibleToBeDiscounted }. |
27 | UnbalancedAmounts | Checked only when payments are sent: the sum of payments[].paymentAmount differs from the order total. Order total = sum of orders[] lines ((salePrice + packaging.salePrice) * quantity + discountAmount, plus bundle options and addons) + deliveryFee + serviceChargeFee + subtotalDiscount.value; discounts are negative values, amounts are rounded to 2 decimals. errorData: { ordersAmount, paidAmount }. |
28 | ForbiddenChannelType | channelType is not a trusted channel configured for the account. errorData is empty. |
29 | NoProductPackagingDefined | packaging was sent for a product that has no packaging defined (see products(...).packaging). errorMessage: product name. |
30 | NoTaxDefinedOnProductPackaging | The packaging product has no tax (VAT) defined. errorMessage: product name. |
31 | OrderDiscountExceedUndiscountedAmount | orders[].discountAmount (absolute value) exceeds salePrice * quantity of that line. errorData: the affected productKey. |
32 | ForbiddenNegativeOrderQuantity | orders[] contains a line with negative quantity. errorData is empty. |
33 | DuplicateKey | An order with the given key (DeliveryOrderCreateInputType.key) already exists; the order was not created again. errorData is empty. |
34 | ValidationTimeout | The order could not be validated within the time limit because of a temporary slowness; this is not caused by the payload. The order was NOT created and it is safe to send it again (with the same key). errorData is empty. |
For the codes of the customer and organization mutations, see the Error Code Glossary in Shared Reference. For the error shapes of both APIs, see the Error Code Glossary in Shared Reference.