API REST v1
Base https://pionono-sandbox.extranet.business/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://pionono-sandbox.extranet.business/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.
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. 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.
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://pionono-sandbox.extranet.business/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 se recuerda 24 horas. Si se reutiliza con un cuerpo distinto se responde 409 IDEMPOTENCY_KEY_REUSED; 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.
Si el id no existe (o ya se eliminó) responden 404 NO_DATA; si el JSON no trae ninguna columna válida, 400 NO_VALID_FIELDS.
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}) | — | ?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.
Códigos de error
| Código | message | Cuándo |
|---|---|---|
400 | NO_DATA_SENT, NO_DATA_ID, NO_VALID_FIELDS, FIELD_REQUIRED: x, FILTER_INVALID: x, PARAM_INVALID: x | Petición incompleta o con campos fuera de la lista blanca |
401 | UNAUTHORIZED, TOKEN_INVALID | Falta el token o no es válido |
403 | FORBIDDEN, TENANT_UNKNOWN | El rol del token no tiene permiso sobre ese recurso (FORBIDDEN), o el subdominio no corresponde a ningún tenant (TENANT_UNKNOWN) |
404 | NO_DATA, NOT_FOUND | Sin resultados o ruta inexistente |
405 | METHOD_NOT_ALLOWED | Método HTTP no soportado |
409 | DUPLICATE | Viola una clave única (nombre, código de barras, slug…) |
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, SERVICE_UNAVAILABLE | Error interno o base de datos no disponible |
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.
Catálogo
Catálogo
Catálogo de productos: dimensiones, códigos de barra, unidades de compra/venta y datos para la tienda en línea.
Stock /v1/stockExistencias por almacén, ubicación, lote y SKU: totales, disponibles, reservados y en tránsito.
Listas de Precios /v1/price-listCabeceras de listas de precios, con su moneda (cur_id) y factor de conversión.
Precios /v1/pricesDetalle de precios por producto/SKU dentro de una lista de precios (prd_prc_id).