Store status changed
A store's intended or observed state changed.
Delivered to the ONE url you registered for stores. Return any 2xx to acknowledge; anything else is retried with exponential backoff. The payload carries the full status object, so you never have to call back to find out what changed to what.
Request
- idstring
Unique event id — the deduplication key.
Deliveries are at-least-once and unordered, so you WILL receive the same event more than once. Every copy of one event carries the same
id: retries of a delivery, and the deliveries to each url you have registered. Two legitimate transitions of the same subject are two different events and carry two different ids.Key your idempotency on this field alone. Nothing else in the payload is a dedup key —
timestampis the moment the attempt was SENT and differs between copies of one event. - eventstring
Always
store.status.changed.Possible values: [store.status.changed] - timestampstring<date-time>
When this delivery was SENT — not when the event happened. Order by the transported state, never by arrival.
- organization_idstring
Your organization id, also carried on the delivery's
X-Organization-IDheader.Snake case on this ONE field is a long-lived wire value; every other field on this API is camelCase.
Treat it as a sanity check, never as proof of origin — it is a value inside a body anyone who learns your url can POST. Proof of origin is the
X-OneTablet-Signatureheader, which store-event deliveries carry on every attempt; see "Verifying webhook signatures". Phase 0 order deliveries are not signed yet, so for those there is no cryptographic authentication at all. - dataStoreStatus
The store's full status, including the embedded
discrepancy— the same objectfetchStoreStatusesreturns.
{
"id": "00000000-0000-0000-0000-000000000000",
"event": "store.status.changed",
"timestamp": "2026-01-01T00:00:00Z",
"organization_id": "00000000-0000-0000-0000-000000000000",
"data": {
"storeId": "00000000-0000-0000-0000-000000000000",
"desired": {
"state": "open",
"pauseReason": "closingEarly",
"pauseNote": "<pauseNote>",
"pausedUntil": "2026-01-01T00:00:00Z",
"updatedAt": "2026-01-01T00:00:00Z"
},
"observed": {
"state": "online",
"orderable": true,
"platformReason": "<platformReason>"
},
"discrepancy": {
"hasDiscrepancy": true,
"classification": "pauseNotPropagated",
"autoRemediable": true,
"requiresHumanIntervention": true,
"remediationChannel": "api"
},
"lastPolledAt": "2026-01-01T00:00:00Z"
}
}About Store status
Whether each storefront is actually taking orders — and whether OneTablet and the delivery platform agree about it.
Separate from Stores, which is the identity map: the brands, kitchens and storefronts you operate, which changes when your estate changes. This section is the OPERATIONAL layer on top of it — one read (fetchStoreStatuses), two writes (pauseStores, unpauseStores) and the store.status.changed webhook — all addressed by the Store.id values fetchStores hands you. Same api key, same org scoping, same ids; a different job, and a different route prefix (/management/store-status) so the url says which.
Three things are kept distinct throughout, and conflating them is the mistake this section exists to prevent: desired is what OneTablet is configured to do, observed is what the platform reports, and discrepancy is the classified disagreement between the two.
Org-scoped to the organization your api key resolves to, exactly as the directory reads are.