Vitrina API
Referencia

Stock público

El lote publicado de una automotora: listar, contar y leer una unidad. Es la superficie que consume un sitio web de la automotora.

El lote publicado de una automotora: listar, contar y leer una unidad. Es la superficie que consume un sitio web de la automotora.

El capítulo que explica este recurso, con ejemplos ejecutables, está acá.

MétodoRutaQué hace
GET/stockList the public stock (dealer-website read API)
GET/stock/{id}Get one public-stock vehicle
GET/stock/countCount the public stock (same filters as the list)
GET/storefrontStorefront bootstrap (channels, published site content + visual identity)
POST/storefront/eventsRecord a storefront traffic/conversion event
POST/website-leadsSubmit a website buyer inquiry (dealer-website write API)

GET /stock

List the public stock (dealer-website read API)

The tenant’s public lot: active vehicles that are not sold (vendido) nor hidden. reservado units are included with their reserved_at so the website can badge them. Filters cover brand/model/gearbox/fuel_type/body/type/status/featured, year/price/odometer/created ranges and sucursal; sort by price, published_at, created_at or featured_at (default created_desc). limit ≤ 100 (default 20) + offset. The plate is never exposed. Each item carries merch_label + merch_label_es — the dealer’s optional merchandising ribbon (oferta | liquidacion | poco_km | cuatro_x_cuatro | full_equipo | recien_llegado | unico_dueno), null when there is none. A tenant that turns stock_publico.mostrar_vendidos on ALSO gets the units sold in the last 90 days, flagged status: "vendido" with their sold_at, in the list AND in /stock/count; the switch is off by default and there is no caller-side override. Each item also carries is_featured + featured_at — whether the dealer PINNED this unit to the destacados grid of their portada, and when. featured=true narrows the selection to those pinned units (pair it with sort=featured_desc, their pinning order, newest pin first); that selection NEVER includes sold units, whatever mostrar_vendidos says, so a featured car that sells leaves the portada on its own. A tenant that turns stock_publico.visit_coordination_disclosure on (ADR 0095, off by default) additionally gets visit_coordination_required: true on a unit with an ACTIVE, modalidad=virtual consignment contract — a purely logistics fact ("coordinate the visit with the owner"), never the commercial one (tenencia/modalidad are never exposed, on this or any other public surface). The key is ABSENT — never false — on every other unit, opted-in or not.

ParámetroEnTipoObligatorioRestricciones
brandquerystringnomín. 1, máx. 120
modelquerystringnomín. 1, máx. 120
gearboxquerystringnomín. 1, máx. 60
fuel_typequerystringnomín. 1, máx. 60
bodyquerystringnomín. 1, máx. 60
typequeryCar \Truck \Motorcycle \
min_yearqueryintegerno≥ 1900, ≤ 2100
max_yearqueryintegerno≥ 1900, ≤ 2100
min_pricequerynumber | nullno≥ 0
max_pricequerynumber | nullno≥ 0
min_odometerqueryinteger | nullno≥ 0
max_odometerqueryinteger | nullno≥ 0
created_fromquerystringnodate-time
created_toquerystringnodate-time
sucursalqueryuuidno
statusquerydisponible \reservado \vendido
featuredqueryanyno
sortqueryprice_asc \price_desc \published_asc \
limitqueryintegerno≥ 1, ≤ 100
offsetqueryinteger | nullno≥ 0
curl https://api.vitrinadev.com/api/v1/stock \
  -H "Authorization: Bearer $VITRINA_KEY"

Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429

GET /stock/{id}

Get one public-stock vehicle

Missing, foreign-tenant and hidden (inactive / deleted / sold) ids all return the same 404. A tenant with stock_publico.mostrar_vendidos on serves the ficha of a unit sold in the last 90 days instead of 404ing it, so the card on the grid links somewhere; every other gate (inactive / deleted / merged) still 404s. Same visit_coordination_required disclosure rule as the list (ADR 0095): present and true only with the tenant opted into stock_publico.visit_coordination_disclosure AND an ACTIVE, modalidad=virtual consignment contract on this unit; absent otherwise.

