Skip to content

Managing inventory

The Inventory API registers and updates your physical inventory — locations (stores) and the devices (screens) deployed in them. A device also carries its own physical placement — its venue classification, zone, and product aisles.

Combined upsert — location + device in one call

Section titled “Combined upsert — location + device in one call”

POST /v1alpha/inventory/mgmt:upsert upserts a location and a device together. If the location upsert fails, the device is not attempted and its outcome reports FAILED_PRECONDITION.

POST /v1alpha/inventory/mgmt:upsert HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{
"location": {
"metadata": { "name": "Store 123" },
"external_id": "store-123",
"supported_languages": ["DE"]
},
"device": {
"metadata": { "name": "Screen A" },
"identifier": { "key": "acme", "player_id": "screen-a" },
"status": "STATUS_ACTIVE",
"location": { "external_id": "store-123" }
}
}

The response reports a per-resource outcome — an independent status plus the upserted resource — for the location and the device.

When you only need one resource type, locations and devices each have their own endpoints:

Resource Upsert List
Locations POST /v1alpha/inventory/locations:upsert POST /v1alpha2/inventory/locations
Devices POST /v1alpha/inventory/devices:upsert POST /v1alpha/inventory/devices

Both also support Get (GET …/{id}), Create (PUT), Update (PATCH), Delete (DELETE …/{id}), and more.

Each dedicated :upsert takes the bare resource object as its request body — a locations:upsert body is a Location, a devices:upsert body is a Device, neither wrapped in a location/device field. The wrapper is only for mgmt:upsert, whose body carries both resources under location and device.

Only partner organizations can create locations, and doing so means claiming that physical store. Verve’s job is to converge every partner’s claim on the same store (same external_id) into one consistent record so targeting stays precise — when two partners claim the same store with different details, that’s a conflict, and it gets resolved into a single canonical version.

Listing returns that tenant-wide canonical view, not just the locations your own organization or project claimed — use only_own to narrow it down to yours.

POST /v1alpha2/inventory/locations returns one row per physical location (external_id). When more than one partner has uploaded the same physical store, the row merges every partner’s version instead of returning a duplicate per partner.

POST /v1alpha2/inventory/locations HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{
"page": 1,
"page_size": 25,
"filters": { "only_conflicts": false, "only_own": false, "external_id": "store-1", "name": "edeka" },
"sort": { "field": "LOCATION_SORT_FIELD_CITY", "direction": "SORT_DIRECTION_ASC" }
}
  • page / page_sizepage_size defaults to 50 and is capped at 100.
  • filters.only_conflicts — only return locations whose versions disagree (see Conflicts below).
  • filters.only_own — only return locations directly created under your organization/project.
  • filters.external_id — only return locations whose external_id contains this value (case-insensitive substring match).
  • filters.name — only return locations whose metadata.name contains this value (case-insensitive substring match).
  • sort.field — the field you want to sort, like LOCATION_SORT_FIELD_STORE_SUBCHAIN, see all the options in the API Reference.
  • sort.direction is SORT_DIRECTION_ASC or SORT_DIRECTION_DESC. Omit sort to order by external_id ascending.

Each returned row looks like this:

{
"locations": [
{
"resource": {
"summary": {
"ids": { "location": "01HHXTNJ3K1QZ8V6X6Y7Z8A9B0", "organization": "*", "project": "*" },
"external_id": "store-123",
"name": "Store 123",
"status": "STATUS_ACTIVE",
"store_chain": "Edeka",
"store_subchain": "Edeka Center",
"city": "Berlin",
"country": "Germany",
"address": { "country": "Germany", "city": "Berlin", "street": "Karl-Marx-Straße", "number": "12", "zip_code": "12043" }
},
"source": "LOCATION_SOURCE_LATEST"
},
"versions": [
{ "summary": { /* same shape as `locations.resource.summary` above */ }, "source": "LOCATION_SOURCE_CANONICAL" },
{ "summary": { /* same shape as `locations.resource.summary` above */ }, "source": "LOCATION_SOURCE_LATEST", "organization": "Acme Retail" }
],
"has_conflict": false,
"device_count": 3
}
],
"total_count": 1,
"total_pages": 1,
"current_page": 1
}
  • resource — the row’s display version: your own claim on that location when you have one, otherwise the canonical (tenant-wide consensus) version.
  • versions — every stored version of that physical location — the canonical record plus each partner’s latest copy — canonical first.
  • has_conflicttrue when the versions carry more than one distinct content checksum.
  • device_count — the amount of devices associated with this location.

