Skip to main content

How to Manage Retail Items via API

Learn how to synchronise merchandise, food and beverage, or other non-ticket inventory with Ventrata.

This guide is for developers maintaining an external retail system that synchronises merchandise, food and beverage, or other non-ticket inventory with Ventrata.

This guide covers:

📒 NOTE

The examples use https://api.ventrata.com/octo as the OCTO base URL. Use the endpoint and API token supplied for your connection.


Obtain Connection API Key

Locate your API key in the Ventrata Dashboard.

Prerequisites:

  • Contact your Ventrata Customer Success Manager or the Connectivity team to create a connection in 'Backoffice' mode for you in the Ventrata Dashboard.

  • The connection must have:

    • read permission for catalogue and tax-rate requests

    • write permission for create, update and delete requests

  1. In your Ventrata Dashboard, go to Connections > Connections.

    Connections

  2. Locate and select the relevant connection.

    Connection

    📗 TIP

    The Mode column shows dashboard for this connection type.

  3. On the connection detail, press the eye icon to reveal your API key.

  4. Copy the API key and send it in the Authorization: Bearer <API_TOKEN> header.

    ‼️ IMPORTANT

    Your API key is a confidential credential that provides access to your Ventrata account. Never share it over chat or email, and do not put it in source control, support tickets, or application logs.


Required Headers

Send the required headers with every request.

Header

Description

Authorization: Bearer <API_TOKEN>

(required) authenticates the request

Octo-Capabilities: ventrata/dashboard

(required) enables the item management capability

📒 NOTE

When a purchase includes an external discount (offerDiscount), also add the octo/offers capability:

Octo-Capabilities: ventrata/dashboard,octo/offers

Content-Type: application/json

(required) indicates a JSON payload

Octo-Fields

(optional) controls which fields are returned; supports nested selections, for example, {id,internalName,destination{id,title}}


Inventory Management Endpoints

Endpoints are relative to the base URL, for example: https://api.ventrata.com/octo.

Operation

Method

Endpoint

List assignable tax rates

GET

/ventrata/dashboard/tax_rates

Retrieve one assignable tax rate

GET

/ventrata/dashboard/tax_rates/{taxRateId}

List or retrieve selected items

GET

/items

Create an item

POST

/ventrata/dashboard/items

Partially update an item

PATCH

/ventrata/dashboard/items/{itemId}

Delete an item

DELETE

/ventrata/dashboard/items/{itemId}

Create a purchase and optionally create or update its item just in time

POST

/purchases

📒 NOTE

Catalogue reads use the existing OCTO Retail endpoint. There are no separate Dashboard list or get-item endpoints.


Discover Assignable Tax Rates

List the tax rates available to the connection before you create or update taxable items. Use the Octo-Fields header to choose which fields are returned.

Field

Type

Description

id

UUID

UUID of the tax rate

internalName

string

canonical name of the tax rate

title

string

name of the tax rate

subtitle

string

optional description

amount

number (percentage) /

integer (fixed rates)

  • percentage: true: the percentage value, for example, 20.0

  • percentage: false: an integer in the currency's minor unit, for example, 250 for £2.50

percentage

boolean

true: amount is a percentage

false: amount is a fixed amount in minor units

rounded

boolean

true: after a percentage tax rate is applied, the total is rounded to the nearest tenth

false: the total is not rounded

currency

string

currency of a fixed tax rate, null for percentage tax rates

tags

string array

tags assigned to the tax rate

Example request

This is a GET request to list all tax rates available to the connection.

curl --get 'https://api.ventrata.com/octo/ventrata/dashboard/tax_rates' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard' \
--header 'Octo-Fields: {id,internalName,title,subtitle,amount,percentage,rounded,currency,tags}'

Example response (percentage tax rate)

{
"id": "<TAX_RATE_UUID>",
"internalName": "Standard VAT",
"title": "Standard VAT",
"subtitle": "Standard retail VAT",
"amount": 20.0,
"percentage": true,
"rounded": false,
"currency": null,
"tags": ["retail"]
}


Retrieve a Known Tax Rate

Retrieve a single tax rate by its UUID using the /ventrata/dashboard/tax_rates/{taxRateId} endpoint.

Example request

curl --get 'https://api.ventrata.com/octo/ventrata/dashboard/tax_rates/<TAX_RATE_UUID>' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard' \
--header 'Octo-Fields: {id,internalName,title,subtitle,amount,percentage,rounded,currency,tags}'


