Errors
Every failure comes back in the
same envelope as every other response,
with a stable error.code and a matching HTTP status. Branch on the code, not on the message.
Codes
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
MISSING_KEY | 401 | No Authorization: Bearer header | Send the key |
INVALID_KEY | 401 | The key is unknown, revoked or expired | Create a new key |
WRONG_SURFACE | 401 | An aov_mcp_ key was used on this API | Create an aov_sk_ key under Settings → Store API |
APP_UNINSTALLED | 403 | The store no longer has the app | Reinstall, then create a new key |
NO_PLAN | 403 | The store is not on a paid plan. Its Store API keys are revoked | Upgrade, then create new keys |
RATE_LIMIT | 429 | Too many requests, too many writes, or another write is in progress | Wait and retry |
NOT_FOUND | 404 | No such endpoint | Check the path against GET /store-api/v1 |
INVALID_INPUT | 400 | A parameter or body field is missing, malformed, or the revision is stale | Read the message, fix the request |
READ_ONLY_CONNECTION | 409 | A read-only key called a write endpoint | Use a read and write key |
TOOL_UNAVAILABLE_FOR_STATE | 409 | The endpoint belongs to the other cart drawer generation | Check GET /context |
CONFIGURATION_NOT_READY | 409 | The Cart Editor has not saved its initial settings yet | Open the Cart Editor in the app and save once |
CONFIGURATION_AMBIGUOUS | 409 | The store has more than one configuration and none can be chosen safely | Resolve it in the app |
CONFIGURATION_NOT_SAVED | 409 | A write was checked but not saved. Details are in data | Read data, correct, retry |
INTERNAL_ERROR | 500 | Something failed on our side. The message is always "The request could not be completed." | Retry; if it persists, contact support |
A request that matched more than one thing and needs you to choose returns 422 with the candidates
in data; send it again with the one you want.
PLAN_CHANGED, CONNECTION_REVOKED and CONNECTION_EXPIRED can also appear as 409 when the key's
state changes while a request is running. Treat them like INVALID_KEY: create a new key.
Authentication, in order
A request is checked in this order, and the first failure is what you see:
- The
Authorizationheader is present — elseMISSING_KEY. - The key's rate limit — else
RATE_LIMIT. - The key exists and is an
aov_sk_key — elseINVALID_KEYorWRONG_SURFACE. - The key has not expired or been revoked — else
INVALID_KEY. - The store still has the app — else
APP_UNINSTALLED. - The store is on a paid plan — else
NO_PLAN.
MISSING_KEY means the header never reached us, which is usually a proxy or a typo in the header name.
INVALID_KEY means it arrived and was not accepted.
NO_PLAN revokes keys
NO_PLAN is not temporary: the store's Store API keys are revoked at the same moment, so upgrading
does not revive them. Why, and what to do, is under
Losing the plan revokes your keys.
Rate limits and the write lock
The three limits (requests per minute, writes per minute and one write at a time) are listed under Limits.
All three return RATE_LIMIT (429). The message says which: the per-key write limit tells you to wait
about a minute; the per-store lock says another change is still being saved. If that save failed, the
store stays locked for up to a minute.
Retry after a short pause, and spread bulk changes out rather than sending them in parallel.
Read-only keys
A read-only key that calls a write endpoint gets READ_ONLY_CONNECTION (409). What a read-only key can
call is under Access levels, and the endpoint
list it sees is under
The API describes itself. Create a
read and write key under Settings → Store API if the integration needs to change things.
A stale revision
When you send expectedRevision and the configuration has moved on, for example because a teammate
saved in the Cart Editor or another integration wrote, the write is refused with
INVALID_INPUT and a message such as "Cart drawer configuration changed from revision 42 to 43. Read
it again before retrying."
Read /drawer-settings again, re-apply your change to what is there now, and send the new revision.
Do not retry the same request unchanged: it will fail the same way.
requestId reuse
Repeating a write with the same requestId and the same body is safe and does not apply the change
twice. Reusing a requestId with a different change is refused with INVALID_INPUT. Use a new
requestId for each distinct change.
A write that was not saved
When a write is checked but not committed, you get success: false, a 409 (or 422), and the details in
data. Nothing was changed. The code is whatever data.code says, or CONFIGURATION_NOT_SAVED when
none is given. Fix what it names and send the request again.
Retrying
| Code | Retry? |
|---|---|
RATE_LIMIT, INTERNAL_ERROR | Yes, after a pause |
INVALID_INPUT, NOT_FOUND, READ_ONLY_CONNECTION, TOOL_UNAVAILABLE_FOR_STATE | No — change the request first |
CONFIGURATION_NOT_READY, CONFIGURATION_AMBIGUOUS, CONFIGURATION_NOT_SAVED | After the cause is fixed |
MISSING_KEY, INVALID_KEY, WRONG_SURFACE, APP_UNINSTALLED, NO_PLAN | No — you need a new key or a new plan |