summary.address carries the location’s full address (country, city, street, number, zip_code, region, state); summary.city / summary.country remain as flattened convenience fields with the same values.

Two partners can upload the same physical store (same external_id) with diverging content — a different address, store name, or opening hours. Set filters.only_conflicts: true to list only the locations where that’s happened, then compare each entry’s versions to see exactly which fields diverge and who claims each version.

POST /v1alpha/inventory/devices returns the devices in your project, paginated. All request fields are optional and combine (AND) to narrow the result:

POST /v1alpha/inventory/devices HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{
"page": 1,
"page_size": 25,
"location_id": "store-123",
"deployment_id": "01KTKMSW7D349VKBZBXBZRG76S",
"external_id": "screen-a",
"name": "Screen A"
}
  • page / page_sizepage_size defaults to 50 and is capped at 100. Omit both to get the first page at the default size.
  • location_id — only devices assigned to the location with this external_id (e.g. store-123) — the same value you set on the device’s location.external_id, not the location’s metadata.id.
  • deployment_id — only devices in this deployment.
  • external_id — only devices whose external_id contains this value (case-insensitive substring match).
  • name — only devices whose metadata.name contains this value (case-insensitive substring match).

Omit every filter to list all devices in the project (still paginated). The response is a flat list of devices plus pagination metadata:

{
"devices": [
{
"metadata": { "id": "01HHXTNJ3K1QZ8V6X6Y7Z8A9B0", "name": "Screen A" },
"identifier": { "key": "acme", "player_id": "screen-a" },
"status": "STATUS_ACTIVE",
"location": { "external_id": "store-123" }
}
],
"total_count": 1,
"total_pages": 1,
"current_page": 1
}
  • total_count — total devices matching the filters across all pages.
  • total_pages — number of pages at the requested page_size.
  • current_page — the page echoed back (1-based).

Each entry is a full Device — see the API Reference for the complete request and response schemas.

  • Location — a physical store.
  • Device — a screen deployed at a location. Its physical placement — venue classification, zone, and product aisles — is owned by the device itself (see Venue, zones and aisles).
  • Upsert — every write is an idempotent create-or-update keyed on identity, so sending the same payload again simply reconciles state rather than creating duplicates.

metadata.id is server-generated and output-only — never send it on a write. Each resource is keyed on an identity you provide:

  • A location’s identity is its top-level external_id field. It is required on locations:upsert (and on the location in mgmt:upsert): the platform resolves an existing location by it, or creates one on a miss. Set the raw value — no externalid: prefix.
  • A device’s identity is its identifier (key + player_id); the device’s own external_id is derived from them as "{key}_{player_id}" and can’t be set directly. Both identifier (with a non-empty player_id) and status are required on devices:upsert.

An upsert response echoes the server-assigned metadata.id (a ULID). Use that ULID, or your own external_id, to reference the resource elsewhere.

A device’s location reference points to a location by its external_id — set it directly on location.external_id (e.g. "location": { "external_id": "store-123" }). This is the location’s own external id, the same value you set on the location’s external_id field. A device’s location reference must point to an existing location — whether one you created or one that already existed.

A device is not created with an inline location — you assign an existing location to it. Check whether the location you need already exists; if it doesn’t, create it first (via locations:upsert or the combined mgmt:upsert). Then reference it from the device’s location field on a device upsert.

Reference the location by its external id, set directly on location.external_id:

