DoorDash

add-to-cart

doordash.com
InstallationInstalls just this skill for your agent
Side effectwrite
Authnone
Needsnavigate · evaluate
Summary

Add a simple (no-required-options) DoorDash menu item to the cart by store id and item id.

Params
{"storeId":"string (DoorDash store id, e.g. '1179093')","itemId":"string (menu item id from get-menu, e.g. '3619258212')","quantity":"int (optional, default 1)"}
Returns
{"ok":"bool","cartId":"string (server cart id) or null","item":"{ id, name, price } (price in the store currency, dollars)","quantity":"int","error":"string or null"}
SKILL.md90 lines

Intent

Add a menu item to the DoorDash cart by store id and item id. Navigates the store page (which loads the menu into the page's Apollo cache and establishes the cart session + CSRF cookie), reads the item's details from that cache, then calls DoorDash's addCartItem GraphQL mutation in-page (endpoint /graphql/addCartItem, field addCartItemV2) reusing the session cookies and the csrf_token cookie. Params: storeId, itemId (both from the doordash.com/search-stores / get-menu skills), quantity (default 1). Returns {ok, cartId, item:{id, name, price}, quantity, error}. This is a WRITE action: it modifies the user's real DoorDash cart (reversible: the item can be removed; it does NOT place an order or pay).

Checkout URL

The returned cartId is DoorDash's order_cart_id. Build the checkout page URL directly from it: https://www.doordash.com/consumer/checkout/?order_cart_id={{cartId}} (DoorDash fills lat/lng from the session). Opening it lands on the checkout screen with the cart ready. It does NOT place the order.

Scope / limitations

  • Handles simple items that add with their default options (DoorDash "quick add" items, and items whose required choices have defaults). It sends the item's default nestedOptions from the menu cache.
  • Items that require the user to choose options (size, protein, etc.) with no default cannot be added this way: the mutation returns an error; add those through the DoorDash UI instead. The skill surfaces the error in error.

Preconditions

  • DoorDash is behind Cloudflare; run this in the user's own logged-in/normal browser profile (a flagged automation profile gets stuck on the "Verifying you are human" challenge).
  • The item id must belong to the given store's currently loaded menu (run get-menu first, or pass ids from it). Cart contents reflect the session's delivery address.

Execute

  1. navigate → https://www.doordash.com/store/{{storeId}}/
  2. Wait ~3s for the menu to load (Apollo fetches it after hydration).
  3. evaluate the Extractor below with params. It reads the item from the Apollo cache and POSTs the add-to-cart mutation, returning the JSON in returns.

Extractor

( async (root, params) => { const client = window.APOLLO_CLIENT; if (!client || !client.cache) return { ok: false, cartId: null, item: null, quantity: 0, error: 'apollo_client_unavailable' }; const data = client.cache.extract(); const deref = (o) => (o && o.__ref) ? data[o._ref] : o; const rq = data.ROOT_QUERY || {}; const feedKey = Object.keys(rq).find((k) => k.indexOf('storepageFeed') === 0); if (!feedKey) return { ok: false, cartId: null, item: null, quantity: 0, error: 'menu_not_loaded' }; let menuId = ''; try { menuId = String(JSON.parse(feedKey.slice('storepageFeed('.length, -1)).menuId || ''); } catch (e) {} const feed = rq[feedKey]; const wantId = String((params && params.itemId) || ''); let found = null; for (const c0 of (feed.itemLists || [])) { const c = deref(c0) || {}; for (const it0 of (c.items || [])) { const it = deref(it0); if (it && String(it.id) === wantId) { found = it; break; } } if (found) break; } if (!found) return { ok: false, cartId: null, item: null, quantity: 0, error: 'item_not_found_in_menu' }; const qty = Math.max(1, parseInt((params && params.quantity) || 1, 10) || 1); const qa = found.quickAddContext || {}; const price = qa.price || {}; const unitPrice = (typeof price.unitAmount === 'number') ? price.unitAmount : 0; const input = { bundleType: 'BUNDLE_TYPE_UNSPECIFIED', cartId: '', currency: price.currency || 'USD', isBundle: false, itemDescription: found.description || '', itemId: wantId, itemName: found.name || '', menuId: menuId, nestedOptions: qa.nestedOptions || '[]', quantity: qty, specialInstructions: '', storeId: String((params && params.storeId) || found.storeId || ''), substitutionPreference: 'substitute', unitPrice: unitPrice }; const variables = { addCartItemInput: input, cartContext: { isBundle: false }, fulfillmentContext: { fulfillmentType: 'Delivery', shouldUpdateFulfillment: false }, monitoringContext: { isGroup: false }, lowPriorityBatchAddCartItemInput: [], returnCartFromOrderService: false, shouldKeepOnlyOneActiveCart: false }; const query = 'mutation addCartItem($addCartItemInput: AddCartItemInput!, $fulfillmentContext: FulfillmentContextInput!, $cartContext: CartContextInput, $returnCartFromOrderService: Boolean, $monitoringContext: MonitoringContextInput, $lowPriorityBatchAddCartItemInput: [AddCartItemInput!], $shouldKeepOnlyOneActiveCart: Boolean) { addCartItemV2(addCartItemInput: $addCartItemInput, fulfillmentContext: $fulfillmentContext, cartContext: $cartContext, returnCartFromOrderService: $returnCartFromOrderService, monitoringContext: $monitoringContext, lowPriorityBatchAddCartItemInput: $lowPriorityBatchAddCartItemInput, shouldKeepOnlyOneActiveCart: $shouldKeepOnlyOneActiveCart) { id } }'; const csrf = (document.cookie.match(/(?:^|;\s*)csrf_token=([^;]+)/) || [])[1] || ''; let res, txt, json = null; try { res = await fetch('/graphql/addCartItem?operation=addCartItem', { method: 'POST', credentials: 'include', headers: { 'content-type': 'application/json', 'accept': '/', 'x-csrftoken': decodeURIComponent(csrf), 'apollographql-client-name': '@doordash/app-consumer-production-ssr-client', 'apollographql-client-version': '3.0', 'x-experience-id': 'doordash', 'x-channel-id': 'marketplace' }, body: JSON.stringify({ operationName: 'addCartItem', variables: variables, query: query }) }); txt = await res.text(); try { json = JSON.parse(txt); } catch (e) {} } catch (e) { return { ok: false, cartId: null, item: { id: wantId, name: found.name || null, price: unitPrice / 100 }, quantity: qty, error: 'request_failed: ' + String(e) }; } const cartId = (json && json.data && json.data.addCartItemV2 && json.data.addCartItemV2.id) || null; const err = (json && json.errors && json.errors[0] && json.errors[0].message) || (res && !res.ok ? ('http' + res.status) : null); return { ok: !!cartId, cartId: cartId, item: { id: wantId, name: found.name || null, price: unitPrice / 100 }, quantity: qty, error: cartId ? null : (err || 'unknown_error') }; } )

Success assertion

{
  "expr": "typeof result === 'object' && result.ok === true && typeof result.cartId === 'string'",
  "type": "value"
}