SimphonyPOS DataStream (Oracle Simphony)

SimphonyPOS DataStream (Oracle Simphony)

Written by AI, not reviewed by a human. Please apply the written-by-ai-not-reviewed label. The contract below comes from Oracle's published documentation — no live Simphony account has been exercised.

Integrates Oracle Simphony as a NexSigns DataStream source, so a Digital Menu Board can bind to a property's live menu.

Source name

SimphonyPOS

Vendor API

Simphony Configuration and Content API ("ccapi") — not Transaction Services

Docs

REST endpoints; auth is documented separately under Transaction Services Gen 2 and applies to all Oracle Restaurants APIs

Status

CODE COMPLETE, UNVERIFIED LIVE

Why this one is different

Simphony is the only one of the fourteen POS sources whose native data model is already "item with sizes". A menu item master is the logical item ("Coffee") and owns up to 64 definitions ("Small", "Medium", "Large"), each with its own price records. Every other source needs a heuristic to guess which modifier group represents size; Simphony needs none, and sizes come out in the operator's own defSequenceNum order — Small before Medium before Large, not alphabetical.

It also has the most intricate authentication of the six, and the one most likely to cause an operational incident if handled carelessly. See Authentication below.

Configuration

Handed to the customer verbatim in the Data Streams "Add" dialog:

{ "DataSource": "SimphonyPOS", "Version": "1.0", "Host": "<Replace Simphony Host e.g. https://xxxxx-ohsim.oracleindustry.com>", "OrgShortName": "<Replace Organization Short Name>", "Username": "<Replace API Account Username>", "Password": "<Replace API Account Password>", "ClientID": "<Replace OAuth Client ID>", "HierUnitID": "<Replace Hierarchy Unit ID>", "Locale": "en-US", "PriceSequenceNum": 1, "PriceDecimalPlaces": 2, "Currency": "", "IncludeConsumerContent": true }

Field

Meaning

Field

Meaning

Host

Every Simphony customer is on their own Oracle host. A pasted value with or without the scheme, and with or without a trailing path, is normalized down to the origin.

OrgShortName

The organization short name, sent on the sign-in step.

Username / Password

The Simphony API account. The password is never written to the refresh log, event log, an exception message or a query string — not even as a fingerprint.

ClientID

The OAuth 2.0 client identifier Oracle issued. Logged only as ****xxxx.

HierUnitID

Scopes every read to one property. Numeric. Blank is legal and returns the whole enterprise — on a multi-property customer that is both slow and wrong for a single board.

Locale

Which language to take from Simphony's name maps. Default en-US.

PriceSequenceNum

Which menu level's price to show. 1 is the base price by universal Simphony convention; higher sequences are happy hour, lunch and so on.

PriceDecimalPlaces

The Configuration API exposes no currency code on any menu resource, so decimals are configured rather than derived.

Currency

Display only. Nothing in the price path reads it.

IncludeConsumerContent

Prefer the guest-facing consumerItemName / consumerItemDescription / consumerName over the till-facing firstName / name. On by default — that preference is what a menu board is for.

A helper endpoint lists the property's category structure so a customer can see what the board's grouping will look like before building a design:

GET /api/nexsigns/DataStreams/GetSimphonyPOSAvailableFamilyGroupList?DataStreamGUID=...

It is keyed on DataStreamGUID rather than taking credentials as parameters, so no secret ever rides in a query string.

Authentication

Simphony locks an API account for 30 minutes after repeated failed sign-ins, and API account passwords expire every 60 days. The integration caches its token for 14 days specifically so the password crosses the wire as rarely as possible. Do not "simplify" the token caching away, and do not debug a credential problem by retrying in a loop.

Three steps, OpenID Connect with PKCE:

  1. GET /oidc-provider/v1/oauth2/authorize — establishes the session cookie.

  2. POST /oidc-provider/v1/oauth2/signin — username + password + orgname. Answers with the authorization code in a JSON body rather than by redirecting. That is what makes the whole flow drivable from a server with no browser.

  3. POST /oidc-provider/v1/oauth2/token — code + PKCE verifier, returns the tokens.

Three things are load-bearing and easy to lose:

  • The cookie jar. Step 2 only succeeds while carrying the cookies step 1 set. RestClientOptions.CookieContainer does not exist in RestSharp 109 — the version this solution is pinned to; it arrived in 110 — so the cookies are relayed by hand, with a parser tolerant enough to survive several Set-Cookie values merged into one header. Do not restore a CookieContainer without upgrading the package for every other consumer in the solution.

  • Redirects must not be followed. The target is apiaccount://callback, a custom scheme no HTTP stack can navigate to.

  • The bearer is the id_token, not the access_token. Oracle's own documentation says the access token is unused by Restaurants APIs. Picking the wrong one produces a 401 on every call with nothing to indicate why.

Token lifetimes: id_token 14 days, refresh_token 28 days. The integration renews 7 days early via the refresh grant — no password involved — and only falls back to a full sign-in if that fails. Both tokens live in DataStream.TempIntegratorData, scoped by client ID + host + username so changing any of the three invalidates the cache.

API contract