POST /v1alpha/inventory/devices:upsert HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{
"metadata": { "name": "Screen A" },
"identifier": { "key": "acme", "player_id": "screen-a" },
"status": "STATUS_ACTIVE",
"location": { "external_id": "store-123" }
}

The request body of a devices:upsert is the bare device object — there is no device wrapper (that wrapper is only used by mgmt:upsert; see Dedicated endpoints). Here location.external_id is the location’s external id (store-123). The reference is resolve-or-reject: the platform looks up the location by that external id, and if no such location exists in the project the device upsert fails with FAILED_PRECONDITION — it never creates the location for you.

A location’s operating schedule is expressed with two fields on the location:

  • weekly_hours — recurring weekly rules, one entry per day/window.
  • date_overrides — date-specific rules that override the weekly schedule (holidays, one-off closures or reduced hours).

Each rule carries an availabilityAVAILABILITY_OPEN or AVAILABILITY_CLOSED — for a window given by a start / end time of day. Leaving start / end unset applies the rule to the whole day.

weekly_hours is a list of per-day rules. Several entries for the same day express multiple windows (e.g. a split shift); a day with no open window is closed. For example, open 07:00–22:00 Monday–Saturday and closed Sunday:

POST /v1alpha/inventory/locations:upsert HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{
"metadata": { "name": "Store 123" },
"external_id": "store-123",
"supported_languages": ["DE"],
"timezone": "Europe/Berlin",
"weekly_hours": [
{ "day": "MONDAY", "availability": "AVAILABILITY_OPEN", "start": { "hours": 7 }, "end": { "hours": 22 } },
{ "day": "SATURDAY", "availability": "AVAILABILITY_OPEN", "start": { "hours": 8 }, "end": { "hours": 20 } },
{ "day": "SUNDAY", "availability": "AVAILABILITY_CLOSED" }
]
}

date_overrides take precedence over the weekly schedule for a specific date. Set date.year to 0 for a rule that recurs every year (e.g. a fixed holiday), or a concrete year for a one-off. As with weekly hours, an unset start / end covers the whole day, and times are in the location’s timezone.

"date_overrides": [
{ "date": { "year": 0, "month": 12, "day": 25 }, "availability": "AVAILABILITY_CLOSED" },
{ "date": { "year": 2026, "month": 6, "day": 15 }, "availability": "AVAILABILITY_OPEN", "start": { "hours": 7 }, "end": { "hours": 14 } }
]

The first rule closes the location every 25 December; the second sets reduced hours (07:00–14:00) on 15 June 2026 only.

A location declares the languages its screens render through supported_languages — a list of two-letter ISO 639-1 language codes such as "DE" or "EN". Input is case-insensitive and normalized to uppercase, so the stored (and returned) form is uppercase.

POST /v1alpha/inventory/locations:upsert HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{
"metadata": { "name": "Store 123" },
"external_id": "store-123",
"supported_languages": ["DE", "EN"]
}

Each entry must be a valid two-letter ISO 639-1 code (case-insensitive); anything else — an unknown code or a longer tag such as "en-US" — is rejected as INVALID_ARGUMENT.

A location declares the retailer it belongs to through two fields:

  • store_chain — the retail chain brand the store trades under, e.g. "EDEKA" or "Rewe".
  • store_subchain — the distribution line / banner within that chain, e.g. "Edeka Center" or "Netto Marken-Discount".

Both are set on a locations:upsert. Each is an object whose name carries the value — set name; the object’s internal_id is server-assigned and output-only:

POST /v1alpha/inventory/locations:upsert HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{
"metadata": { "name": "Store 123" },
"external_id": "store-123",
"supported_languages": ["DE"],
"store_chain": { "name": "Edeka" },
"store_subchain": { "name": "Edeka Center" }
}

The chain and sub-chain drive targeting and listing: an advertiser can restrict delivery to one or more chains (and optionally specific sub-chains under them) in the audience builder, and locations can be listed sorted by sub-chain (LOCATION_SORT_FIELD_STORE_SUBCHAIN). In listing responses the two come back as plain strings on each row’s summary (store_chain, store_subchain).