Read the Item Catalogue

List the items in the catalogue and store each item's Ventrata id as the durable mapping key in your external system. For the list of fields you can request with the Octo-Fields header, see Item Response Fields.

Example request

This is a GET request to list items available to the connection.

curl --get 'https://api.ventrata.com/octo/items' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard' \
--header 'Octo-Fields: {id,internalName,reference,billingAccountCode,destination{id,title},active,customRetail,retail,customCost,cost,currency,coverImageUrl,taxIncluded,taxRates{id,internalName,amount,percentage,currency}}'


Pagination

The item list supports the page and perPage query parameters. When pagination applies, the response includes the Octo-Total-Pages header.

Example request

curl --get 'https://api.ventrata.com/octo/items' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard' \
--data-urlencode 'page=2' \
--data-urlencode 'perPage=50'


Retrieve Known Items

To retrieve specific items by their Ventrata UUIDs, repeat the itemIds[] query parameter. This is an efficient way to reconcile items your system has already mapped.

Example request

curl --get 'https://api.ventrata.com/octo/items' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard' \
--data-urlencode 'itemIds[]=<ITEM_UUID_1>' \
--data-urlencode 'itemIds[]=<ITEM_UUID_2>'


Item Identifiers

Each item has several fields that identify or name it.

Field

Use it for

Notes

Editable

id

the durable mapping key in your external system

generated by Ventrata

N

reference

a human-readable external reference, for example, a barcode

maps to the 'SKU/Code' field in the Ventrata Dashboard;

used for just-in-time matching and can be used as {itemId} when updating or deleting an item

Y

billingAccountCode

an accounting reference

maps to the 'Billing Account Code' field in the Ventrata Dashboard;

independent of reference;

returned in item and purchase responses

Y

internalName

the item's name

maps to the translated item name in the Ventrata Dashboard

Y

title

a fallback for internalName when creating or updating an item

returns the same name as internalName

Y

📒 NOTE

Do not use reference as your system's only identity key as it is editable. Store the Ventrata id instead.


Create an Item

Create an item using one of two retail pricing models.

📒 NOTE

Cost is configured independently of the pricing mode; each model below shows both options. For all other item parameters, such as internalName, reference, billingAccountCode, destinationId, coverImageUrl, and taxRateIds, see Writable Item Parameters.

The response is the created item, filtered by Octo-Fields when supplied.

Fixed Price Managed in Ventrata

Use this model when Ventrata owns the item's selling price.

  • set customRetail to false, or omit it because false is the default

  • send retail on the item in integer minor units, for example, 2500 for £25.00

  • send taxIncluded to state whether that price includes tax

  • for a fixed cost, set customCost to false and send cost in integer minor units

Example request

This is a POST request to create a fixed-price item at £25.00.

The currency is based on the default currency set in the Supplier Settings.

curl --request POST 'https://api.ventrata.com/octo/ventrata/dashboard/items' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard' \
--header 'Content-Type: application/json' \
--data '{
"internalName": "Tatton guidebook",
"reference": "GUIDEBOOK",
"billingAccountCode": "GUIDEBOOK-001",
"category": "Books",
"destinationId": "<DESTINATION_UUID>",
"customRetail": false,
"retail": 2500,
"customCost": false,
"cost": 1000,
"coverImageUrl": "https://images.example.com/tatton-guidebook.jpg",
"taxIncluded": true,
"taxRateIds": ["<TAX_RATE_UUID>"],
"active": true
}'


Price Managed by the External System

Use this model when the external system owns the selling price and any discount applied at the point of sale. The item does not store a price. Instead, the external system sends the price with each purchase.

Step 1: Create the Item

  • set customRetail to true

  • do not send an item-level retail value

  • set customCost to true if the external system also supplies the cost with each purchase; otherwise set it to false and send a fixed cost

  • send taxIncluded to state whether the retail values sent with purchases include tax

📗 TIP

To have Ventrata enforce the prices the external system can send, define customRetailOptions or restrictions.minCustomRetail and restrictions.maxCustomRetail on the item.

Example request

This is a POST request to create an item with an externally managed price and cost.

