Skip to content

Endpoints

Luis Vervaet edited this page Aug 7, 2026 · 7 revisions

Endpoints

The gateway is self-describing: MCP's tools/list returns every tool it offers, with descriptions and input schemas. At the time of writing that is 62 tools, of which ddREST covers the 26 that make up ordinary browse-cart-order use.

bun run list-tools

That prints what the gateway currently advertises and flags anything ddREST calls that it does not — the check to run when adding a route or after DoorDash ships a change.

Method Path Tool
GET /v1/restaurants doordash_find_restaurants
GET /v1/nearby-stores internal_find_nearby_stores
GET /v1/stores/{store_id} internal_get_store_info
GET /v1/stores/{store_id}/menu doordash_get_restaurant_menu
GET /v1/stores/{store_id}/items internal_find_items_in_store
GET /v1/stores/{store_id}/items/{item_id} internal_get_item_details
GET /v1/stores/{store_id}/menus/{menu_id}/items/{item_id} doordash_get_food_item
GET /v1/stores/{store_id}/promotions internal_list_eligible_cart_promotions
POST /v1/product-lists doordash_create_product_list
GET /v1/carts doordash_list_active_carts
POST /v1/carts/items doordash_add_to_cart
POST /v1/carts/{cart_uuid}/items doordash_add_to_cart
GET /v1/carts/{cart_uuid} doordash_get_cart
DELETE /v1/carts/{cart_uuid} doordash_clear_cart
DELETE /v1/carts/{cart_uuid}/items/{cart_item_id} doordash_remove_cart_item
POST /v1/carts/{cart_uuid}/promotions internal_apply_cart_promotion
DELETE /v1/carts/{cart_uuid}/promotions/{promo_code} internal_remove_cart_promotion
POST /v1/carts/{cart_uuid}/preview internal_preview_order
POST /v1/carts/{cart_uuid}/order internal_submit_order
GET /v1/carts/{cart_uuid}/checkout-url doordash_get_checkout_url
GET /v1/orders internal_get_order_history
GET /v1/orders/{order_uuid}/receipt internal_get_order_receipt
GET /v1/orders/{order_uuid}/status internal_get_order_status
POST /v1/orders/{order_uuid}/reorder internal_reorder
GET /v1/addresses doordash_list_delivery_addresses
PUT /v1/addresses/current doordash_set_delivery_address
GET /v1/payment-methods doordash_get_payment_info

POST /v1/carts/{cart_uuid}/order places a real order and charges the account. tip_amount_cents is required rather than defaulted, so a tip is always deliberate.

Locations

Anywhere coordinates are accepted — GET /v1/restaurants and GET /v1/nearby-stores — you can pass an address_id from GET /v1/addresses instead, and ddREST reads the coordinates off the saved address for you:

curl -s "http://localhost:8787/v1/restaurants?query=pizza&address_id=addr-home" \
  -H "authorization: Bearer dds2.…"

That costs one extra upstream lookup, so passing latitude/longitude directly stays the cheaper path. Nothing is cached — an address you just added would otherwise be invisible until a TTL elapsed.

Pass address_id=default for whichever address DoorDash marks as the account default, so the common case needs no lookup at all:

curl -s "http://localhost:8787/v1/restaurants?query=pizza&address_id=default" \
  -H "authorization: Bearer dds2.…"

The id is the address_id field, not address_link_id.

Sending address_id and coordinates is refused rather than picking a winner, since the two can disagree and you would have no way to tell which was used. An unknown id returns address_not_found listing the addresses that do exist — ids are opaque numbers, so each comes with its printable address:

{
  "error": "address_not_found",
  "message": "No saved address has the id \"9999\".",
  "addresses": "/v1/addresses",
  "known_addresses": [
    { "id": "1611178960", "address": "Thompson Hotels, 21 E Bellevue Pl, Chicago, IL 60611, USA" }
  ]
}

A saved address carrying no coordinates returns address_missing_coordinates rather than quietly falling back to the configured default.

Optional parameters

