Skip to main content
Version: v1

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.

KindEntityKind caseRequired payload key(s)
productEntityKind::Productname
variationEntityKind::Variationparent_external_id
categoryEntityKind::Categoryname
tagEntityKind::Tagname
attributeEntityKind::Attributename
pageEntityKind::Pagetitle
postEntityKind::Posttitle
cptEntityKind::Cpttitle
menuEntityKind::Menuname
menu_itemEntityKind::MenuItemlabel, menu_external_id
siteEntityKind::Site(none)
shipping_methodEntityKind::ShippingMethodlabel
payment_methodEntityKind::PaymentMethodlabel
Required ≠ everything the bot uses

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

FieldTypeNotes
namestringRequired.
skustring
slugstring
product_typestringe.g. simple, variable
price_amountintegerMinor units (19999 = 199.99).
regular_price_amountintegerMinor units.
sale_price_amountintegerMinor units.
currencystringISO 4217, e.g. UAH.
descriptionstringPlain text (strip HTML).
short_descriptionstringPlain text.
stock_statusstringinstock / outofstock / onbackorder.
stock_quantityinteger
permalinkstringAbsolute URL to the product page.
categorieslist of stringsCategory slugs, not objects/ids.
tagslist of stringsTag slugs.
imageslist of stringsImage URLs, primary first.
Every *_amount is integer minor units

price_amount, regular_price_amount and sale_price_amount are integers in the currency's minor unit199.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

FieldTypeNotes
namestringRequired.
slugstring
parent_external_idstring | nulllaravel:category:{parent_id}, or null for a root.
descriptionstringPlain text.
permalinkstringAbsolute URL.

page / post / cpt

FieldTypeNotes
titlestringRequired.
slugstring
content_textstringPlain text for retrieval (strip HTML).
permalinkstringAbsolute URL.
excerptstringPlain text.

variation (standalone)

FieldTypeNotes
parent_external_idstringRequired. laravel:product:{parent_id}.
skustring
pricestring/numberDecimal price, e.g. "199.99"not minor units.
regular_pricestring/numberDecimal.
sale_pricestring/numberDecimal.
stock_statusstring
attributesobject{name: value} pairs.
Standalone vs inline variation pricing differ

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.

FieldTypeNotes
menu_external_idstringRequired. laravel:menu:{menu_id}.
labelstringRequired.
urlstring
parent_external_idstring | nullFor nested items.
order_indexinteger

shipping_method / payment_method

FieldTypeNotes
labelstringRequired. 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.

Backend is the source of truth

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.