Use these chain and sub-chain names as the reference set. Every sub-chain belongs to exactly one chain — set store_chain to the chain and store_subchain to the matching sub-chain in the same row:

Chain Sub-chains
Edeka Edeka · Edeka Center · nah & gut · Marktkauf · Netto Marken-Discount · SPAR express · Budni
Rewe REWE · REWE Center · nahkauf · akzenta · Penny · REWE To Go · toom Baumarkt · B1 Discount Baumarkt
Schwarz Gruppe Lidl · Kaufland
Aldi Aldi Süd · Aldi Nord
Metro AG Metro
Salling Group Netto
Bartels-Langness (Bela) famila (Nord-Ost) · Markant (Nord/Ost) · nah & frisch
Bünting Group Combi · famila (Nord-West) · Markant Nordwest
Globus Holding Globus · Globus Baumarkt
Dohle Handelsgruppe HIT
Ceconomy MediaMarkt · Saturn
dm-drogerie markt dm-drogerie markt
Rossmann Rossmann
Müller Müller
Alnatura Alnatura
Denns BioMarkt Denns BioMarkt
tegut tegut
Norma Norma

A device’s physical placement describes where the screen sits: the kind of venue it’s in, the zone within that venue, and — for grocery stores — the product aisles it faces. All three live on the device, grouped under a single placement object on a devices:upsert:

  • placement.venue — the DOOH venue classification.
  • placement.retail_grocery_zone / placement.retail_mall_zone — the zone within the venue (a oneof, so set at most one).
  • placement.aisles — the product aisles the device serves (grocery only).

placement.venue classifies the screen against the OpenOOH venue taxonomy (v1.2.1) — a parent tier (VENUE_TRANSIT_*, VENUE_RETAIL_*, …) with more specific child values. Retail media typically uses the retail parents, for example:

Venue Value
Grocery VENUE_RETAIL_GROCERY
Mall VENUE_RETAIL_MALL
Convenience store VENUE_RETAIL_CONVENIENCE_STORE
Pharmacy VENUE_RETAIL_PHARMACY
Fueling station VENUE_RETAIL_FUELING_STATION

See the Inventory API reference for the full Venue enum.

A zone pinpoints the device within a typed venue, as a grandchild of the venue classification. Zones are only defined for venues that have a typed subtype — today grocery and mall — and each has its own field in the zone oneof:

Grocery zones (placement.retail_grocery_zone):

Zone Value
Entrance VENUE_RETAIL_GROCERY_ENTRANCE
Check-out VENUE_RETAIL_GROCERY_CHECK_OUT
Aisles VENUE_RETAIL_GROCERY_AISLES
Exterior VENUE_RETAIL_GROCERY_EXTERIOR

Mall zones (placement.retail_mall_zone):

Zone Value
Concourse VENUE_RETAIL_MALLS_CONCOURSE
Food court VENUE_RETAIL_MALLS_FOOD_COURT
Spectacular VENUE_RETAIL_MALLS_SPECTACULAR

Set the zone field that matches the venue: retail_grocery_zone for VENUE_RETAIL_GROCERY, retail_mall_zone for VENUE_RETAIL_MALL. Venues without a typed subtype carry no zone — leave both fields unset.

For a grocery device positioned in the aisles, placement.aisles lists the product aisles it faces, as a repeated set of Aisle values (e.g. AISLE_PRODUCE, AISLE_DAIRY, AISLE_BAKERY, AISLE_FROZEN, AISLE_BEVERAGE, AISLE_MEAT). Aisles apply to grocery venues only; leave the list empty for any other venue. See the Inventory API reference for the full Aisle enum.

A grocery screen in the entrance that faces the produce and dairy aisles:

