Skip to content

15.09.2026-1

A pass over the Inventory API documentation to make it say what the service actually does. No API behaviour changed — every correction below describes how the platform already worked.

The guides wrote field names one way and the reference another. Everything now reads as it appears on the wire: externalId, playerId, pageSize, weeklyHours, supportedLanguages, storeChain, and so on — in the prose and in the request and response samples you copy.

Both spellings are accepted on input, but a response only ever contains camelCase, so that is what the docs show.

Four places genuinely use snake_case, and still do:

  • the verve-metadata payload inside VAST
  • the OAuth token endpoint (grant_type, client_id, expires_in)
  • the /ads query parameters (playerid, dvactid, retail_grocery_zone)
  • field paths carried as string values, such as fieldViolations[].field

The reference used to say “All fields are optional unless otherwise noted” and mark nothing. Fields the platform has always required are now marked as such:

Resource Required on upsert
Device identifier (with identifier.playerId), status, location (with location.externalId)
Location externalId, supportedLanguages

Within opening hours, an entry you send must be complete: weeklyHours[].day and .availability, and dateOverrides[].availability.

At least one supportedLanguages entry is required because booking is language-keyed: a location declaring none exposes no bookable formats, so its devices are unbookable however else they are configured.

Required is stated from the creating perspective — what a resource must carry the first time you upsert it. Two rules decide whether a value counts as supplied:

  • Every enum has an *_UNSPECIFIED member, which is also its default. It means absent, so sending "status": "STATUS_UNSPECIFIED" on a required enum is rejected exactly as omitting it is.
  • An omitted field and an explicitly empty one are the same thing, so an empty array does not satisfy a required repeated field.

Troubleshooting previously covered four authentication failures and nothing else. It now documents:

  • the two body shapes — service errors carry details and a numeric gRPC code; gateway errors have no details and code is the HTTP status
  • the status codes each API returns
  • how to read BadRequest.fieldViolations — the field path format and what each description prefix means
  • requestId, the trace id to quote when raising something

Note this is not the AIP-193 shape: there is no error wrapper and no status field.

A device’s externalId is derived by the platform from its identifier as "{key}_{playerId}" — or the bare playerId when key is empty, with no leading separator. It is read-only: a value sent on a write is ignored. Set identifier instead, which is required when the device is first created and ignored afterwards, so a device’s identity cannot be changed.

  • locationId on the device listing matches the location’s externalId (for example a TDLinx code), not its metadata.id. The filter was always exact, never a substring.
  • internalId on a store chain, sub-chain or merchant is server-assigned and output-only. It was previously written internal_id, which made it hard to match against the reference.
  • Pagination samples showed "currentPage": 0 next to a request sending "page": 1. Paging is 1-based; the sample was wrong.
  • Conflicts between location versions are described as versions holding different values, rather than in terms of internal storage.

These were documented but did nothing. Sending them was never an error, and still is not — they are simply no longer advertised.

Field
mask Not used. The request body determines what is written.
adNetworkId filter Never applied by the device listing.
deploymentId filter Never applied by the device listing.

The device listing filters on locationId, externalId and name.

POST /v1alpha/inventory/mgmt:upsert is no longer documented. It combined a location and a device in one call, but was never atomic — the location was written first, then the device, with no rollback. Use locations:upsert and devices:upsert, which is what it called anyway. The endpoint still works for anyone already using it.