Vitrina API

Sucursales

Crear, leer, corregir y desactivar las sucursales de una automotora — con el código de comuna que las ancla y lo que pasa con los autos cuando una se apaga.

Una sucursal es el lugar físico donde está un auto. Es el bloque sucursal que viene embebido en cada unidad del stock y el valor del filtro ?sucursal=.

Cinco endpoints, CRUD completo. Leer pide tenant:read; escribir, tenant:write.

Ver las que hay

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

En un workspace recién creado:

{ "data": [] }

Esta lista no pagina y no trae meta. Una automotora tiene sucursales, no miles.

Crear una

Solo name es obligatorio. Todo lo demás ayuda a que la sucursal se vea bien en el sitio:

curl -X POST https://api.vitrinadev.com/api/v1/locations \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Sucursal Providencia",
    "address_street": "Av. Nueva Providencia",
    "address_number": "2214",
    "comuna_code": "13123",
    "phone": "+56229876543",
    "email": "[email protected]",
    "timezone": "America/Santiago"
  }'
{
  "data": {
    "id": "6812d9f0-9bed-44b5-91df-4e5b90941f6b",
    "tenant_id": "00000000-0000-4000-8000-000000000001",
    "name": "Sucursal Providencia",
    "address": null,
    "address_street": "Av. Nueva Providencia",
    "address_number": "2214",
    "address_unit": null,
    "comuna_code": "13123",
    "region_code": "13",
    "address_source": "manual",
    "phone": "+56229876543",
    "email": "[email protected]",
    "manager_name": null,
    "hours": null,
    "timezone": "America/Santiago",
    "geo": null,
    "notes": null,
    "arrival_info": null,
    "metadata": {},
    "is_active": true,
    "display_seq": 1,
    "display_id": "B-1",
    "created_at": "2026-09-15T18:34:09.375Z",
    "updated_at": "2026-09-15T18:34:09.375Z"
  }
}

Tres campos que no mandé y sin embargo están:

region_code salió de comuna_code. No lo mandes tú: se deriva, y mandarlo no lo cambia.

address_source quedó en "manual" porque escribí la dirección campo por campo. La API lo estampa para distinguir una dirección que escribió una persona de una que llegó por otro camino.

display_id es B-1, y la segunda sucursal que creé quedó como B-2. Es un identificador corto y legible que se asigna por orden de creación, para que una persona pueda nombrar la sucursal sin leer un uuid. Los endpoints que reciben {id} aceptan cualquiera de los dos.

El código de comuna

comuna_code es el código CUT oficial de cinco dígitos, no el nombre de la comuna ni el id de ningún portal. Mandarle otra cosa falla, y el mensaje te lo dice:

curl -X POST https://api.vitrinadev.com/api/v1/locations \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Sucursal Maipú", "comuna_code": "131" }'
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": {
      "body": [{
        "path": "comuna_code",
        "message": "comuna_code must be the official 5-digit CUT code (13114 = Las Condes), never a comuna name and never a portal id",
        "code": "invalid_string"
      }]
    },
    "field_errors": {
      "comuna_code": "comuna_code must be the official 5-digit CUT code (13114 = Las Condes), never a comuna name and never a portal id"
    },
    "requestId": "4c1e5e23-ae89-4e57-a711-5e962e8f4a8b"
  }
}

Fíjate en field_errors: es el mismo mensaje que details, pero ya indexado por campo, listo para pintarlo bajo el input que corresponde. Está en toda validación de cuerpo.

Omitir un campo obligatorio se ve igual:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": { "body": [{ "path": "name", "message": "Required", "code": "invalid_type" }] },
    "field_errors": { "name": "Required" },
    "requestId": "45c582f0-741c-4592-95bc-c2b4f679b7d2"
  }
}

Leer y corregir

curl https://api.vitrinadev.com/api/v1/locations/6812d9f0-9bed-44b5-91df-4e5b90941f6b \
  -H "Authorization: Bearer $VITRINA_KEY"

El PATCH es parcial: manda solo lo que cambia.

curl -X PATCH https://api.vitrinadev.com/api/v1/locations/6812d9f0-9bed-44b5-91df-4e5b90941f6b \
  -H "Authorization: Bearer $VITRINA_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "manager_name": "Paula Riquelme",
    "hours": { "lun_vie": "09:00-19:00", "sab": "10:00-14:00" }
  }'
{
  "data": {
    "id": "6812d9f0-9bed-44b5-91df-4e5b90941f6b",
    "name": "Sucursal Providencia",
    "manager_name": "Paula Riquelme",
    "hours": { "sab": "10:00-14:00", "lun_vie": "09:00-19:00" },
    "updated_at": "2026-09-15T18:34:26.470Z"
  }
}

hours es un objeto libre: la API lo guarda tal cual y no valida sus llaves. Elige una convención y úsala en todas tus sucursales.

Una sucursal que no existe responde 404:

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Sucursal no encontrada",
    "requestId": "75a3d88b-8703-42a2-9c40-127fec0dfcc3"
  }
}

Desactivar

El DELETE no borra: apaga.

curl -X DELETE https://api.vitrinadev.com/api/v1/locations/094e2408-9b01-4ab6-ae65-c102a02310d1 \
  -H "Authorization: Bearer $VITRINA_KEY"

204, sin cuerpo. La sucursal desaparece del listado normal:

[
  { "name": "Sucursal Providencia", "display_id": "B-1", "is_active": true },
  { "name": "Sucursal Vitacura",    "display_id": "B-3", "is_active": true }
]

…y sigue ahí cuando la pides:

curl "https://api.vitrinadev.com/api/v1/locations?include_inactive=true" \
  -H "Authorization: Bearer $VITRINA_KEY"
[
  { "name": "Sucursal Providencia", "display_id": "B-1", "is_active": true },
  { "name": "Sucursal Maipú",       "display_id": "B-2", "is_active": false },
  { "name": "Sucursal Vitacura",    "display_id": "B-3", "is_active": true }
]

Es a propósito: los autos, las ventas y la agenda que apuntan a esa sucursal siguen apuntando a algo con nombre. Para reactivarla, PATCH con { "is_active": true }.

Trampa

Apagar una sucursal no esconde sus autos

Desactivé Sucursal Maipú con el DELETE de arriba. El Mazda CX-5 que estaba ahí siguió apareciendo en GET /stock, con su bloque sucursal completo apuntando a la sucursal apagada, y GET /stock?sucursal=094e2408-… siguió devolviéndolo.

is_active gobierna el listado de sucursales, no el lote público. Si cierras una sucursal de verdad, mueve sus autos a otra o sácalos del lote antes — apagar la sucursal no hace ninguna de las dos cosas.

Lo que sigue

En esta página