POST /v1alpha/inventory/devices:upsert HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{
"metadata": { "name": "Screen A" },
"identifier": { "key": "acme", "player_id": "screen-a" },
"status": "STATUS_ACTIVE",
"location": { "external_id": "store-123" },
"placement": {
"venue": "VENUE_RETAIL_GROCERY",
"retail_grocery_zone": "VENUE_RETAIL_GROCERY_ENTRANCE",
"aisles": ["AISLE_PRODUCE", "AISLE_DAIRY"]
}
}

A device advertises which creatives it can play through supported_ad_types. Each entry pairs an ad type with the ad-format ids the device can render, so the exchange serves a device only the formats it declares:

POST /v1alpha/inventory/devices:upsert HTTP/1.1
{
"metadata": { "name": "Screen A" },
"supported_ad_types": [
{
"ad_type": "AD_TYPE_DISPLAY",
"formats": [
"IMAGE-HORIZONTAL_16:9_3840x2160_JPG+PNG",
"HTML5-HORIZONTAL_16:9_1920x1080_ZIP"
]
}
]
}

To let a device receive HTML5 creatives, include an HTML5 ad-format id (delivered as an application/zip bundle) such as HTML5-HORIZONTAL_16:9_1920x1080_ZIP or HTML5-VERTICAL_9:16_1080x1920_ZIP. A device that declares only video or image formats is served only those; the exchange withholds HTML5 from it. See Creative formats for how a player renders each format, and Ad types & formats for the full catalog of format ids, dimensions, and file formats.

A device declares the audience sensors attached to it through sensors, a list of sensor capabilities. Each sensor enables a specific inline audience signal on GET /v1alpha2/ads and tells the exchange how to interpret it.

Sensor Meaning
SENSOR_DEMOGRAPHICS The device reports the audience it detects via the seg parameter, using IAB Audience Taxonomy 1.1 segment ids. seg values are read as IAB segments directly (no vendor mapping).
SENSOR_ADVERTIMA The device’s audience detection is provided by Advertima. seg carries Advertima’s own segment ids, which the exchange maps to IAB before selection. See SENSOR_ADVERTIMA.
SENSOR_BASKET The device reports the shopper’s basket via the products parameter, matched against flights that target cart contents.
SENSOR_LOCATION The device reports where it currently is inside the store, via the zone and aisle parameters on the ad request. For moving players — a handheld or cart-mounted device — whose position changes between requests. A fixed screen carries its position in placement instead (see Venue, zones and aisles) and does not declare this sensor.
POST /v1alpha/inventory/devices:upsert HTTP/1.1
{
"metadata": { "name": "Screen A" },
"sensors": ["SENSOR_DEMOGRAPHICS", "SENSOR_BASKET"]
}

A device without the matching sensor cannot interpret that signal; such a request is rejected (400) — seg without SENSOR_DEMOGRAPHICS, products without SENSOR_BASKET, a zone or aisle without SENSOR_LOCATION. Additional sensor types (for other vendors) are added as those integrations land — each brings its own interpretation of the values.

A device can hold at most one sensor of each type. Sensors that drive the same signal are of the same type and are therefore mutually exclusive: a device cannot declare both SENSOR_DEMOGRAPHICS and SENSOR_ADVERTIMA, since both are demographic sensors feeding the seg signal. An upsert that lists two sensors of the same type is rejected (400).

An ad network is a demand source that a device’s inventory is marketed in. Ad networks are defined per tenant; each device declares the networks it belongs to as part of its deployment, and the exchange only markets a device’s inventory in the networks it lists.

Ad networks are configured per tenant. The Verve tenant defines these:

Ad network Marketed inventory
Retail Media Alliance (National Advertisement) National advertising demand via the Retail Media Alliance.
Regional Advertisement Regional advertising demand.

The set is tenant-specific, so a device can only reference a network that exists for its tenant. List the networks for your tenant — the response gives each network’s id, which is the value you put in a device’s deployment:

POST /v1alpha/inventory/adNetworks HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{ "tenant_id": "<TENANT_ID>" }