Every read is a POST. That is not a mistake in the code — Oracle models reads as POST bodies (getMenuItems, getMenuItemPrices, …) so that searchCriteria, includeAll and the paging fields have somewhere to live.

Call

Supplies

Call

Supplies

POST /config/sim/v1/menuItems/getMajorGroups

parent category names

POST /config/sim/v1/menuItems/getFamilyGroups

the board's categories, plus consumer names/descriptions and images

POST /config/sim/v1/menuItems/getMenuItemMasters

the logical items — name, objectNum, family/major group refs, dietary labels

POST /config/sim/v1/menuItems/getMenuItemDefinitions

the sizes — names, descriptions, images, defSequenceNum

POST /config/sim/v1/menuItems/getMenuItemPrices

one price record per definition per menu level

Paging is offset/limit with an authoritative hasMore flag; 500 rows per page, capped at 20 pages per resource. includeAll: "detailed" is sent because "basic" omits every extended value and would leave a board with names and prices only.

Prices are decimal major units8.49 means eight dollars forty-nine. No division anywhere. (That matches NCRPOS and RevelPOS, and is the opposite of SquarePOS, CloverPOS and PARPOS.)

How the menu is normalized

{ "source": "SimphonyPOS", "schema_version": 1, "host": "...", "hier_unit_id": "1012", "location_name": "", "locale": "en-US", "price_sequence_num": 1, "currency": "", "price_decimal_places": 2, "categories": [ { "id", "name", "description", "parent_id", "parent_name", "sort_order", "image" } ], "items": [ { "id", "name", "description", "object_num", "category_ids": [], "image", "thumbnail", "alt_text", "dietary_labels", "prep_time", "sold_out", "variations": [ { "id", "name", "definition_id", "def_sequence_num", "price_amount", "menu_level", "sold_out", "is_default", "source" } ] } ] }

Notable projection rules:

  • Categories are family groups with their major group as the parent, rendered in the picker as "Major Group / Family Group".

  • The item's name, description and image come from the first DEFINITION, not the master. That is where Simphony actually keeps them — a master carries only a name and its group references — so reading them off the master would leave every board row without a picture or a description.

  • A master with no definition at this hierarchy unit is dropped. It is a shell inherited from a higher level and is not sellable here.

  • When a definition has no price for the configured PriceSequenceNum, the lowest sequence it does have is used rather than an arbitrary one.

  • Images are chosen by lowest imageIndex so the choice does not depend on array order — the change-detection hash runs over this document.

Data fields available in the Smart Menu Editor

Name, Definition Name (Size), Price, Description, Image URL, Thumbnail URL, Image Alt Text, Object Number, Dietary Labels, Prep Time, Menu Level, Definition Sequence, Inventory Status, Item ID, Currency.

Inventory Status always reads "available". The Configuration and Content API is exactly that — configuration — and carries no availability, 86 list or stock count on any menu resource. The field is implemented rather than omitted so a design carried over from ToastPOS or CloverPOS keeps working.

No calories. Nutrition lives on Simphony's separate nutrient sets, which the menu resources only reference by object number and which this integration does not fetch. A calories binding resolves to a blank, not an error token.

Multi-property designs

A binding records the item's objectNum and name into its fallback key. That is the right choice for a chain: menuItemMasterId is a surrogate that differs per property, whereas objectNum is the number the operator assigns and, in an enterprise configured top-down, is identical everywhere. So one design authored at one property resolves correctly at the others.

Troubleshooting

Symptom

Likely cause

Symptom

Likely cause

"signin was rejected"

Wrong username / password / org short name — or the account is locked for 30 minutes after earlier failures, or the password has hit its 60-day expiry. Reset it via the Reporting and Analytics portal.

"the token response carried no id_token"

The client ID is wrong, or the OAuth client is not configured for the authorization-code + PKCE flow.

Sync succeeds but keeps zero items

HierUnitID points at a level that has masters but no definitions. Definitions are what carry names and prices. The refresh log says this explicitly.

Every price resolves {E2}

PriceSequenceNum points at a menu level these items do not participate in. Try 1.

Paging exceeded the budget

HierUnitID is blank on a multi-property enterprise, so every property's rows are being read. Set it.

Names in the wrong language

Locale does not match what the property populates. The fallback is deliberately the first populated entry rather than a blank — an unnamed row is worse than a wrong-language one.

Known gaps

  • No availability of any kind (see above).

  • No nutrition / calories.

  • Condiments, combo meals and menu item groups are fetched by neither the sync nor the projection — only masters, definitions, prices and the two group levels.

  • location_name is projected as a blank pending a hierarchy-unit lookup.

  • No webhook — the menu refreshes on the hourly interval.

Verification status

Done: builds clean (0 errors); the editor partial parsed with the real Razor engine (0 parser errors) and verified pure ASCII; every contract above traced to Oracle's published documentation.

Not done: no live Simphony call; no offline assertion harness; no end-to-end walkthrough in the running app.

Attempt Simphony end to end early. Of the six integrations this is the one with the most argued-from-documentation surface — the PKCE sign-in, the hand-rolled cookie relay and the id_token-not-access_token choice are all unverified against a real host. Budget for the 30-minute account lockout while debugging.