OneTablet API Documentation

menu.publication.completed

Webhook

A menu publication finished, successfully or not.

Delivered to the ONE url you registered for menus. Return any 2xx to acknowledge; anything else is retried with exponential backoff. Check data.stores[] for the per-store, per-platform outcome — the publication reaching a terminal state does not mean every store succeeded.

When it fires. Once for the publication as a whole, never once per store, each time it reaches a terminal state — the moment fetchMenuPublications would first report it completed or failed:

  • every store finished, some possibly with a failure — data.status is completed;
  • the publication failed as a whole — failed;
  • it was cancelled while still queued or publishing — failed, the value the read reports for a cancelled publication. Cancelling a publication that had already failed sends nothing more: its failed event was already sent;
  • no storefront had a menu to publish — completed, with an empty data.stores, sent at once.

A reopened publication sends a second event. When OneTablet retries the failed stores of a finished publication, it goes back to publishing and its completedAt is cleared; when it finishes again, this fires again with the new completedAt and a new id. Both deliveries are real events, not duplicates: keep the one with the later data.completedAt.

The id. Deduplicate on the envelope id, as for every event. For this event it is built from the publication and the instant this terminal state was reached — today <publicationId>:<completedAt>, for example 9b2f…:2026-10-08T12:34:56.123Z — so every retry of one delivery carries the same id, and a reopened publication's next event carries a new one. It is not a UUID: store it as a string, and read data.publicationId and data.completedAt rather than parsing it.

Request

application/json
required
  • eventstring

    Always menu.publication.completed.

    Possible values: [menu.publication.completed]
  • dataMenuPublication

    The publication and its per-store, per-platform outcomes — the same object fetchMenuPublications returns.

  • 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 — timestamp is the moment the attempt was SENT and differs between copies of one event.

  • 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-ID header.

    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-Signature header, which store-event deliveries carry on every attempt (ONE-2747); see "Verifying webhook signatures". Phase 0 order deliveries are not signed yet, so for those there is no cryptographic authentication at all.

About Menus

Menus — two reads (ONE-2726) and one write (ONE-2780), the section's full published surface.

publishMenu is the ONLY WRITE here and the only operation on this whole API that reaches live DoorDash / UberEats / Grubhub storefronts. There is no unpublish. ONE-2724 once held it behind an undocumented header; ONE-2908 removed that, so today the only thing standing in front of it is authentication plus the org scope check below. It is still absent from MANAGEMENT_API_READ_METHODS in ManagementApiKeyHook, on purpose — that list is now the platform's read/write CLASSIFIER (ONE-2890, ONE-2718 build on it), and a write on a read list would be a lie. Do not "fix" that by adding it.

All three operations are org-scoped to the organization the api key resolves to. Cross-tenant and unknown ids are INDISTINGUISHABLE by design — every one of the three returns the published 404 shape for both cases, same convention as StoresApi / ItemAvailabilityApi. On publishMenu that rule has teeth beyond information disclosure: a foreign menuId must never become a write, so the scope check runs BEFORE anything is enqueued.

NOT IMPLEMENTED: replay via Idempotency-Key. The published contract declares the parameter on publishMenu, and nothing on the platform implements idempotent replay today — the same honest gap ItemAvailabilityApi's header records for setItemAvailability (ONE-2621 decision 3). ⚠️ IT MATTERS MORE HERE: a retried publishMenu creates a SECOND publication and re-submits to the platforms rather than returning the first result. That is not corrupting — a publication is a full replace of the same saved state, so the second is a no-op in content — but it doubles platform traffic and produces two publicationIds for one intent. Callers should treat a timeout as "may have succeeded" and check fetchMenuPublications before retrying.