Crea una clave de API en Configuración → Configuración de API — eligiendo acceso de Lectura o Lectura/Escritura y un alcance de empleado opcional — y envíala en el encabezado x-api-key a la API REST v1. Los webhooks salientes hacen POST de JSON a tus URLs al crear o editar clientes, ventas, recepciones, artículos y órdenes de trabajo, y las integraciones de Sidekick y Zapier usan esta misma base.
Cuando las integraciones incluidas no cubren lo que necesitas — un sitio web a la medida, un panel interno, una herramienta de almacén — la API REST le da a tu propio software los mismos datos que usa el POS. Combínala con los webhooks salientes y tus sistemas pueden reaccionar a la actividad de la tienda en el momento en que ocurre, sin estar consultando.
Las claves de API y los webhooks se administran en Configuración, que requiere el permiso del módulo Configuración (permisos y plantillas).
Qué cubre la API
La API está versionada como v1 y la mayoría de los endpoints están documentados en una especificación OpenAPI, así que puedes explorarla de forma interactiva o generar clientes. Los endpoints cubren lo ancho de la aplicación: artículos, kits de artículos, ventas, clientes, proveedores, empleados, recepciones, tarjetas de regalo, gastos y categorías de gastos, facturas, citas y tipos de cita, entregas, reglas de precios, cajas, tiendas, informes, etiquetas, niveles, modificadores, atributos, categorías, fabricantes, tipos de venta y solicitudes de permiso.
La referencia de endpoints vive en la página de la API. Las solicitudes van contra tu propia tienda, por ejemplo:
https://tutienda.phppointofsale.com/index.php/api/v1/items
Algunas convenciones aplican en todas partes:
- No hay
PUTniPATCH. Creas conPOST /<recurso>y actualizas conPOST /<recurso>/{id}— el id en la URL es lo que lo convierte en actualización. - Dos endpoints llevan un segmento extra en la ruta en lugar de un id simple: las facturas se direccionan por tipo,
customerosupplier(GET /invoices/customer,POST /invoices/supplier/{id}), y los pagos de factura son un sub-recurso — registra uno conPOST /invoices/payments/{tipo}y consulta uno conGET /invoices/payments/{tipo}/{payment_id}. - Las entregas solo pueden actualizarse, con
POST /deliveries/{id}contra el id de una entrega existente — las entregas las crea el POS cuando una venta se envía, no la API, y no existe endpoint batch de entregas. - Las solicitudes de permiso son el único recurso donde una clave de Lectura puede escribir:
POST /permission_requestsenvía una solicitud yPOST /permission_requests/approve/{id}aprueba una — el lado API de las solicitudes de permiso.
Crear una clave de API
- Ve a Configuración y busca API para abrir Configuración de API.
- La tabla Claves de API lista las claves existentes con su Descripción, la terminación de la clave, sus Permisos, su Alcance del empleado y un enlace Eliminar.
-
Haz clic en Agregar clave de API. La ventana genera una clave y ofrece:
- Descripción — nombra el sistema que usará la clave (una clave por integración hace que revocar sea indoloro).
- Clave API — el valor generado, con un botón de Copiar al portapapeles. Cópiala ahora: por seguridad, después de guardar solo se muestran los últimos caracteres.
- Permisos — Leer (consultar datos; lo único que puede escribir son los endpoints de solicitudes de permiso de arriba) o Leer escribir (también puede crear, actualizar y eliminar).
- Alcance del empleado — Sin alcance (acceso completo al nivel elegido) o un empleado específico. Una clave con alcance solo llega a los módulos y acciones que los permisos de ese empleado permiten. Una clave para tu herramienta de surtido puede limitarse igual que una cuenta de empleado de surtido.
- Confirma el aviso (te recuerda que la clave no volverá a mostrarse) y la clave queda guardada.
Para revocar una clave, haz clic en Eliminar en su fila y confirma — las solicitudes con esa clave dejan de funcionar de inmediato.
Qué verifica una clave con alcance
Para una clave con alcance, la mayoría de los endpoints verifican el módulo del mismo nombre, y la regla por defecto es: leer exige el permiso de búsqueda de ese módulo, crear o actualizar exige Agregar, actualizar y eliminar exige Eliminar. Sin embargo, el módulo no siempre corresponde uno a uno con el endpoint, y algunos endpoints verifican acciones distintas:
| Endpoint(s) | Módulo verificado | Acciones que difieren de la regla por defecto |
|---|---|---|
attributes, categories, manufacturers, modifiers, tags
|
Inventario (artículos) | escribir en categorías, etiquetas y fabricantes verifica Administrar categorías, Administrar etiquetas, Manejo de Fabricantes |
registers |
Tiendas | — |
sale_types, tiers
|
Ventas | endpoints de solo lectura |
expenses_categories |
Gastos | escribir verifica Administrar categorías |
sales |
Ventas |
POST verifica Venta completa (completar venta), DELETE verifica Eliminar venta
|
receivings |
Recibos |
POST verifica Editar entrada, DELETE verifica Eliminar entrada
|
invoices, appointments
|
Facturas, citas |
POST verifica las acciones de agregar y de editar a la vez (Agregar factura y Editar factura; Añadir citas y Editar citas) |
reports |
Informes | cada informe verifica además su propia acción de consulta |
Haz tu primera solicitud
Cada solicitud se autentica enviando la clave en el encabezado x-api-key:
curl -H "x-api-key: TU_CLAVE_DE_API" \
"https://tutienda.phppointofsale.com/index.php/api/v1/items"
Una respuesta exitosa devuelve JSON. Los errores devuelven un pequeño sobre JSON, {"status": false, "error": "..."}, y el código HTTP te dice de qué tipo es:
-
403 — sin clave, o clave inválida (
Invalid API key); también una clave con alcance cuyo empleado no tiene el módulo o la acción que la solicitud exige. - 401 — el nivel de la clave es insuficiente (una clave de Leer intentando escribir), o alcanzaste el límite de tasa.
Vale la pena diseñar alrededor del límite de tasa: las solicitudes se cuentan por clave de API, sumando todos los endpoints juntos, y el presupuesto es de 60 solicitudes por ventana de 60 segundos. Al pasarte, la respuesta es HTTP 401 con {"status":false,"error":"This API key has reached the time limit for this method"} — y cada solicitud rechazada reinicia la ventana, así que el contador solo se limpia tras un minuto completo sin enviar nada. Cuando lo veas, detente 60 segundos; insistir en bucle solo te mantiene bloqueado. Espacia las llamadas, guarda en caché lo que consultes y usa los endpoints batch de abajo para hacer más por solicitud.
Leer listados: paginación y filtros
Los endpoints de listado aceptan limit y offset, y toda respuesta de listado incluye el encabezado de respuesta x-total-records con el total de filas — léelo para saber cuándo dejar de paginar. Los topes varían por recurso:
| Recurso |
limit por defecto |
limit máximo |
|---|---|---|
| La mayoría de los recursos (clientes, proveedores, empleados, tarjetas de regalo, gastos, citas, facturas, kits de artículos, reglas de precios, cajas, tiendas, entregas…) | 20 | 100 |
items |
20 | 1000 |
sales, receivings
|
500 | 1000 |
reports |
el ajuste Número de artículos por página de tu tienda (20 si no está definido) | 500 |
modifiers |
devuelve la lista completa — sin paginación | — |
La mayoría de los listados también aceptan search, search_field, sort_col, sort_dir y location_id donde tienen sentido — la especificación en /api.php documenta qué campos admite cada recurso.
Las búsquedas de ventas van más lejos, porque las ventas son el conjunto de datos más grande. GET /sales acepta:
-
verbosity(oprojection) —minimal,mediumofull(el valor por defecto). Usaminimalcuando solo necesites los campos de cabecera;fulldevuelve cada renglón, pago e impuesto. -
customer_id, oemail_addresspara buscar al cliente por correo. -
suspended_type— una lista separada por comas delayaway,estimateo el nombre de un tipo de venta, para traer ventas suspendidas en lugar de completadas. - Rangos de fechas:
start_date/end_date(momento de la venta),start_date_created/end_date_created,start_date_updated/end_date_updatedystart_payment_date/end_payment_date— cada uno acepta una fecha o una fecha con hora. Agregainclude_created_sales_in_rangepara incluir además las ventas creadas dentro del rango.
Las recepciones admiten los mismos rangos start_date/end_date y de creación/actualización.
Escribir datos y los endpoints batch
Las escrituras individuales son POST /<recurso> (crear) y POST /<recurso>/{id} (actualizar). Cuando tienes muchos cambios, diecisiete recursos exponen además POST /<recurso>/batch: tipos de cita, citas, atributos, categorías, clientes, empleados, gastos, categorías de gastos, tarjetas de regalo, kits de artículos, artículos, fabricantes, modificadores, reglas de precios, cajas, proveedores y etiquetas. El cuerpo agrupa el trabajo:
{
"create": [ { "...": "registros a crear" } ],
"update": [ { "person_id": 12, "...": "campos modificados" } ],
"delete": [ 34, 56 ]
}
Cada sección es opcional, delete lleva ids simples, y la respuesta devuelve las mismas tres secciones con los registros resultantes (o un marcador de error por registro). Una llamada batch cuenta como una sola solicitud contra el límite de tasa, lo que la vuelve la herramienta correcta para importaciones y sincronizaciones nocturnas.
Las escrituras a ventas, recepciones, clientes y citas aceptan una bandera extra en el cuerpo: "skip_webhook": true. Suprime el webhook saliente que ese guardado dispararía normalmente — úsala cuando quien escribe es tu integración, para que tu propio endpoint de webhook no reciba el eco del cambio que acaba de empujar y entre en bucle.
Reintentar un cargo con tarjeta sin riesgo
POST /sales/charge_card acepta una idempotency_key opcional en el cuerpo: una cadena única tuya, de hasta 64 caracteres, que identifica un cargo concreto.
Al enviarla, la primera solicitud se procesa normalmente y la clave se registra junto con el resultado. Si llega otra vez la misma clave con los mismos datos del cargo, se devuelve la respuesta guardada en lugar de volver a cobrar la tarjeta — que es justo lo que quieres tras un tiempo de espera agotado o una conexión caída, cuando no sabes si la solicitud original llegó a pasar.
Dos guardas la hacen confiable:
- Reutilizar una clave con datos de cargo distintos se rechaza (
idempotency_key was already used for a different charge request) en lugar de devolver una respuesta que no corresponde. - Un reintento que llega mientras la primera solicitud sigue corriendo se rechaza con un conflicto (
A charge with this idempotency_key is already in progress), así que dos reintentos no pueden cobrar ambos.
Genera una clave nueva por cada cargo previsto — lo habitual es un UUID — y reutilízala solo al reintentar ese mismo cargo.
Ejecutar informes por la API
Todos los informes del catálogo de informes se pueden llamar. GET /reports devuelve un catálogo con las 138 claves de informe, cada una con el modelo que la respalda, la acción de permiso del módulo Informes que verifica, y las entradas que acepta (rangos de fechas, ids de tienda, filtros de lista y demás). GET /reports/{report_key} ejecuta uno, tomando esas entradas como parámetros de consulta más limit (tope de 500) y offset. Una clave con alcance necesita el módulo Informes más la acción de consulta del informe individual.
Webhooks salientes
Los webhooks te empujan los eventos en lugar de obligarte a consultar. Abre Configuración y busca ganchos para encontrar la sección Ganchos de red (Web Hooks); pega la URL de tu endpoint en cualquiera de los diez campos, tal como aparecen etiquetados en la aplicación:
- Nuevo URL del Web Hook del cliente y Editar URL de enlace web del cliente
- Nueva URL de gancho web de venta y Editar URL de enlace web de venta
- Nuevo gancho web receptor y Editar URL de enlace web de recepción
- Gancho web de nuevo elemento y Editar elemento Web Hook
- Gancho web de nueva orden de trabajo y Editar orden de trabajo Web Hook
Cuando ocurre el evento, el POS envía un POST HTTP a tu URL con el encabezado Content-Type: application/json. Lo que trae el cuerpo depende del evento:
- Ventas, recepciones y órdenes de trabajo envían el registro completo que se creó o editó — renglones, pagos y todo.
- Clientes y artículos envían los campos que se enviaron en ese guardado, que no es necesariamente el registro completo. Trata la carga como una notificación de cambio que lleva el id, y vuelve a leer el registro completo por la API cuando necesites todos los campos.
Dos peculiaridades de tiempo que conviene prever. Guardar un artículo dispara el gancho de Editar elemento Web Hook en cada guardado de un artículo existente — y crearlo normalmente también lo dispara, porque el formulario de artículo remata la inserción con escrituras adicionales (precios, imágenes, datos por tienda) dentro del mismo guardado. Espera que un artículo recién creado golpee tanto la URL de artículo nuevo como la de artículo editado, y deduplica por item_id. El gancho de Editar orden de trabajo Web Hook, en cambio, solo se dispara cuando la orden de trabajo se guarda desde la pantalla de órdenes de trabajo — las ediciones que ocurren por otros caminos no lo activan.
La solicitud expira a los cinco segundos aproximadamente y no se reintenta, así que haz que tu endpoint responda rápido (encola el trabajo y responde de inmediato). Un receptor mínimo en PHP:
<?php
$jsonStr = file_get_contents("php://input");
$json = json_decode($jsonStr);
Los POST de webhook van sin firma — no hay secreto ni encabezado de firma, así que cualquiera que conozca la URL puede enviarte una solicitud de aspecto idéntico. Trata la URL misma como un secreto (usa una ruta larga e inadivinable) y verifica lo que importe releyendo el registro por la API en lugar de confiar en la carga. Si tu integración también escribe por la API, envía skip_webhook en esas escrituras para que no reciba de vuelta sus propios cambios.
Con eso alcanza para la mayoría de las integraciones reactivas: sincronizar un cliente nuevo con tu plataforma de correo, avisar a un canal cuando cierra una venta grande, o arrancar el surtido cuando cambia una orden de trabajo. Deja un campo vacío para desactivar ese evento.
Algo que nunca configuras aquí: las integraciones de ecommerce. Conectar Shopify o WooCommerce registra sus propios webhooks entrantes automáticamente, así que los pedidos y cambios de catálogo fluyen hacia el POS sin tocar los campos de Ganchos de red — ver cómo funciona la sincronización de ecommerce, conectar Shopify y conectar WooCommerce.
Integración de reseñas con Sidekick
Sidekick puede pedir automáticamente una reseña a los clientes después de comprar. Se configura por tienda:
- Ve a Tiendas en el menú izquierdo, selecciona la tienda y haz clic en Editar.
- Abre la pestaña Integraciones y baja hasta el campo Clave de API de Compañero (así etiquetada en la aplicación: la clave de API de Sidekick), cerca del final.
- Pega ahí la clave de API de tu cuenta de Sidekick.
- Marca Sidekick solicita reseñas automáticamente después de la venta para enviar un correo de solicitud de reseña tras cada venta de esa tienda.
- Haz clic en Guardar.
Una venta solo dispara la solicitud de reseña cuando está asociada a un cliente con correo electrónico o número de teléfono registrado (ver perfiles de clientes). Para poner a tus clientes existentes a disposición de Sidekick, ve a Clientes → Clientes, haz clic en los puntos suspensivos (...) arriba a la derecha y elige Exportar a Sidekick — esto empuja tu lista completa de clientes una vez; los clientes nuevos y editados se sincronizan con Sidekick automáticamente. Repite los pasos de tienda por cada tienda adicional que quieras en Sidekick.
Conecta miles de apps con Zapier
Si prefieres no escribir nada de código, Zapier conecta PHP Point Of Sale con miles de aplicaciones a través de esta misma API — hojas de cálculo, herramientas de correo, CRMs y más, unidos con automatización de apuntar y hacer clic. Ver la integración con Zapier.
Preguntas frecuentes
¿Dónde encuentro mi clave de API después de crearla? No la encuentras — en la tabla de claves solo se muestran los últimos caracteres. Copia la clave desde la ventana de creación (hay un botón de copiar al portapapeles). Si se pierde, elimina la clave y crea una nueva.
¿Qué encabezado usa la autenticación?
x-api-key, enviado en cada solicitud con la clave como valor.
¿Cómo actualizo un registro si no hay PUT?
Correcto: las actualizaciones son POST /<recurso>/{id}. La presencia del id en la URL es lo que distingue una actualización de una creación.
¿Por qué recibo un error de permisos en una solicitud que antes funcionaba?
Tres causas típicas: la clave es de Leer y la solicitud escribe (401); la clave tiene Alcance del empleado y ese empleado perdió el permiso de módulo o de acción que el endpoint exige (403); o la clave fue eliminada (403 Invalid API key). Revisa la fila de la clave en Configuración → Configuración de API.
¿Por qué mis solicitudes empezaron a fallar de repente tras muchas llamadas?
Alcanzaste el límite de tasa: 60 solicitudes por ventana de 60 segundos, contadas por clave sumando todos los endpoints. La respuesta es HTTP 401 con "This API key has reached the time limit for this method". Deja de enviar por completo durante un minuto — las solicitudes hechas mientras estás sobre el límite reinician la ventana, así que reintentar en bucle te mantiene bloqueado. Después agrupa y espacia tus llamadas.
¿Cómo recorro un listado grande?
Envía limit y offset, y lee el encabezado de respuesta x-total-records para conocer el total. La mayoría de los recursos topan limit en 100; artículos, ventas y recepciones permiten hasta 1000; los informes topan en 500.
¿Los webhooks se reintentan si mi servidor está caído? No. El POST se envía una sola vez, con un tiempo de espera corto y sin reintentos. Usa la API REST para recuperar lo perdido — por ejemplo, consulta las ventas recientes al arrancar.
¿Qué trae el cuerpo del webhook?
Para ventas, recepciones y órdenes de trabajo, el registro completo que disparó el evento. Para clientes y artículos, los campos enviados en ese guardado — consulta el registro por la API cuando necesites todo. Todos los cuerpos son JSON con Content-Type: application/json, y ninguno va firmado, así que mantén en secreto tus URLs de webhook.
Sidekick no envía solicitudes de reseña — ¿por qué? Confirma que la Clave de API de Compañero de la tienda esté capturada, que Sidekick solicita reseñas automáticamente después de la venta esté marcada en esa tienda y que la venta tenga un cliente con correo electrónico o número de teléfono. Corre también Exportar a Sidekick una vez para que Sidekick conozca a tus clientes existentes.
¿Hay un entorno de pruebas para explorar los endpoints? La especificación OpenAPI en /api.php documenta la mayoría de los endpoints, parámetros y formas de respuesta; una clave de Leer contra tu propia tienda es la forma más segura de explorar datos reales.
Comentarios
0 comentarios
Inicie sesión para dejar un comentario.