PageSourceSearch

https://stream-partner-docs.vercel.app/assets/js/d72d961a.214be350.js

js stream-partner-docs.vercel.app collected 2026-10-03 07:50:03 UTC 17,521 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[8590],{3905:(e,t,n)=>{n.d(t,{Zo:()=>m,kt:()=>h});var a=n(7294);function i(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function o(e){for(var t=1;t<arguments.length;t++){var n=null!=arguments[t]?arguments[t]:{};t%2?r(Object(n),!0).forEach((function(t){i(e,t,n[t])})):Object.getOwnPropertyDescriptors?Object.defineProperties(e,Object.getOwnPropertyDescriptors(n)):r(Object(n)).forEach((function(t){Object.defineProperty(e,t,Object.getOwnPropertyDescriptor(n,t))}))}return e}function l(e,t){if(null==e)return{};var n,a,i=function(e,t){if(null==e)return{};var n,a,i={},r=Object.keys(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||(i[n]=e[n]);return i}(e,t);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(a=0;a<r.length;a++)n=r[a],t.indexOf(n)>=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(i[n]=e[n])}return i}var s=a.createContext({}),u=function(e){var t=a.useContext(s),n=t;return e&&(n="function"==typeof e?e(t):o(o({},t),e)),n},m=function(e){var t=u(e.components);return a.createElement(s.Provider,{value:t},e.children)},d="mdxType",p={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},c=a.forwardRef((function(e,t){var n=e.components,i=e.mdxType,r=e.originalType,s=e.parentName,m=l(e,["components","mdxType","originalType","parentName"]),d=u(n),c=i,h=d["".concat(s,".").concat(c)]||d[c]||p[c]||r;return n?a.createElement(h,o(o({ref:t},m),{},{components:n})):a.createElement(h,o({ref:t},m))}));function h(e,t){var n=arguments,i=t&&t.mdxType;if("string"==typeof e||i){var r=n.length,o=new Array(r);o[0]=c;var l={};for(var s in t)hasOwnProperty.call(t,s)&&(l[s]=t[s]);l.originalType=e,l[d]="string"==typeof e?e:i,o[1]=l;for(var u=2;u<r;u++)o[u]=n[u];return a.createElement.apply(null,o)}return a.createElement.apply(null,n)}c.displayName="MDXCreateElement"},1805:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>d,frontMatter:()=>r,metadata:()=>l,toc:()=>u});var a=n(7462),i=(n(7294),n(3905));const r={sidebar_position:3,title:"Menu Ingestion",toc_min_heading_level:2,toc_max_heading_level:6},o=void 0,l={unversionedId:"become-a-stream-integration/integrate-with-stream-as-a-dsp/integration-steps/menu-ingestion",id:"become-a-stream-integration/integrate-with-stream-as-a-dsp/integration-steps/menu-ingestion",title:"Menu Ingestion",description:"As a Stream DSP, merchants will be able to publish their menus and schedules to your platform.",source:"@site/docs/become-a-stream-integration/integrate-with-stream-as-a-dsp/integration-steps/menu-ingestion.md",sourceDirName:"become-a-stream-integration/integrate-with-stream-as-a-dsp/integration-steps",slug:"/become-a-stream-integration/integrate-with-stream-as-a-dsp/integration-steps/menu-ingestion",permalink:"/docs/become-a-stream-integration/integrate-with-stream-as-a-dsp/integration-steps/menu-ingestion",draft:!1,tags:[],version:"current",sidebarPosition:3,frontMatter:{sidebar_position:3,title:"Menu Ingestion",toc_min_heading_level:2,toc_max_heading_level:6},sidebar:"defaultSidebar",previous:{title:"Location Management",permalink:"/docs/become-a-stream-integration/integrate-with-stream-as-a-dsp/integration-steps/location-management"},next:{title:"Menu Schemas",permalink:"/docs/become-a-stream-integration/integrate-with-stream-as-a-dsp/integration-steps/menu-schemas"}},s={},u=[{value:"Requesting a Menu Refresh",id:"requesting-a-menu-refresh",level:2},{value:"Menu Contents",id:"menu-contents",level:2},{value:"PublishMenusPayload",id:"publishmenuspayload",level:4},{value:"Schedule Management",id:"schedule-management",level:4},{value:"Schedule Exceptions (Holidays)",id:"schedule-exceptions-holidays",level:4},{value:"Taxes",id:"taxes",level:4},{value:"Modifier Group Rules",id:"modifier-group-rules",level:4},{value:"Feature Flags",id:"feature-flags",level:2},{value:"Active / Inactive Item and Modifier Handling",id:"active--inactive-item-and-modifier-handling",level:3},{value:"Default Behavior",id:"default-behavior",level:4},{value:"Configurable Behavior for Inactive Items and Modifiers",id:"configurable-behavior-for-inactive-items-and-modifiers",level:4},{value:"Menu Schema V2",id:"menu-schema-v2",level:3},{value:"Key Changes",id:"key-changes",level:4},{value:"Menu Object Updates",id:"menu-object-updates",level:3}],m={toc:u};
1function d(e){let{components:t,...n}=e;return(0,i.kt)("wrapper",(0,a.Z)({},m,n,{components:t,mdxType:"MDXLayout"}),(0,i.kt)("p",null,"As a Stream DSP, merchants will be able to publish their menus and schedules to your platform."),(0,i.kt)("p",null,"When this happens, we will call the ",(0,i.kt)("a",{parentName:"p",href:"/api/become-a-dsp#operation/AppController_dspHandleEvent"},"New Webhook Event")," endpoint with an event type of ",(0,i.kt)("inlineCode",{parentName:"p"},"location.publish"),". You will need to retrieve the data from ",(0,i.kt)("inlineCode",{parentName:"p"},"download_url")," for the menus and schedules."),(0,i.kt)("p",null,"If the changes originate from the POS, they will automatically sync to your platform. However, if a user makes changes within Stream using Stream's UI, those changes will remain unpublished until the user manually clicks Publish."),(0,i.kt)("h2",{id:"requesting-a-menu-refresh"},"Requesting a Menu Refresh"),(0,i.kt)("p",null,"If your platform falls out of sync with the merchant's menu in Stream \u2014 for example, after recovering from an outage on your side, or whenever you detect that a previously delivered menu is stale \u2014 you can request that Stream re-publish the latest menu for a location instead of asking the merchant to manually re-publish from the Stream Website."),(0,i.kt)("p",null,"To request a refresh, send a ",(0,i.kt)("inlineCode",{parentName:"p"},"menu_refresh")," event to the ",(0,i.kt)("a",{parentName:"p",href:"/api/become-a-dsp#operation/WebhookController_streamHandleEvent"},"DSP \u2013> Stream via Webhook")," endpoint with the affected ",(0,i.kt)("inlineCode",{parentName:"p"},"location_id"),". Stream will then re-deliver the latest menu via a fresh ",(0,i.kt)("inlineCode",{parentName:"p"},"location.publish")," webhook to your platform, exactly as if the merchant had triggered the publish themselves."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n  "type": "menu_refresh",\n  "location_id": "<dsp_location_id>"\n}\n')),(0,i.kt)("h2",{id:"menu-contents"},"Menu Contents"),(0,i.kt)("h4",{id:"publishmenuspayload"},"PublishMenusPayload"),(0,i.kt)("p",null,"The data within the S3 file has now been defined in our OpenAPI specification, allowing for more accurate types. ",(0,i.kt)("a",{parentName:"p",href:"/api/become-a-dsp#operation/AppController_getMenuFileContents"},"Click Here")),(0,i.kt)("h4",{id:"schedule-management"},"Schedule Management"),(0,i.kt)("p",null,"Each menu has its own schedule, represented by the ",(0,i.kt)("inlineCode",{parentName:"p"},"Schedule")," entity above. These will be provided in the ",(0,i.kt)("inlineCode",{parentName:"p"},"schedule")," field of each ",(0,i.kt)("inlineCode",{parentName:"p"},"Menu"),". This allows each individual menu to have specific availablity hours. The example below contains a schedule with hours of 9 am to 4 pm on weekdays, and 8 am to 6 pm on weekends."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'{\n  ...\n  schedule: {\n    monday: ["9:00-16:00"],\n    tuesday: ["9:00-16:00"],\n    wednesday: ["9:00-16:00"],\n    thursday: ["9:00-16:00"],\n    friday: ["9:00-16:00"],\n    saturday: ["8:00-18:00"],\n    sunday: ["8:00-18:00"]\n  }\n}\n')),(0,i.kt)("h4",{id:"schedule-exceptions-holidays"},"Schedule Exceptions (Holidays)"),(0,i.kt)("p",null,"Holiday information that overrides the schedule for the ",(0,i.kt)("strong",{parentName:"p"},"location")," will be provided in the ",(0,i.kt)("inlineCode",{parentName:"p"},"holidays")," property of the payload."),(0,i.kt)("p",null,"Each holiday has a ",(0,i.kt)("inlineCode",{parentName:"p"},"date")," represented in the ",(0,i.kt)("inlineCode",{parentName:"p"},"MM-DD")," format, and a flag ",(0,i.kt)("inlineCode",{parentName:"p"},"recurring_yearly")," that indicates if this holiday repeats every year. If ",(0,i.kt)("inlineCode",{parentName:"p"},"recurring_yearly")," is false, the ",(0,i.kt)("inlineCode",{parentName:"p"},"specific_years")," array will contain the specific years that the holiday occurs on."),(0,i.kt)("p",null,"Finally, the ",(0,i.kt)("inlineCode",{parentName:"p"},"hours")," array contains the specifc timeslots that the location is open for this holiday. If ",(0,i.kt)("inlineCode",{parentName:"p"},"hours")," is omitted, the location is closed for the entire day."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'{\n  ...\n  holidays: [\n    /** Annual closures / hour updates */\n    {\n      date: "12-25",\n      recurring_yearly: true,\n      name: "Christmas Day"\n    },\n    {\n      date: "01-01",\n      recurring_yearly: true,\n      name: "New Year\'s Day",\n      hours: ["9:00-17:00"]\n    },\n    /** One-off holidays/closure */\n    {\n      date: "11-28",\n      recurring_yearly: false,\n      specific_years: [2024],\n      name: "Thanksgiving Day",\n    }\n  ]\n}\n')),(0,i.kt)("h4",{id:"taxes"},"Taxes"),(0,i.kt)("p",null,"We will provide tax rate information assigned the item level, allowing for things like exemptions, inclusive rates and more. Below are some examples of how this data can be provided."),(0,i.kt)("p",null,"Here is a default tax rate across the menu, with some items excluded from the rate."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'{\n  ...\n  item: [\n    {\n      stream_id: "item_with_tax",\n      tax_ids: ["tax_1234"],\n      ...\n    },\n    {\n      stream_id: "item_without_tax",\n      tax_ids: [],\n      ...\n    }\n  ]\n  taxes: [\n    /** Annual closures / hour updates */\n    {\n      is_inclusive: false,\n      stream_id: "tax_1234",\n      rate: 7.5, /** 7.5% */\n      is_default: true,\n      name: "Sales tax"\n    }\n  ]\n}\n')),(0,i.kt)("p",null,"Here is an example with multiple tax rates across the menu, with certain items having certain rates."),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'{\n  ...\n  item: [\n    {\n      stream_id: "item_with_tax",\n      tax_ids: ["tax_1234"],\n      ...\n    },\n    {\n      stream_id: "item_with_added_excise",\n      tax_ids: ["tax_1234", "tax_5678"],\n      ...\n    }\n  ]\n  taxes: [\n    /** Annual closures / hour updates */\n    {\n      is_inclusive: false,\n      stream_id: "tax_1234",\n      rate: 7.5, /** 7.5% */\n      is_default: true,\n      name: "Sales tax"\n    },\n    {\n      is_inclusive: false,\n      stream_id: "tax_5678",\n      rate: 2.5, /** 2.5% */\n      is_default: false,\n      name: "Excise tax"\n    }\n  ]\n}\n')),(0,i.kt)("h4",{id:"modifier-group-rules"},"Modifier Group Rules"),(0,i.kt)("p",null,"Modifier group rules are a way to group modifiers together and apply rules to them."),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"selection_type")," (",(0,i.kt)("inlineCode",{parentName:"p"},"single")," or ",(0,i.kt)("inlineCode",{parentName:"p"},"multiple"),') determines what "unique" means for this group\'s counts:'),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"strong"},"single"))," (or when the field is omitted): each selection must be a distinct modifier. ",(0,i.kt)("inlineCode",{parentName:"li"},"minimum_unique_modifiers_allowed")," / ",(0,i.kt)("inlineCode",{parentName:"li"},"maximum_unique_modifiers_allowed")," count distinct options, so a value of 2 requires 2 different modifiers."),(0,i.kt)("li",{parentName:"ul"},(0,i.kt)("strong",{parentName:"li"},(0,i.kt)("inlineCode",{parentName:"strong"},"multiple")),": these fields validate ",(0,i.kt)("em",{parentName:"li"},"total quantity selected")," only, with no distinctness requirement \u2014 the same modifier may be selected more than once and counts each time toward the minimum/maximum.")),(0,i.kt)("p",null,(0,i.kt)("inlineCode",{parentName:"p"},"minimum_unique_modifiers_allowed")," is the minimum number of modifiers that can be selected from the group (see ",(0,i.kt)("inlineCode",{parentName:"p"},"selection_type")," above for what counts toward this).\n",(0,i.kt)("inlineCode",{parentName:"p"},"maximum_unique_modifiers_allowed")," is the maximum number of modifiers that can be selected from the group (see ",(0,i.kt)("inlineCode",{parentName:"p"},"selection_type")," above for what counts toward this).\n",(0,i.kt)("inlineCode",{parentName:"p"},"amount_of_modifiers_free")," is the amount of free modifiers that can be selected from the group before a charge is applied.\n",(0,i.kt)("inlineCode",{parentName:"p"},"default_modifier_quantities")," is an object that maps modifier ids to their default selection quantity, allowing for defaulting of modifiers"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-js"},'{\n  selection_type: "multiple", // "single" (or omitted) requires distinct options; "multiple" allows repeats\n  minimum_unique_modifiers_allowed: 1,\n  maximum_unique_modifiers_allowed: 2,\n  amount_of_modifiers_free: 1,\n  default_modifier_quantities: {\n    "modifier_1": 1, // default selection quantity\n    "modifier_2": 0 // can be omitted or set to 0, indicating that the modifier is not defaulted\n  }\n}\n')),(0,i.kt)("h2",{id:"feature-flags"},"Feature Flags"),(0,i.kt)("p",null,"The following are features that can be configured and unique to your integration. Please reach out to Stream if you would like to have access to and implement the following features."),(0,i.kt)("p",null,"For the complete list of optional capabilities across every integration type, see ",(0,i.kt)("a",{parentName:"p",href:"/docs/become-a-stream-integration/integrate-with-stream-as-a-dsp/feature-flags"},"DSP Feature Flags"),"."),(0,i.kt)("h3",{id:"active--inactive-item-and-modifier-handling"},"Active / Inactive Item and Modifier Handling"),(0,i.kt)("h4",{id:"default-behavior"},"Default Behavior"),(0,i.kt)("p",null,"If an item family, item, or modifier is marked as inactive in Stream, either by the user or the POS system, the menu sent to the DSP will automatically exclude all inactive items. Items, item families, and modifiers will always be sent with ",(0,i.kt)("inlineCode",{parentName:"p"},"is_active")," equal to ",(0,i.kt)("inlineCode",{parentName:"p"},"true"),"."),(0,i.kt)("p",null,"Example"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n  "_id": "item_1",\n  "name": "Water Bottle",\n  "price_amount": 100,\n  "price_currency": "usd",\n  "is_active": true\n}\n')),(0,i.kt)("h4",{id:"configurable-behavior-for-inactive-items-and-modifiers"},"Configurable Behavior for Inactive Items and Modifiers"),(0,i.kt)("p",null,"A DSP can opt in if they would like to recieve inactive data for item family, item, and/or modifier for a recieved menu. The main benefits of this feature include:"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"When you 86 an item and publish the menu (removing the item from the new publish), resuming the item later requires a full menu re-publish to add it back. To avoid unnecessary menu rebuilds, this feature allows inactive items to be included in the initial menu publish. This way, future 86'ing of items can be handled by a targeted itemized update, reducing the need for full menu publishes."),
1(0,i.kt)("li",{parentName:"ul"},"You may have an UI/UX that shows the consumer that an item does exist on the menu, but is currently unavailable.")),(0,i.kt)("p",null,"Example"),(0,i.kt)("pre",null,(0,i.kt)("code",{parentName:"pre",className:"language-json"},'{\n  "_id": "item_1",\n  "name": "Water Bottle 16oz",\n  "price_amount": 100,\n  "price_currency": "usd",\n  "is_active": true\n}\n{\n  "_id": "item_2",\n  "name": "Water Bottle 24oz",\n  "price_amount": 175,\n  "price_currency": "usd",\n  "is_active": false\n}\n')),(0,i.kt)("h3",{id:"menu-schema-v2"},"Menu Schema V2"),(0,i.kt)("p",null,"The v2 menu schema provides an optimized data structure that significantly reduces payload size through improved data associations. Contact support to enable v2 schema access when building to the API."),(0,i.kt)("h4",{id:"key-changes"},"Key Changes"),(0,i.kt)("ul",null,(0,i.kt)("li",{parentName:"ul"},"Better alignment with Stream catalog standard"),(0,i.kt)("li",{parentName:"ul"},"Implemented association-based data structure",(0,i.kt)("ul",{parentName:"li"},(0,i.kt)("li",{parentName:"ul"},"Better insight into relation based overrides"),(0,i.kt)("li",{parentName:"ul"},"Reduced overall payload size")))),(0,i.kt)("h3",{id:"menu-object-updates"},"Menu Object Updates"),(0,i.kt)("p",null,"A DSP can also opt-in to be notified of object-level updates to items, variations, and modifiers in favor of a full menu publish."),(0,i.kt)("p",null,(0,i.kt)("a",{parentName:"p",href:"/docs/become-a-stream-integration/integrate-with-stream-as-a-dsp/integration-steps/menu-object-updates"},"See more")))}d.isMDXComponent=!0}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.