curl --request POST 'https://api.ventrata.com/octo/ventrata/dashboard/items' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard' \
--header 'Content-Type: application/json' \
--data '{
"internalName": "Externally priced guidebook",
"reference": "GUIDEBOOK-CUSTOM",
"billingAccountCode": "GUIDEBOOK-CUSTOM-001",
"category": "Books",
"destinationId": "<DESTINATION_UUID>",
"customRetail": true,
"customCost": true,
"taxIncluded": true,
"taxRateIds": ["<TAX_RATE_UUID>"],
"active": true
}'


Step 2: Record a Purchase with Price, Cost, and Discount

Send the actual per-unit values with each purchase.

  • send retail, the per-unit gross selling price before discount

  • send cost when the item has customCost: true; it is required for these items and not applied to fixed-cost items

  • send the per-unit external discount as offerDiscount.retail, and include the octo/offers capability; omit offerDiscount when no external discount applies

Example request

This is a POST request to create a purchase with a £25.00 per-unit retail value, £10.00 per-unit cost, and a £5.00 per-unit external discount.

curl --request POST 'https://api.ventrata.com/octo/purchases' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard,octo/offers' \
--header 'Content-Type: application/json' \
--header 'Octo-Fields: {uuid,itemId,quantity,retail,cost}' \
--data '{
"itemId": "<ITEM_UUID>",
"quantity": 1,
"currency": "GBP",
"retail": 2500,
"cost": 1000,
"offerDiscount": {
"retail": 500
}
}'


Create an Item Just in Time with a Purchase

You do not need to create every item before sending purchases. A POST /purchases request with the ventrata/dashboard capability can include a nested item object containing the same writable parameters as POST /ventrata/dashboard/items.

When a purchase includes an item object, Ventrata resolves the item as follows:

  • If the request includes itemId, Ventrata uses that existing item.

  • If there is no itemId, Ventrata reuses an item available to the connection with the same non-empty reference.

  • If no item matches, Ventrata creates a new item from the item parameters.

‼️ IMPORTANT

The item is saved before the purchase. If the purchase then fails validation, the item create or update remains saved. Purchase preview requests containing a nested item also save that item create or update.

For the purchase parameters, including the item object, see Purchase Parameters.

Example request

This is a POST request that creates or reuses a custom-retail soft-drink item while recording a purchase with a £2.70 per-unit gross retail value and a £1.00 per-unit external discount.

curl --request POST 'https://api.ventrata.com/octo/purchases' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard,octo/offers' \
--header 'Content-Type: application/json' \
--header 'Octo-Fields: {uuid,itemId,quantity,retail,pricing{original,retail,offerDiscount{retail}}}' \
--data '{
"quantity": 1,
"currency": "GBP",
"retail": 270,
"offerDiscount": {
"retail": 100
},
"item": {
"internalName": "Soft drink",
"reference": "POS-SOFT-DRINK",
"billingAccountCode": "SOFT-DRINK-001",
"category": "Food and beverage",
"customRetail": true,
"taxIncluded": true,
"taxRateIds": ["<TAX_RATE_UUID>"],
"active": true
}
}'

In this example:

  • retail: 270 is the per-unit gross value before discount.

  • offerDiscount.retail: 100 is the per-unit discount supplied by the external system.

  • The resulting per-unit payable retail is £1.70.

The discount can come from a loyalty reward, a promotion, or another external pricing rule. Ventrata records the supplied split and does not recalculate it.

Because the nested item has customRetail: true, it has no item-level retail, and the actual price is sent on the purchase. If the nested item has customCost: true, also send the per-unit cost on the purchase.

The purchase response returns the item's Ventrata UUID in itemId, whether the item was matched or created. If you use Octo-Fields, include itemId. Store this UUID and send it as itemId in later purchases.


Partially Update an Item

Only the parameters included in a PATCH request are changed. Omitted parameters keep their current values. For the full list of parameters, see Writable Item Parameters.

📒 NOTE

  • Use either the Ventrata UUID or a non-empty reference as {itemId}. UUID is recommended.

  • taxRateIds replaces the complete set of assigned tax rates. To add a rate, send the existing UUIDs together with the new one. Pass an empty array to clear all assignments.

  • All supplied tax-rate IDs are validated before the item is updated. If any ID is unavailable to the connection, no part of the update is applied.

  • internalName is canonical. title is accepted as a fallback when internalName is absent.

  • destinationId: "DEFAULT" clears the item's destination.

  • Supplying coverImageUrl replaces the cover image. Omit it when the image should remain unchanged.

The response is the updated item, filtered by Octo-Fields when supplied.

