API REST v1
Base https://sandbox.minimarket.digital/v1 · respuestas en JSON · multi-tenant por subdominio
Autenticación
Todas las peticiones requieren la cabecera X-SYNCS-API-TOKEN con un token vigente, emitido por el tenant. El tenant se resuelve por el subdominio de la petición.
Verificar un token
curl -H "X-SYNCS-API-TOKEN: TU_TOKEN" https://sandbox.minimarket.digital/v1/acl
{
"status": "success",
"code": "200",
"message": "TOKEN_VALID",
"data": {
"emp_id": "tester",
"rol_id": "api",
"name": "Token de pruebas",
"tenant": "demo"
}
}
| Situación | Código | message |
|---|---|---|
| Falta la cabecera | 401 | UNAUTHORIZED |
| Token inexistente, anulado o expirado | 401 | TOKEN_INVALID |
| El subdominio no corresponde a un tenant | 403 | TENANT_UNKNOWN |
| El rol del token no puede ejecutar esa operación | 403 | FORBIDDEN |
| El token tiene una lista de IPs y la petición no viene de ninguna de ellas | 403 | IP_NOT_ALLOWED |
Cuando al token le quedan pocos días de vigencia, cada respuesta trae las cabeceras X-Token-Expires (fecha) y X-Token-Expires-In (días). Es el aviso para pedir el reemplazo antes de que deje de funcionar.
Alcance de los permisos
El rol del token decide qué operaciones puede ejecutar sobre cada recurso, y cada permiso tiene un alcance: Propios (lo que registró o tiene asignado la persona del token), Grupo (lo propio y lo de quienes la tienen como jefatura directa, en cadena) o Todo (toda la empresa). Un registro fuera del alcance no existe para ese token: se responde 404 NO_DATA, igual que si no existiera, y los listados no lo cuentan.
Tampoco existe lo que en el sistema está como borrador, anulado, archivado o eliminado: no se lista, no se lee, no se edita ni se vuelve a eliminar.
Estados con permiso propio
En los recursos con estado, algunos estados exigen su propio permiso (aprobar, rechazar, anular…). Dejar un registro en uno de esos estados, editarlo mientras sigue en él o sacarlo de él pide ese permiso, con su alcance. Si el rol no lo tiene, se responde 403 FORBIDDEN: comando, con el comando que falta (por ejemplo, FORBIDDEN: apv para Aprobar).
Formato de respuesta
Todas las respuestas comparten la misma envoltura. meta aparece solo en los listados.
{
"status" : "success" | "error",
"code" : "200",
"message" : "LIST | GET | ADD | UPDATE | DELETED | ...",
"data" : { ... } | null,
"meta" : { "page": 1, "per_page": 20, "count": 20, "total": 134 }
}
En los listados, data es un objeto indexado por el id de cada registro. El código HTTP de la respuesta coincide siempre con el campo code.
Métodos HTTP
| Método | Ruta | Cuerpo | Acción |
|---|---|---|---|
| GET | /v1/{x} | — | Lista paginada |
| GET | /v1/{x}/{id} | — | Un registro |
| POST | /v1/{x} | JSON completo | Agrega. Devuelve 201 ADD |
| PUT | /v1/{x}/{id} | JSON parcial | Actualiza los campos enviados |
| PATCH | /v1/{x}/{id} | JSON parcial | Idéntico a PUT |
| DELETE | /v1/{x}/{id} | — | Elimina el registro |
Sobre POST
Solo se graban las columnas de la lista blanca de cada recurso (los campos grabables que aparecen en cada endpoint); cualquier otra clave del JSON se ignora en silencio. Cada valor es un texto, un número, un booleano (se guarda como 1 o 0) o null; un arreglo o un objeto responde 400 FIELD_INVALID: campo. Los componentes de la clave primaria que no se envíen toman el valor ---. El id puede enviarse (máximo 45 caracteres alfanuméricos, _, - y .); si se omite, la API genera un UUID.
Recursos de sólo lectura
Algunos recursos sólo se consultan, porque su contenido es el resultado de un proceso del sistema y no un dato que se edita (el stock, por ejemplo). Su página lo indica, y POST, PUT, PATCH y DELETE responden 405 READ_ONLY.
Fechas y horas
Las horas se devuelven y se reciben en la zona horaria de la empresa, no en la de quien consulta: si la operación se realiza en Chile, la hora es la de Chile aunque el sistema que llame esté en otro país. El formato es YYYY-MM-DD HH:MM:SS. Los parámetros since y until se interpretan también en esa zona.
Las fechas sin hora (YYYY-MM-DD), como la de una factura o un vencimiento, no se convierten nunca: no son un instante, son el día que decidió la empresa.
Reintentos seguros (idempotencia)
Un POST que se reintenta crea el registro dos veces: pasa con un tiempo de espera agotado en el que la petición sí llegó, con un doble clic o con una cola que reenvía lo que cree fallido. Para evitarlo, envía la cabecera Idempotency-Key con un valor único por operación (un UUID, por ejemplo). La primera petición se ejecuta y su respuesta queda guardada; las siguientes con la misma clave devuelven esa misma respuesta, con la cabecera Idempotent-Replay: true, sin crear nada nuevo.
curl -X POST "https://sandbox.minimarket.digital/v1/customer" \
-H "X-SYNCS-API-TOKEN: TU_TOKEN" \
-H "Idempotency-Key: 7f3c1e40-9b2a-4c11-8e77-0a1b2c3d4e5f" \
-H "Content-Type: application/json" \
-d '{"name":"ACME"}'
La clave es de una operación: el token, el método, el recurso, la ruta y el cuerpo. Se recuerda 24 horas. Si se reutiliza en otra operación (otro recurso, otra ruta, otro método o un cuerpo distinto) se responde 409 IDEMPOTENCY_KEY_REUSED, nunca la respuesta de la otra; si la primera petición todavía está en curso, 409 IDEMPOTENCY_IN_PROGRESS. Quien no envíe la cabecera no nota ningún cambio.
Sobre PUT y PATCH
Ambos hacen exactamente lo mismo: actualización parcial. Solo se modifican las columnas presentes en el JSON, el resto queda intacto — no hace falta reenviar el registro completo. Se usa PATCH por corrección semántica y PUT por compatibilidad con clientes que no lo soportan. La API graba modified y modified_by automáticamente.
Los componentes de la clave identifican el registro y no se cambian: enviarlos responde 400 FIELD_READONLY: campo, con todos los que llegaron. En los recursos por idioma, lng_id indica qué traducción se edita y no se escribe.
Si el id no existe, ya se eliminó o queda fuera del alcance del permiso, responden 404 NO_DATA; si el JSON no trae ninguna columna válida, 400 NO_VALID_FIELDS.
Contenido con formato y enlaces
Los campos con formato (el cuerpo de un artículo, la descripción de un cliente, un producto, un almacén o una lista de precios, y los que la empresa marque con editor de contenido en el sistema) se sanean al guardar: se conservan párrafos, títulos, listas, tablas, enlaces e imágenes, y se quitan los scripts, los atributos de evento (onclick…), los estilos no permitidos, los enlaces con un esquema distinto de http, https, mailto y tel, y las imágenes que no son http, https o PNG, JPEG, GIF o WebP embebidas. No se rechaza: se guarda limpio, así que lo que se lee después puede diferir de lo enviado. Los campos de enlace aceptan una URL http o https completa, o vacío; cualquier otra cosa responde 400 FIELD_INVALID: campo.
Quien muestre ese contenido en una página debe sanearlo también, con su propia lista blanca: la API no presupone quién escribió cada registro, y lo guardado antes de esta regla no se volvió a sanear.
Enviar el cuerpo
Se acepta el body crudo con Content-Type: application/json (recomendado) o el parámetro d con el JSON codificado en la URL.
Uso desde el navegador (CORS)
Las llamadas desde servidor (curl, un backend, una app móvil) no tienen restricción de origen. Desde JavaScript, en cambio, solo se permite llamar a la API desde los dominios que el propio tenant tenga registrados: su sitio público, su portal, su aplicación, su extranet y su plataforma de formación. Otros orígenes se piden al administrador. El preflight OPTIONS se responde sin token.
Parámetros de listado
| Parám. | Descripción | Defecto | Ejemplo |
|---|---|---|---|
page | Número de página | 1 | ?page=2 |
per_page | Registros por página (máx. 1000) | 20 | ?per_page=50 |
since | Inicio del rango de creación (columna entered). Solo fecha = desde las 00:00:00 | — | ?since=2026-07-01 |
until | Fin del rango de creación. Solo fecha = hasta las 23:59:59 | — | ?until=2026-07-31 |
q | Búsqueda de texto en los campos indicados en cada recurso | — | ?q=abc |
s | Campo de ordenamiento | según recurso | ?s=entered |
o | Sentido: asc o desc | asc | ?o=desc |
f | Filtros exactos en JSON | — | ?f={"cat_id":"X"} |
i | Id del registro (equivale a /{recurso}/{id}). Con el id en la ruta, manda el de la ruta: un i distinto, vacío o en arreglo responde 400 PARAM_INVALID: i | — | ?i=ABC123 |
Los formatos aceptados en since / until son YYYY-MM-DD y YYYY-MM-DD HH:MM:SS; cualquier otro devuelve 400 PARAM_INVALID. Un campo de ordenamiento o un filtro que no pertenezca al recurso devuelve 400 en vez de ignorarse, y el valor de un filtro tiene que ser texto, número o booleano: un arreglo, un objeto o null responde 400 FILTER_INVALID: campo.
Códigos de error
| Código | message | Cuándo |
|---|---|---|
400 | NO_DATA_SENT, NO_DATA_ID, NO_VALID_FIELDS, FIELD_REQUIRED: x, FIELD_INVALID: x, FIELD_READONLY: x, y, FILTER_INVALID: x, PARAM_INVALID: x | Petición incompleta o con campos fuera de la lista blanca; un valor que no es texto, número, booleano ni null, o una URL que no es http(s) (FIELD_INVALID); una columna de la clave o un campo calculado en un PUT/PATCH (FIELD_READONLY); un filtro con un valor que no es escalar (FILTER_INVALID); con el id en la ruta, un i distinto en la query o en el formulario (PARAM_INVALID: i) |
401 | UNAUTHORIZED, TOKEN_INVALID | Falta el token o no es válido |
403 | FORBIDDEN, FORBIDDEN: x, TENANT_UNKNOWN, IP_NOT_ALLOWED | El rol del token no tiene permiso sobre ese recurso (FORBIDDEN), o no tiene el que exige un estado o una exportación (FORBIDDEN: comando, con el comando que falta), o el subdominio no corresponde a ningún tenant (TENANT_UNKNOWN) |
404 | NO_DATA, NOT_FOUND | Sin resultados, ruta inexistente, o un registro que no existe, se eliminó o queda fuera del alcance del permiso |
405 | METHOD_NOT_ALLOWED, READ_ONLY | Método HTTP no soportado (METHOD_NOT_ALLOWED), o una escritura en un recurso de sólo lectura (READ_ONLY) |
409 | DUPLICATE, IDEMPOTENCY_KEY_REUSED, IDEMPOTENCY_IN_PROGRESS | Viola una clave única (nombre, código de barras, slug…), o reutiliza una Idempotency-Key en otra operación (IDEMPOTENCY_KEY_REUSED) o mientras la primera sigue en curso (IDEMPOTENCY_IN_PROGRESS) |
429 | RATE_LIMIT_EXCEEDED, TOO_MANY_ATTEMPTS, TOO_MANY_REQUESTS | Se superó el ritmo permitido. La respuesta trae Retry-After con los segundos que hay que esperar. Hay dos límites: peticiones por minuto de un mismo token, e intentos fallidos de autenticación desde una misma IP. |
500 / 503 | DB_ERROR, INTERNAL_ERROR, SANITIZER_UNAVAILABLE, SERVICE_UNAVAILABLE | Error interno o base de datos no disponible. SANITIZER_UNAVAILABLE: el servidor no puede sanear un campo con formato y no lo guarda sin sanear |
Colección de Postman
Toda la API lista para importar en Postman: una carpeta por módulo, las seis operaciones de cada recurso con sus parámetros y ejemplos, y pruebas que comprueban la envoltura de cada respuesta. Se genera en el momento a partir de esta misma documentación, así que nunca va por detrás de lo publicado.
Descargar la colección Descargar el entorno
Con la colección basta: el baseUrl ya apunta a esta API. Pega tu token en la variable token y envía cualquier petición. El entorno es opcional — sirve para apuntar la misma colección a otro servidor sin tocarla.
Recursos
Cada recurso tiene su propia página con el detalle de sus operaciones, campos y ejemplos.
Almacenes
Clientes y terceros
Planificación del transporte
Productos y servicios
Catálogo
Sólo lectura
Existencias por almacén, ubicación, lote y SKU: totales, disponibles, reservados y en tránsito.
Precios /v1/pricesUna línea de precio por lista (prd_prc_id) y SKU, ya creada al dar de alta la lista o el SKU. POST crea la que falta o actualiza la existente; el producto sale del SKU y la moneda de la lista. El precio de venta (price_sell) lo calcula la API: costo × (1 + margen/100), o el precio fijo si está vigente hoy.
Publicaciones internas
Sólo lectura
Administración de anuncios.
Noticias /v1/newsSólo lectura
Administración de noticias.