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.