Example request 1

This is a PATCH request to update a fixed price to £27.50, set the unit cost to £11.00, replace the cover image, and replace the tax-rate assignments.

curl --request PATCH 'https://api.ventrata.com/octo/ventrata/dashboard/items/<ITEM_UUID>' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard' \
--header 'Content-Type: application/json' \
--header 'Octo-Fields: {id,internalName,reference,billingAccountCode,customRetail,retail,customCost,cost,currency,coverImageUrl,taxIncluded,taxRates{id,internalName,amount,percentage,currency}}' \
--data '{
"customRetail": false,
"retail": 2750,
"customCost": false,
"cost": 1100,
"coverImageUrl": "https://images.example.com/tatton-guidebook-new.jpg",
"taxRateIds": ["<TAX_RATE_UUID>"]
}'

Example request 2

This is a PATCH request to switch an item to externally managed pricing without supplying a fixed retail value.

curl --request PATCH 'https://api.ventrata.com/octo/ventrata/dashboard/items/<ITEM_UUID>' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard' \
--header 'Content-Type: application/json' \
--data '{
"customRetail": true
}'


Delete an Item

Delete a single item identified by its Ventrata UUID or its non-empty reference. UUID is recommended.

Example request

This is a DELETE request to delete a single item.

curl --request DELETE 'https://api.ventrata.com/octo/ventrata/dashboard/items/<ITEM_UUID>' \
--header 'Authorization: Bearer <API_TOKEN>' \
--header 'Octo-Capabilities: ventrata/dashboard'

A successful delete returns HTTP 200 with an empty body. The item no longer appears in catalogue reads. After a successful delete, remove the item's mapping from your external system or mark it as deleted.


Item Reference

Writable Item Parameters

Use these parameters when you create an item, update an item, or send a nested item in a purchase.

Parameter

Type

Description

active

boolean

true if the item is available for sale

internalName

string

canonical name of the item;

maps to the translated item name in the Ventrata Dashboard

title

string

accepted as a fallback when internalName is omitted

reference

string

human-readable external reference, for example, a barcode;

maps to the 'SKU/Code' field in the Ventrata Dashboard

billingAccountCode

string or null

accounting reference, independent of reference;

on update, omit it to keep the existing value or send null to clear it;

maps to the 'Billing Account Code' field in the Ventrata Dashboard

subtitle

string

optional description

category

string

retail category of the item

destinationId

UUID or DEFAULT

UUID of the destination where the item is sold;

pass DEFAULT to create the item without a destination, or to clear the destination on update

📒 NOTE

If the connection is scoped to a single destination, a new item inherits that destination when destinationId is omitted.

An explicit destinationId overrides it.

customRetail

boolean

  • true if the selling price is supplied with each purchase by the external system;

  • false (default) if the price is managed in Ventrata

retail

integer

fixed per-unit selling price in integer minor units, for example, 1000 for 10.00;

do not send when customRetail is true

customCost

boolean

  • true if the external system supplies the cost with each purchase

  • false if the cost is managed in Ventrata

cost

integer

fixed per-unit cost in integer minor units, for example, 1000 for 10.00;

do not send when customCost is true

coverImageUrl

string

publicly accessible HTTPS image URL or image data URI, such as data:image/png;base64,...

📒 NOTE

Ventrata ingests the cover image and returns its stored URL in coverImageUrl, so the response URL may differ from the submitted URL.

taxIncluded

boolean

true if retail values include tax

taxRateIds

UUID array

UUIDs of the tax rates to assign; replaces the complete set of assigned tax rates when present; item responses return the assigned rates as full objects under taxRates

tags

string array

item tags

customRetailOptions

integer array

(optional) allowed custom prices in minor units

restrictions.minCustomRetail

integer

(optional) minimum custom price in minor units

restrictions.maxCustomRetail

integer

(optional) maximum custom price in minor units

Purchase Parameters

Use these parameters in a POST /purchases request.

Parameter

Type

Description

itemId

UUID

UUID of the purchased item; if supplied, Ventrata uses that item

item

object

any writable item parameters; used when itemId is not supplied to reuse an item with the same reference or create a new one; see Create an Item Just in Time with a Purchase

quantity

integer

number of items purchased

currency

string

3-letter code of the purchase currency, for example, GBP

retail

integer

per-unit gross selling price before discount, in integer minor units, for example, 2500 for 25.00;