A device’s ad networks live on its deployment, in deployment.ad_networks — a list of ad-network ids (each network’s metadata.id from the list above, not its name). Set them on a device upsert:

POST /v1alpha/inventory/devices:upsert HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{
"metadata": { "name": "Screen A" },
"deployment": {
"ad_networks": [
"01KMDH2GRKP9ZXZW242G9C8FBB",
"01KTKMSW7D349VKBZBXBZRG76S"
]
}
}

The two ids used above map to the Verve tenant’s networks like this:

Ad network id Ad network
01KMDH2GRKP9ZXZW242G9C8FBB Retail Media Alliance (National Advertisement)
01KTKMSW7D349VKBZBXBZRG76S Regional Advertisement

Ids are tenant-specific, so always take them from the adNetworks list rather than hard-coding.

Where ad_networks says which networks a device is marketed in, a deployment’s allocation_targets decide how a device’s impressions are split across supply sources. There are two kinds:

  • Static — routes a fixed percentage (0–100) of impressions to one or more specific ad servers, typically an on-site CMS serving venue-sourced content. Carries percentage and ad_servers.
  • Non-static — routes the remaining share to the internal SSP auction, governed by a balance_policy (today always AD_SERVER_BALANCE_POLICY_BIDDING). Every device carries exactly one non-static target; the tenant’s own ad server is added by the auction at serve time, so it usually lists no explicit ad_servers.

The static percentages are carved out first and the non-static target takes whatever is left. A device with only a non-static target sends 100% of its impressions to the auction.

Target Key fields Routes
static percentage, ad_servers A fixed % to specific ad servers (on-site CMS / venue-sourced)
non_static balance_policy (optional ad_servers) The remaining % to the internal SSP auction

A static target’s ad_servers reference AdServer entities defined for your tenant. So far there is one:

Ad server id Ad server Source type
viewento-cms Viewento CMS SOURCE_TYPE_CMS (on-site content)

For example, a device that sends 50% to an on-site CMS and the remaining 50% to the auction:

POST /v1alpha/inventory/devices:upsert HTTP/1.1
Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6...
X-Verve-Namespace: project:01ABCDEFGHIJKLMNOPQRSTUVWX
Content-Type: application/json
{
"metadata": { "name": "Screen A" },
"deployment": {
"ad_networks": ["01KMDH2GRKP9ZXZW242G9C8FBB"],
"allocation_targets": [
{
"static": {
"percentage": 50,
"ad_servers": [{ "reference": { "id": "viewento-cms" } }]
}
},
{ "non_static": { "balance_policy": "AD_SERVER_BALANCE_POLICY_BIDDING" } }
]
}
}

Each ad_servers entry is a reference to an AdServer entity, resolved by its id. See the Inventory API reference for the full Deployment schema.

Every device carries a status, and it is mandatory — a device upsert that leaves it unset (STATUS_UNSPECIFIED, the default) is rejected. Set it explicitly on every write:

Status Value In the audience builder Playouts & impressions Receives content
Active STATUS_ACTIVE Included Counted Yes
Inactive STATUS_INACTIVE Excluded Not counted Yes

An inactive device is not offline: it can still request and play ads — it receives content like any other device. What changes is that it is left out of the audience builder (not counted or targetable there) and its plays do not increase playout or impression totals. Marking a device inactive therefore removes it from measured, bookable inventory without stopping it from receiving content.

A device’s playout impression multiplier sets how many impressions a single playout counts as. On DOOH screens one play can represent several contacts, so the multiplier is a fractional value, configured per weekday and optional time window through the device’s playout_impression_factors.

Unset means one, not zero. When the multiplier value is not set (null), it is treated as 1 — one playout counts as one impression. An absent value is never interpreted as 0, so a device with no multiplier configured still counts normally.

To count zero, state it explicitly. If a playout should count as 0 impressions (for example a window that must not be billed), you must set the value to 0 explicitly. Leaving it unset yields 1, not 0.


For full request and response schemas, see the Inventory API reference.