Data reference
This page lists the entity kinds monoverse/voicebot-laravel-sync can emit, the required payload
key(s) the backend validator enforces per kind, and the canonical fields the assistant reads. The
wire envelope, signing, and protocol negotiation are defined by the canonical contract — this page does
not restate them (see The protocol).
Entity kinds
Enable only the kinds you have, in config/voicebot.php. Each external_id you supply is wrapped to
laravel:{kind}:{id} automatically.
| Kind | EntityKind case | Required payload key(s) |
|---|---|---|
product | EntityKind::Product | name |
variation | EntityKind::Variation | parent_external_id |
category | EntityKind::Category | name |
tag | EntityKind::Tag | name |
attribute | EntityKind::Attribute | name |
page | EntityKind::Page | title |
post | EntityKind::Post | title |
cpt | EntityKind::Cpt | title |
menu | EntityKind::Menu | name |
menu_item | EntityKind::MenuItem | label, menu_external_id |
site | EntityKind::Site | (none) |
shipping_method | EntityKind::ShippingMethod | label |
payment_method | EntityKind::PaymentMethod | label |
host_profile | EntityKind::HostProfile | (none) — declares which bot actions the host supports |
The "required" column is the minimum the backend validator demands — a record missing it is rejected
(and, for deltas, dead-lettered). voicebot:doctor checks exactly these keys on a sampled mapped row
before your first push. Beyond the required key, send as many of the canonical fields below as you have;
the more you send, the better the assistant answers.
The published config/voicebot.php ships product, category and page enabled (with example maps
commented out) and the rest disabled. Flip enabled per kind as needed.
Canonical fields the assistant uses
The fields the bot actually reasons over, per common kind. Map the ones you have; omit the rest. See Mapping for the gotchas (money in minor units, slug lists, plain-text content).
product
| Field | Type | Notes |
|---|---|---|
name | string | Required. |
sku | string | |
slug | string | |
product_type | string | e.g. simple, variable |
price_amount | integer | Minor units (19999 = 199.99). |
regular_price_amount | integer | Minor units. |
sale_price_amount | integer | Minor units. |
currency | string | ISO 4217, e.g. UAH. |
description | string | Plain text (strip HTML). |
short_description | string | Plain text. |
stock_status | string | instock / outofstock / onbackorder. |
stock_quantity | integer | |
permalink | string | Absolute URL to the product page. |
categories | list of strings | Category slugs, not objects/ids. |
tags | list of strings | Tag slugs. |
images | list of strings | Image URLs, primary first. |
variant_axes | list of objects | Configurable-product choice dimensions. Each axis: { name, slug?, values: [{ label, slug?, external_id? }] }. Required for select_variant to work. |
variations | list of objects | Concrete variations. Each: { external_id, attributes: {name: value}, price_amount?, stock_status? }. The bot resolves variation_external_id from these when the shopper picks axes. |
attributes | list of objects | Non-variant product attributes (material, brand, etc.) for catalog search facets. |
*_amount is integer minor unitsprice_amount, regular_price_amount and sale_price_amount are integers in the currency's minor
unit — 199.99 UAH is 19999, never a float or decimal string. Sending a float is a 100× price
error. See Mapping → Money is integer minor units.
category
| Field | Type | Notes |
|---|---|---|
name | string | Required. |
slug | string | |
parent_external_id | string | null | laravel:category:{parent_id}, or null for a root. |
description | string | Plain text. |
permalink | string | Absolute URL. |
page / post / cpt
| Field | Type | Notes |
|---|---|---|
title | string | Required. |
slug | string | |
content_text | string | Plain text for retrieval (strip HTML). |
permalink | string | Absolute URL. |
excerpt | string | Plain text. |
variation (standalone)
| Field | Type | Notes |
|---|---|---|
parent_external_id | string | Required. laravel:product:{parent_id}. |
sku | string | |
price | string/number | Decimal price, e.g. "199.99" — not minor units. |
regular_price | string/number | Decimal. |
sale_price | string/number | Decimal. |
stock_status | string | |
attributes | object | {name: value} pairs. |
The standalone variation kind uses decimal price fields (price / regular_price / sale_price).
This is different from the inline product.variations[] shape, which uses integer minor units
(price_amount / regular_price_amount). Do not map price_amount on a standalone variation — it is
silently ignored and the variation ships with no price.
Variations may also be sent inline on the parent product as a variations list; the standalone kind
is for when you model them as their own rows.
menu_item
| Field | Type | Notes |
|---|---|---|
menu_external_id | string | Required. laravel:menu:{menu_id}. |
label | string | Required. |
url | string | |
parent_external_id | string | null | For nested items. |
order_index | integer |
host_profile
The host_profile entity is a singleton (always external_id = "host") that tells VoiceBot which
actions your host supports beyond the read/nav baseline. Without it the bot only has catalog search,
product lookup, compare, and navigation. Enable and configure it in config/voicebot.php:
EntityKind::HostProfile->value => [
'enabled' => true,
'capabilities' => [
'cart.add',
'cart.remove',
'cart.update_qty',
'cart.view',
'variant.select',
],
'cart_endpoint' => null,
'metadata' => [],
],
| Field | Type | Notes |
|---|---|---|
capabilities | string[] | Capability ids from the closed vocabulary. Unknown strings are silently dropped. Duplicates are de-duped. |
cart_endpoint | string | null | Optional. A server-side cart endpoint, if your host exposes one. Omit for client-bridge-only carts. |
metadata | object | Optional free-form host metadata. |
After enabling, run php artisan voicebot:sync --full (or --kind=host_profile) to push the entity.
shipping_method / payment_method
| Field | Type | Notes |
|---|---|---|
label | string | Required. Human-readable method name. |
On-the-wire record shapes
You don't construct these by hand — the mapper does — but for reference:
- Snapshot (full) lines are NDJSON records:
{ "kind", "external_id", "payload", "lang"?, "translation_of"? }. - Delta (events) ops are operation objects:
{ "type": "upsert" | "delete", "entity_kind", "external_id", "payload"?, "lang"?, "translation_of"? }.
The same canonical fields populate payload in both shapes.
The protocol
The signing, headers, replay window, batch ceilings, snapshot upload flow, and protocol-version
negotiation are the authoritative VoiceBot Sync Protocol v2 contract — this package implements it,
it does not redefine it. The contract lives in the VoiceBot repository at
docs/contracts/sync_protocol_v2.md (with the version-negotiation rule in
docs/shared/adr/ADR-033_sync_protocol_versioning.md and the ingest design in
docs/shared/adr/ADR-045_canonical_signed_ingest.md / ADR-055_laravel_ingest_producer.md).
For the bot-facing side of the same VoiceBot key (the browser/app conversation channel), see the Wire protocol reference.
The required-key table mirrors the backend validators
(apps/api/src/voicebot/modules/ingest/parsers/entity_validators.py). If the backend tightens a
requirement, voicebot:doctor and a delta's per-op errors will surface it before bad data lands.