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ónCódigomessage
Falta la cabecera401UNAUTHORIZED
Token inexistente, anulado o expirado401TOKEN_INVALID
El subdominio no corresponde a un tenant403TENANT_UNKNOWN
El rol del token no puede ejecutar esa operación403FORBIDDEN
El token tiene una lista de IPs y la petición no viene de ninguna de ellas403IP_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étodoRutaCuerpoAcción
GET/v1/{x}Lista paginada
GET/v1/{x}/{id}Un registro
POST/v1/{x}JSON completoAgrega. Devuelve 201 ADD
PUT/v1/{x}/{id}JSON parcialActualiza los campos enviados
PATCH/v1/{x}/{id}JSON parcialIdé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ónDefectoEjemplo
pageNúmero de página1?page=2
per_pageRegistros por página (máx. 1000)20?per_page=50
sinceInicio del rango de creación (columna entered). Solo fecha = desde las 00:00:00?since=2026-07-01
untilFin del rango de creación. Solo fecha = hasta las 23:59:59?until=2026-07-31
qBúsqueda de texto en los campos indicados en cada recurso?q=abc
sCampo de ordenamientosegún recurso?s=entered
oSentido: asc o descasc?o=desc
fFiltros exactos en JSON?f={"cat_id":"X"}
iId 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ódigomessageCuándo
400NO_DATA_SENT, NO_DATA_ID, NO_VALID_FIELDS, FIELD_REQUIRED: x, FILTER_INVALID: x, PARAM_INVALID: xPetición incompleta o con campos fuera de la lista blanca
401UNAUTHORIZED, TOKEN_INVALIDFalta el token o no es válido
403FORBIDDEN, TENANT_UNKNOWNEl rol del token no tiene permiso sobre ese recurso (FORBIDDEN), o el subdominio no corresponde a ningún tenant (TENANT_UNKNOWN)
404NO_DATA, NOT_FOUNDSin resultados o ruta inexistente
405METHOD_NOT_ALLOWEDMétodo HTTP no soportado
409DUPLICATEViola una clave única (nombre, código de barras, slug…)
429RATE_LIMIT_EXCEEDED, TOO_MANY_ATTEMPTS, TOO_MANY_REQUESTSSe 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 / 503DB_ERROR, SERVICE_UNAVAILABLEError 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

Comercial

Comercial

Logística