ParámetroEnTipoObligatorioRestricciones
idpathuuid
curl https://api.vitrinadev.com/api/v1/stock/<id> \
  -H "Authorization: Bearer $VITRINA_KEY"

Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429

GET /stock/count

Count the public stock (same filters as the list)

ParámetroEnTipoObligatorioRestricciones
brandquerystringnomín. 1, máx. 120
modelquerystringnomín. 1, máx. 120
gearboxquerystringnomín. 1, máx. 60
fuel_typequerystringnomín. 1, máx. 60
bodyquerystringnomín. 1, máx. 60
typequeryCar \Truck \Motorcycle \
min_yearqueryintegerno≥ 1900, ≤ 2100
max_yearqueryintegerno≥ 1900, ≤ 2100
min_pricequerynumber | nullno≥ 0
max_pricequerynumber | nullno≥ 0
min_odometerqueryinteger | nullno≥ 0
max_odometerqueryinteger | nullno≥ 0
created_fromquerystringnodate-time
created_toquerystringnodate-time
sucursalqueryuuidno
statusquerydisponible \reservado \vendido
featuredqueryanyno
curl https://api.vitrinadev.com/api/v1/stock/count \
  -H "Authorization: Bearer $VITRINA_KEY"

Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429

GET /storefront

Storefront bootstrap (channels, published site content + visual identity)

The dealer’s public identity and live contact channels: trading name, whether WhatsApp is connected (and on which number), whether the embeddable webchat is configured, and the active sucursales. Lets a Vitrina-built website decide which CTAs to render — a dealer who connects WhatsApp sees the button appear with no config edit and no redeploy. Readable with the narrow stock:read scope; returns only facts a storefront already publishes to the world (no secrets).

