Gareth Wilson Gareth Wilson

Guide to Uber Webhooks: Features and Best Practices

Published


Uber Eats webhooks notify your systems when order activity happens on a connected store: a new order comes in, a scheduled order is placed, an order is canceled. If you're building an order-management or POS integration on Uber Eats, webhooks are how you react in real time without polling.

This guide covers how Uber Eats webhooks work, the events you'll handle, how to verify the X-Uber-Signature, the retry behavior, and the best practices for production, plus a note on how Uber Direct's signing differs.

What are Uber webhooks?

Uber Eats webhooks are HTTP POSTs delivered to a Primary Webhook URL you configure, each carrying a JSON event about an order or store. Each delivery is signed with an X-Uber-Signature header you verify with your app's client secret.

One thing to be clear about up front: this guide covers Uber Eats. Uber Direct (Deliveries) uses a different signing key (a per-webhook Signing Key rather than the client secret), covered briefly at the end.

Uber Eats webhook features

FeatureDetails
ConfigurationDeveloper Dashboard (Webhooks tab), one Primary Webhook URL
Signature headerX-Uber-Signature
Signature schemelowercase hex HMAC-SHA256 over the raw body, keyed with the client secret
AcknowledgementHTTP 200 with an empty body
RetriesOn 500/502/503/504, timeouts, network errors; backoff 10s, 30s, 60s, 120s, then exponential (~7 attempts)
Auth optionsBasic Auth or OAuth on the endpoint
SDKNo official webhook SDK
Uber DirectSeparate: per-webhook Signing Key, x-uber-signature / x-postmates-signature

Common events

Uber Eats events cover the order lifecycle and store provisioning:

EventFires when
orders.notificationA new order is received
orders.scheduled.notificationA scheduled order is placed
orders.cancelAn order is canceled
store.provisionedA store is provisioned to your integration

Subscribe to the events you process, and consult Uber's webhook reference for the full list.

Setting up Uber webhooks

Configure a single Primary Webhook URL per integration in the Developer Dashboard's Webhooks tab, with Basic Auth or OAuth on the endpoint. Respond to every delivery with HTTP 200 and an empty body to acknowledge.

Securing Uber webhooks

Every Uber Eats delivery carries an X-Uber-Signature header: a lowercase hex HMAC-SHA256 of the raw request body, keyed with your app's client secret. Compute over the exact raw bytes and compare in constant time.

const crypto = require("crypto");

const CLIENT_SECRET = process.env.UBER_CLIENT_SECRET;

function verify(rawBody, signature) {
  const expected = crypto
    .createHmac("sha256", CLIENT_SECRET)
    .update(rawBody)
    .digest("hex"); // lowercase hex
  const a = Buffer.from(expected);
  const b = Buffer.from((signature || "").toLowerCase());
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  if (!verify(req.body, req.headers["x-uber-signature"])) {
    return res.sendStatus(401);
  }

  res.status(200).send(""); // empty-body 200
  processQueue.add(JSON.parse(req.body)); // process asynchronously
});

The same check in Python:

import hashlib
import hmac
import os

CLIENT_SECRET = os.environ["UBER_CLIENT_SECRET"].encode()


def verify(raw_body: bytes, signature: str) -> bool:
    expected = hmac.new(CLIENT_SECRET, raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, (signature or "").lower())

How Uber Direct differs

Uber Direct (Deliveries) webhooks are signed with a dedicated per-webhook Signing Key (not your client secret) and use x-uber-signature / x-postmates-signature. If your integration spans both products, key each verifier to the correct secret; a single client-secret check won't validate Direct deliveries.

Uber webhook limitations and pain points

Two products, two signing keys

The Problem: Uber Eats signs with the client secret; Uber Direct signs with a per-webhook Signing Key. Code that assumes one key rejects the other product's deliveries.

Why It Happens: Eats and Direct are separate products with separate signing models under a shared "Uber" umbrella.

Workarounds:

  • Route by product and verify Eats with the client secret, Direct with its per-webhook Signing Key.

How Hookdeck Can Help: Hookdeck verifies each source with the right secret at the edge, so your application receives pre-verified events regardless of which Uber product sent them.

Empty-body 200 required

The Problem: Uber expects an HTTP 200 with an empty body. Returning a JSON body or a non-200 is treated as a failed delivery and triggers retries.

Why It Happens: Uber's ack contract is a bare 200.

Workarounds:

  • Send 200 with an empty body and defer processing to a queue.

How Hookdeck Can Help: Hookdeck acknowledges Uber correctly and durably queues the event, so your processing time never affects the ack.

Retries bring duplicates

The Problem: Uber retries on 5xx, timeouts, and network errors with backoff up to ~7 attempts, so the same event can arrive more than once.

Why It Happens: At-least-once delivery favors eventual delivery of order events.

Workarounds:

  • Dedupe on an order/event identifier and make side effects idempotent.

How Hookdeck Can Help: Hookdeck deduplicates deliveries at the edge, so retries don't double-process orders. See our guide to webhook idempotency.

No official webhook SDK

The Problem: There's no official SDK helper for verifying Uber webhooks, so you implement the HMAC yourself.

Why It Happens: Uber's SDKs target the APIs, not webhook ingestion.

Workarounds:

  • Use the constant-time HMAC verification above, keyed correctly per product.

How Hookdeck Can Help: Hookdeck verifies the signature at the edge, so your app receives pre-verified events without hand-rolled crypto.

Best practices

Verify with the correct key per product

Eats: lowercase hex HMAC-SHA256 over the raw body keyed with the client secret. Direct: the per-webhook Signing Key. Compare in constant time.

Acknowledge with an empty 200, process asynchronously

Return an empty-bodied 200 immediately and defer work to a queue. See why to process webhooks asynchronously.

Dedupe on the order/event ID

Use an order or event identifier as an idempotency key so retries don't double-process.

Secure the endpoint

Layer Basic Auth or OAuth on the endpoint in addition to signature verification for defense in depth.

Make Uber webhooks production-ready

Hookdeck verifies X-Uber-Signature, deduplicates, and durably queues every order event

Conclusion

Uber Eats webhooks put order and store activity on a push channel your integration can act on, verified with an X-Uber-Signature lowercase-hex HMAC keyed with your client secret, acknowledged with an empty-bodied 200. The main traps are the empty-200 contract, at-least-once retries that bring duplicates, and the fact that Uber Direct signs with a different key entirely.

Hookdeck verifies the signature (per product), deduplicates, and durably queues every event at the edge, so your app only ever processes verified, unique order events.

Get started with Hookdeck for free and handle Uber webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.