Every optional argument the gateway advertises is exposed, bar the guest-session ones (session_id, session_nonce, is_guest) which switch identity away from your authenticated account or ask for an AES-encrypted response. /docs has the full list; the ones worth knowing about:

Route Parameter Why it matters
/v1/restaurants radius Miles. Upstream defaults to 3, which is tight outside a dense city — widen it if results look sparse.
/v1/restaurants desired_restaurant_name, item_name Narrow to a named restaurant or a specific dish.
/v1/stores/{id}/menu include_extras Every item's options inline. Much larger response.
/v1/stores/{id}/items disable_ads Suppress sponsored placements. Worth setting if you take only the first result, since a sponsored placement is not ranked.
/v1/stores/{id}/items max_results, snap_eligible_only Results per item; SNAP/EBT filter.
/v1/carts/{uuid}, add-to-cart include_pricing Subtotal, taxes and fees inline. An estimate — the preview endpoint is what an order is priced against.
/v1/carts/{uuid}/preview and /order address_id Deliver this order to a saved address without changing the account default. Sent upstream as delivery_address_id.
/v1/nearby-stores use_store_ranker Experimental upstream ranking instead of soonest-ETA.

Boolean query parameters accept true/false, 1/0, yes/no, and reject anything else rather than guessing — ?disable_ads=maybe is a 400, not a silent true.

The auth and pairing routes are not tool-backed:

Method Path Purpose
POST /v1/auth/login/start Begin the paste-back login
POST /v1/auth/login/complete Finish it and get a session
GET /v1/auth/session Inspect the current session
POST /v1/auth/logout Revoke it
POST /v1/auth/pair/request Device: ask for a pairing code
POST /v1/auth/pair/token Device: poll for the session
GET/POST /v1/auth/pair Human: the approval pages (HTML)
POST /v1/auth/pair/verify Human: look up a code (JSON)
POST /v1/auth/pair/complete Human: approve it (JSON)
POST /v1/auth/pair/deny Human: refuse it (JSON)

About intent

Every MCP tool accepts an intent string, and per dd-cli's own help text DoorDash "may review this data for research and product-improvement purposes". This API generates it server-side, per operation, and forwards no end-user text. Callers cannot set or influence it. Every string sent is in one auditable place: src/mcp/tools.ts.

It is optional on all 26 tools, which the gateway's own schemas confirm — they describe it as "logged for product analytics and monitoring only — has no effect on the results returned". ddREST sends it anyway, because a generated one-line description of the operation is a smaller disclosure than an empty field is a benefit, and it keeps behaviour close to dd-cli's. Nothing would break if it were dropped.

Errors

{ "error": "session_expired", "message": "", "login_start": "/v1/auth/login/start" }

error is a stable machine-readable code. Notable ones: session_missing, session_invalid, session_expired, csrf_origin_rejected, login_ticket_expired, state_mismatch, token_exchange_failed, doordash_unauthorized, doordash_forbidden, upstream_error.

A 403 doordash_forbidden with private_beta_gating: true means the account authenticated fine but is not an approved consumer-MCP tester.

Response bodies

Tool responses are passed through unvalidated, but they are documented. The gateway advertises an outputSchema per tool alongside its inputs, so /docs shows the real response shape for the 20 of 26 tools that describe one. The other six declare a bare {additionalProperties: true} and fall back to the generic pass-through result:

doordash_get_restaurant_menu     internal_get_item_details
doordash_list_active_carts       doordash_get_checkout_url
doordash_list_delivery_addresses doordash_get_payment_info

Documented is not enforced. Nothing validates a response against its schema — DoorDash can add a field at any time and it must still pass through untouched.

The schemas are committed rather than fetched at boot, so the API document is identical for everyone, needs no account, and renders /docs without a network call. Refresh them after a DoorDash change:

bun run list-tools --dump && bun run gen-schemas

That rewrites src/schemas/results.generated.ts, hoisting each tool's $defs into shared components so their $refs resolve inside the OpenAPI document.

Clone this wiki locally