Restaurant partner API

Send dish and rating updates securely.

Use the E-Table237 webhook to keep your restaurant's dishes, images, prices, availability, and customer ratings synchronized with Explore Dishes.

Webhook endpointPOST /api/webhooks/restaurants/{restaurantId}

HMAC-SHA256 · JSON · HTTPS

Getting started

Quick start

An E-Table237 administrator first creates your partner restaurant. The dashboard then displays your unique restaurant ID, webhook endpoint, and signing secret.

  1. Save the secret securely.It is used only by your backend and must never be exposed in browser code.
  2. Create the event payload.Include complete dish display details and the current aggregate rating.
  3. Sign the exact request body.Generate an HMAC-SHA256 signature using the timestamp and raw JSON body.
  4. POST the event.Send it over HTTPS and retry only when appropriate.
Ranking behavior

Explore Dishes ranks active dishes by average rating, then rating count, then dish name. A valid update can change the public order immediately.

Request signing

Authentication

Every request needs two signature headers. The timestamp must be Unix time in seconds and within five minutes of the E-Table237 server clock.

HeaderValue
X-ETable-TimestampCurrent Unix timestamp in seconds
X-ETable-Signaturesha256=<hex-digest>
Content-Typeapplication/json
Signed valuetimestamp + "." + rawRequestBodyDigestHMAC_SHA256(restaurantSecret, signedValue)

Sign the exact bytes sent in the HTTP body. Reformatting the JSON after calculating the signature causes verification to fail.

Event contract

Rating update payload

The event upserts the dish record. This means your restaurant should send all required dish fields with every update—not only the rating.

rating-event.jsonUTF-8
{
  "eventId": "rating-order-928-item-12",
  "type": "dish.rating.updated",
  "occurredAt": "2026-08-11T12:00:00.000Z",
  "dish": {
    "externalId": "menu-12",
    "slug": "fish-grill",
    "name": "Fish Grill",
    "description": "Whole grilled fish with herbs and lemon.",
    "category": "Grills",
    "imageUrl": "https://restaurant.example/images/fish.jpg",
    "imageAlt": "Whole grilled fish",
    "price": 9500,
    "currency": "XAF",
    "isActive": true
  },
  "rating": {
    "average": 4.8,
    "count": 42
  }
}
FieldRequirement
eventIdUnique for this event. Reusing it returns a successful duplicate response without applying the update twice.
occurredAtISO 8601 timestamp describing when the rating changed.
dish.externalIdYour stable identifier for the dish.
dish.slugLowercase URL slug using letters, numbers, and hyphens.
dish.imageUrlPublic HTTPS image URL or an agreed E-Table237 asset path.
rating.averageNumber from 0 through 5.
rating.countNon-negative total number of ratings.
Implementation

Request examples

Node.js

send-rating-update.mjsUTF-8
import { createHmac } from "node:crypto";

const endpoint = "http://localhost:3000/api/webhooks/restaurants/{restaurantId}";
const secret = process.env.ETABLE237_WEBHOOK_SECRET;
const body = JSON.stringify(payload);
const timestamp = Math.floor(Date.now() / 1000).toString();
const signature = createHmac("sha256", secret)
  .update(timestamp + "." + body)
  .digest("hex");

const response = await fetch(endpoint, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "X-ETable-Timestamp": timestamp,
    "X-ETable-Signature": "sha256=" + signature,
  },
  body,
});

if (!response.ok) throw new Error(await response.text());

cURL

Calculate the real signature in your application before sending this request.

TerminalUTF-8
curl -X POST "http://localhost:3000/api/webhooks/restaurants/{restaurantId}" \
  -H "Content-Type: application/json" \
  -H "X-ETable-Timestamp: 1770000000" \
  -H "X-ETable-Signature: sha256=<hex-digest>" \
  --data @rating-event.json
HTTP contract

Responses

StatusMeaningAction
202New event accepted and appliedNo retry
200Event was already processedNo retry
401Missing, expired, or invalid signatureCheck secret, timestamp, and raw body
404Restaurant is unknown or inactiveContact E-Table237
422Payload validation failedCorrect the payload; do not retry unchanged
500Temporary server failureRetry with backoff
Accepted responseUTF-8
{
  "accepted": true,
  "duplicate": false,
  "dish": {
    "id": "7b21…",
    "slug": "fish-grill",
    "rating": 4.8,
    "ratingCount": 42
  }
}
Reliability

Retries and idempotency

Retry network failures, timeouts, and 5xx responses with exponential backoff. Do not automatically retry authentication or validation failures.

  • Keep the same eventId for every retry of one event.
  • Generate a fresh timestamp and signature for each attempt.
  • Suggested delays: 2 seconds, 10 seconds, 30 seconds, 2 minutes, then 10 minutes.
  • Stop after a reasonable limit and alert your operations team.
Production checklist

Security and operations

  • Store the webhook secret in a server-side secret manager.
  • Never include the secret in a mobile app, browser bundle, repository, or log.
  • Use HTTPS for dish images and webhook requests.
  • Synchronize server clocks using NTP.
  • Rotate the secret immediately if it may have been exposed.
  • Keep event and response logs without recording the signing secret.
Secret rotation

An E-Table237 administrator can rotate your restaurant secret. Rotation invalidates the previous secret immediately, so coordinate the change before production traffic continues.

Integration support

Ready to connect your restaurant?

Contact the E-Table237 team for partner onboarding, webhook credentials, or help validating a test event.

Contact E-Table237