OneTablet API Documentation

Menu publication completed

Webhookmenu.publication.completed

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

  • eventstring

    Always menu.publication.completed.

    Possible values: [menu.publication.completed]
  • 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; see "Verifying webhook signatures". Phase 0 order deliveries are not signed yet, so for those there is no cryptographic authentication at all.

  • dataMenuPublication

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

About Menus

Menus — read a menu, publish it to the delivery platforms, and follow each publication's per-store, per-platform outcome.

publishMenu is the only write here and the only operation on this API that changes what guests see on live DoorDash, Uber Eats and Grubhub storefronts. There is no unpublish.

Every operation is scoped to the organization your API key belongs to. An id that does not exist and one that belongs to another organization are the same 404, indistinguishably — and on publishMenu a menu or store outside your organization is rejected before anything is published.

Retries create a second publication. publishMenu does not replay a previous result: a retried call creates a new publication with its own publicationId and re-submits to the platforms. That changes no content — a publication is a full replace of the same saved menu — but it doubles platform traffic. Treat a timeout as "may have succeeded" and check fetchMenuPublications before retrying.