Tenants holding the dealer_site add-on additionally get sitio — the PUBLISHED site snapshot, never the draft — whose sitio.theme carries the visual identity: primary_color (#rrggbb), base (light|dark), display_font/body_font (from the closed whitelist saira|ibmPlexSans|poppins|nunitoSans|archivo|montserrat), radius (sharp|rounded) and density (compact|roomy), plus the OPTIONAL skin (classic|luxe) — which component tree the renderer draws the same content with. skin is the one theme field that may be absent: absent means classic, the look every site published before the field existed already has, so a stored value the server does not recognise drops the skin alone and leaves the rest of the theme intact — plus branding (logo_url, and primary_color as the fallback for a tenant that has not published a theme). These are RAW identity inputs, not a derived palette: the renderer derives the token set from the single brand colour, contrast-adjusting the text/accent token against the active base so any valid hex yields a WCAG-AA-readable page (vitrina-template ADR 0005). Server-side validation is therefore strict SHAPE validation (hex format, whitelisted font, closed enums) — colours are never silently repainted. Also carries analytics (ga4_measurement_id, meta_pixel_id, ticket 86e2rwqy9) — the dealer’s own GA4/Meta Pixel ids, null per field when unconfigured, so the template loads NO third-party script until a valid id is set.

Entitled tenants ALSO get the rest of the dealer’s public identity (ticket 86e2t94ee), so a website can render itself end to end from this one response with no per-client config file: content (tagline, hero_image_url, city, founded_year, show_name_with_logo, partners[], stats[]), contact (sales and optional service channels with label/email/phone_display/phone_e164/whatsapp_message, plus address and maps_url), social (instagram/facebook/linkedin/tiktok/x/youtube), features (blog, services, consignment, catalog_filters, financing, branches, chat), trust (financing, card_payment, trade_in, insurance, digital_transfer) and financing (annualRate, monthlyRatePct, caeRefPct, terms[], minDownPct, defaultDownPct, defaultTermMonths, legalNote, highlightannualRate/monthlyRatePct are the TAN/TEM rate pair, at least one required; caeRefPct is the CAE referencial shown with equal prominence per Art. 17 G, Ley 19.496). content/contact/features/trust are LIVE tenant config — an edit reaches the site at the next bootstrap window with no Publicar — while social and financing are projected from the PUBLISHED Sitio snapshot, where the dealer already edits them. Unset feature flags take the platform defaults; unset trust seals are false, because a seal is a promise the dealership makes and is never inferred. The webchat block additionally carries welcome_message and position (br|bl) from the same settings.webchat key GET /widget/config serves — webchat.enabled stays UNGATED (it is a channel fact), only its appearance rides the add-on gate.

Also carries publishable_key (ClickUp 86e2t94e1): a browser-safe pk_ widget key (ADR 0033) DERIVED on every call from the workspace’s registered storefront domains (GET /tenant/storefront-domains), origin-locked to apex + www. of each, plus the Vitrina-hosted subdomain. Nothing is stored: adding or removing a domain changes the next value, so a new domain reaches the site at its next bootstrap window with no re-mint and no redeploy. null when the workspace has registered no domain — a key with an empty allow-list admits nothing, so returning one would look like success and behave like a 403. POST /publishable-keys is unchanged and remains the surface for arbitrary origins and isolated keys. It is NOT gated on dealer_site: what gates it is having a domain.

Also carries turnstile_site_key: the Cloudflare Turnstile site key every public form on the site must render, or null for "render no widget". Resolved from the same pair the intake routes verify with (POST /widget/consignments, /website-leads, /widget/appointments), so the challenge the page shows and the check the server runs cannot drift — a site that renders no widget against a verifying server has every submission refused. Mirrors GET /widget/config.turnstileSiteKey.

Without the add-on, or before the first Publicar, sitio/branding/analytics/content/contact/social/features/trust/financing are null and theme is absent, and the storefront falls back to its built-in defaults.

curl https://api.vitrinadev.com/api/v1/storefront \
  -H "Authorization: Bearer $VITRINA_KEY"

Responde: 200 · 400 · 401 · 403 · 404 · 409 · 429

POST /storefront/events

Record a storefront traffic/conversion event

Capture one page_view / vehicle_view / lead_submit signal from the tenant's own Vitrina-built storefront. Append-only, no person data — the optional dedup_key is an opaque session-ish hint, hashed server-side before storage. Requires the narrow, write-only storefront_events:write scope. A bogus or foreign-tenant vehicle_id is refused by the underlying composite FK and acked identically to success (no enumeration oracle). Feeds GET /insights/storefront.

Cuerpo

CampoTipoObligatorioRestricciones
eventpage_view \vehicle_view \lead_submit
vehicle_iduuid
dedup_keystringmín. 1, máx. 200
curl -X POST https://api.vitrinadev.com/api/v1/storefront/events \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ … }'

Responde: 201 · 400 · 401 · 403 · 404 · 409 · 429

POST /website-leads

Submit a website buyer inquiry (dealer-website write API)

Creates a CRM lead with website origin, attaching the inquired Vehículo when vehicle_id is given. Requires the write-only leads:intake scope (never leads:write). Returns a minimized \{ status \} ack — received on create, duplicate on dedup — and echoes no internal ids. Rate-limited like every authenticated endpoint; a hidden hp_website honeypot field silently drops spam.

Cuerpo

CampoTipoObligatorioRestricciones
namestringmín. 1, máx. 200
emailstringemail, máx. 254
phonestringmín. 5, máx. 40
messagestringmín. 1, máx. 5000
vehicle_iduuid
intentbuy \sell \financing \
hp_websitestringmáx. 500
turnstile_tokenstringmáx. 4096
consentobject
curl -X POST https://api.vitrinadev.com/api/v1/website-leads \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ … }'

Responde: 200 · 201 · 400 · 401 · 403 · 404 · 409 · 429

En esta página