send it for items with customRetail: true

cost

integer

per-unit cost in integer minor units, for example, 1000 for 10.00;

required when the item has customCost: true; not applied to fixed-cost items

offerDiscount.retail

integer

(optional) per-unit discount supplied by the external system, in integer minor units, for example, 500 for 5.00;

requires the octo/offers capability;

omit offerDiscount when no external discount applies

Item Response Fields

Item responses support these fields through Octo-Fields.

📗 TIP

destination and taxRates are expanded objects. Select the nested properties needed for your integration.

Field

Type

Description

id

UUID

UUID of the item; use it as the durable mapping key

internalName

string

canonical name of the item;

maps to the translated item name in the Ventrata Dashboard

title

string

returns the same name as internalName

titlePlural

string

the plural label of the item

subtitle

string

optional description

reference

string

human-readable external reference

billingAccountCode

string or null

accounting reference;

null when no code is set

category

string

retail category of the item

tags

string array

item tags

locale

string

two-letter language code based on the 'Default Language' field configured in the Supplier Settings, for example, en

timeZone

string

time zone configured in the Supplier Settings, for example Europe/Prague

destination.id

UUID or DEFAULT

  • UUID of the destination where the item is sold

  • DEFAULT when no destination is assigned

destination.title

string

name of the destination

active

boolean

  • true: the item is available for sale

  • false: the item is not available for sale

customRetail

boolean

  • true: the price is supplied by the external system

  • false: the price is managed in Ventrata

customRetailOptions

integer array

allowed custom prices in minor units

restrictions.minCustomRetail

integer

minimum custom price in minor units

restrictions.maxCustomRetail

integer

maximum custom price in minor units

retail

integer

fixed per-unit selling price in integer minor units;

not the charged price when customRetail is true

customCost

boolean

  • true: the cost is supplied by the external system with each purchase

  • false: the cost is managed in Ventrata

cost

integer

fixed per-unit cost in integer minor units

currency

string

currency of the item's monetary values; use it when presenting or converting retail and cost

coverImageUrl

string

URL of the stored cover image

taxIncluded

boolean

  • true: retail values include tax

  • false: retail values exclude tax

taxRates.id

UUID

UUID of the assigned tax rate

taxRates.internalName

string

canonical name of the tax rate

taxRates.amount

number (percentage) /

integer (fixed rates)

  • percentage: true: percentage value for percentage tax rates

  • percentage: false: integer in the currency's minor unit for fixed tax rates

taxRates.percentage

boolean

  • true: amount is a percentage;

  • false: amount is a fixed amount

taxRates.currency

string

  • currency of a fixed tax rate

  • null for percentage tax rates

Example item response

{
"id": "<ITEM_UUID>",
"internalName": "Tatton guidebook",
"reference": "GUIDEBOOK",
"billingAccountCode": "GUIDEBOOK-001",
"destination": {
"id": "<DESTINATION_UUID>",
"title": "Tatton Park"
},
"active": true,
"customRetail": false,
"retail": 2750,
"customCost": false,
"cost": 1100,
"currency": "GBP",
"coverImageUrl": "https://res.cloudinary.com/ventrata/image/upload/tatton-guidebook.jpg",
"taxIncluded": true,
"taxRates": [
{
"id": "<TAX_RATE_UUID>",
"internalName": "Standard VAT",
"amount": 20.0,
"percentage": true,
"currency": null
}
]
}


General Integration Recommendations

  • List the Ventrata item catalogue and store each item's Ventrata id as the durable mapping key.

  • List tax rates and map their Ventrata IDs before sending item writes.

  • Choose fixed or custom retail and cost explicitly for every item. For custom-retail or custom-cost items, send the applicable retail, cost, and optional discount on each purchase.

  • Create missing Ventrata items in advance, or create them just in time with a purchase, and persist the returned item IDs.

  • Send partial updates containing only parameters that changed. Include taxRateIds only when replacing tax assignments.

  • Reconcile known mappings efficiently with GET /items?itemIds[]=....

  • Delete items that should be removed, then remove the local mapping or mark it as deleted after success.

  • Keep retail, cost, and other monetary values as integer minor units, and use the response currency when presenting or converting them.

  • Invalid authentication, permissions, destinations, items, or tax rates return the standard OCTO error response. Treat these as configuration or validation failures rather than automatically retrying them as transient failures.

Did this answer your question?