PuntoAyuda

Buscar

Buscar en la ayuda

Desarrolladores · versión 1.1.0

API de integraciones de Punto

Con esta API, el sistema de un comercio que usa Punto (su tienda online, su app de pedidos, su sistema de gestión) le manda a Punto sus clientes y sus pedidos, y Punto hace el resto: descuenta stock, calcula los impuestos, numera el comprobante, emite la factura electrónica cuando corresponde y lo suma a los reportes, igual que si la venta se hubiera hecho en el local.

Los pedidos entran de dos maneras: como venta (un pedido cobrado, que se factura en el acto) o como orden (un pedido que el comercio prepara y entrega, y que se factura después).

Si es tu primera vez, empezá por Antes de empezar y seguí con la Guía de inicio.

Dirección base de la API

export PUNTO_API="https://api.punto.la"
export PUNTO_KEY="<tu key de integración>"

Antes de empezar

Esta API conecta el sistema de un comercio que usa Punto —su tienda online, su app de pedidos, su sistema de gestión— con Punto. La programás vos, con la autorización del comercio, y necesitás tres cosas que el comercio prepara desde su panel de Punto:

  1. La API de integraciones activa. Es un complemento del plan del comercio. Si al emitir la key la opción Integración de ventas aparece apagada, el comercio tiene que pedirle a Punto que la active. Sin el complemento, todas las llamadas responden 403 con reason: addon_api_required.
  2. Una caja para tu sistema. Cada venta y cada orden se registran contra una caja de Punto. El comercio la crea en Configuración › Sucursales, como cualquier otra caja, y esa caja decide lo fiscal: si tiene cargados el timbrado y el punto de expedición, las ventas salen con factura electrónica; si no, se registran igual, sin factura. Pedile una caja dedicada a tu sistema, sin ningún dispositivo conectado: si en la misma caja también vende una persona, los dos numeran comprobantes de la misma serie.
  3. Una key de integración, que el comercio emite y te pasa. Ver Autenticación.

Todas las direcciones de esta referencia son relativas a la dirección base de la API, que en los ejemplos aparece como $PUNTO_API. Los cuerpos y las respuestas son JSON en UTF-8, y las fechas, en la zona horaria del comercio.

Guía de inicio

El recorrido completo, de la key a la factura. Cada paso tiene su sección en la referencia, con todos los campos.

1. Probá la key con las cajas disponibles.

curl "$PUNTO_API/v1/integrations/registers" \
  -H "Authorization: Bearer $PUNTO_KEY"

La respuesta lista las cajas que tu key puede usar. Guardá el registerId de la que el comercio preparó para tu sistema, que tiene que estar con available: true: una caja que usa un dispositivo del comercio no se puede usar desde tu sistema, y la que empiece a usar tu sistema deja de poder usarse en el local; si su electronicInvoice es true, las ventas que registres ahí salen con factura electrónica. Si recibís 401, la key está mal copiada; si recibís 403, mirá error.details.reason en Errores.

2. Traé el catálogo y los medios de pago. Cada línea de un pedido nombra un artículo de Punto por su itemId (o por su sku), y cada venta, el medio de pago por su id:

curl "$PUNTO_API/v1/items?after=&limit=200" -H "Authorization: Bearer $PUNTO_KEY"
curl "$PUNTO_API/v1/payment-methods" -H "Authorization: Bearer $PUNTO_KEY"

3. Creá el cliente. Con el identificador del cliente en tu sistema (externalId). Si el comercio ya lo tenía cargado con el mismo documento o teléfono, Punto te devuelve ese cliente en vez de duplicarlo:

curl -X POST "$PUNTO_API/v1/integrations/customers" \
  -H "Authorization: Bearer $PUNTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "externalId": "cliente-4812", "name": "Ana Gómez", "document": "4567890", "phone": "+595981000111" }'

4. Cargá la orden. Un pedido que el comercio tiene que preparar y entregar entra como orden. Nombrá al comprador con el mismo externalId:

curl -X POST "$PUNTO_API/v1/integrations/orders" \
  -H "Authorization: Bearer $PUNTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "WEB-10246",
    "registerId": "81c541da-640e-4891-a1a0-b32841e64c75",
    "fulfillment": "takeaway",
    "items": [ { "itemId": "10223f3b-2e3d-4339-8496-9f288d8be65b", "quantity": 2, "unitPrice": 26000 } ],
    "total": 52000,
    "customer": { "externalId": "cliente-4812" }
  }'

La orden entra pendiente: alguien del comercio la acepta o la rechaza. Consultá GET /v1/integrations/orders?orderId=WEB-10246 y mirá acceptance.status.

5. Facturala. Cuando el comprador pagó y la orden está aceptada, registrá la venta con fromOrderId:

curl -X POST "$PUNTO_API/v1/integrations/sales" \
  -H "Authorization: Bearer $PUNTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "WEB-10246",
    "fromOrderId": "WEB-10246",
    "registerId": "81c541da-640e-4891-a1a0-b32841e64c75",
    "items": [ { "itemId": "10223f3b-2e3d-4339-8496-9f288d8be65b", "quantity": 2, "unitPrice": 26000 } ],
    "total": 52000,
    "payment": { "method": "e7a1c3d5-2b49-4f60-8d17-5c9e0a3b6f24", "reference": "psp_7f3a91" }
  }'

Si tu sistema cobra y entrega en el momento, salteá la orden: mandá directo la venta, sin fromOrderId.

6. Consultá la factura y el KuDE. La factura electrónica se valida en segundo plano. Consultá la venta cada 30 segundos hasta que einvoice.sifenVerdict deje de ser pending, y mostrale al comprador el link einvoice.portalUrl, donde descarga su factura:

curl "$PUNTO_API/v1/integrations/sales?orderId=WEB-10246" -H "Authorization: Bearer $PUNTO_KEY"

Autenticación

Cada llamada lleva la key en el encabezado Authorization:

Authorization: Bearer <tu key>

Cómo se obtiene. La emite el comercio desde su panel de Punto, en Configuración › Keys de integración → Nueva key, eligiendo Integración de ventas en Qué puede hacer. Se muestra una sola vez. Una key emitida con otra opción de esa pantalla no sirve para esta API: responde 403 con reason: api_key_scope_missing, y no se puede convertir; el comercio emite una nueva.

Guardala en tu servidor. Nunca en el navegador, en una app que se instala en el teléfono ni en un repositorio de código: quien tenga la key puede registrar ventas a nombre del comercio.

Una key puede lo mismo que el usuario que la emitió. Si ese usuario no tiene permiso para vender, las llamadas responden 403; si solo tiene acceso a algunas sucursales, la key solo ve esas sucursales y sus cajas. Pedile al comercio que la emita con un usuario que tenga permiso para vender y acceso a la sucursal de tu caja.

Cortar el acceso. El comercio revoca la key desde la misma pantalla y deja de funcionar en el acto, sin tocar lo que ya se registró. Si el comercio da de baja el complemento de integraciones, todas sus keys dejan de responder.

Límites de uso

Cada key tiene dos límites, que se cuentan por key:

  • 60 llamadas por minuto.
  • 5.000 llamadas por día.

Al pasarte, la llamada responde 429 y no se procesa. Esperá un minuto antes de reintentar y espaciá los reintentos (por ejemplo, 1, 2, 4 y 8 segundos entre intentos). Para seguir el estado de una factura o de una orden alcanza con una consulta cada 30 segundos.

Otros topes: hasta 200 líneas por pedido, 200 artículos por consulta de stock y 100 clientes por página.

Errores

Toda respuesta tiene la misma forma. Si salió bien:

{ "ok": true, "data": { ... } }

Si no:

{
  "ok": false,
  "error": {
    "message": "El total del pedido no coincide con lo que suman sus líneas: mandaste 19000 y las líneas dan 22000.",
    "code": 422,
    "details": { "reason": "total_mismatch", "declared": 19000, "computed": 22000 }
  }
}

Programá contra error.details.reason, no contra el texto: el texto es para la persona que lee el registro y puede cambiar.

StatusQué significa y qué hacer
401Falta la key o no es válida (mal copiada o revocada). Revisá el encabezado; si sigue, pedile una key nueva al comercio.
403La key no puede hacer esto. Reintentar no lo arregla: lo resuelve el comercio (ver la tabla de abajo).
404No hay nada con ese identificador en este comercio.
409El estado del comercio impide la operación. No reintentes igual: hace falta que alguien lo resuelva en Punto o esperar a que cambie.
422El pedido tiene un dato que corregir. Corregilo y volvé a mandarlo.
429Pasaste el límite de uso. Esperá y reintentá.
5xxFalla de Punto. Reintentá con el MISMO identificador: es seguro (ver Idempotencia).

Los motivos más comunes y qué hacer:

reasonQué hacer
addon_api_requiredEl comercio no tiene activa la API de integraciones. Tiene que pedirle a Punto que la active.
api_key_scope_missingLa key no se emitió como Integración de ventas. El comercio emite una nueva con esa opción.
account_blocked, account_suspended, account_inactiveLa cuenta del comercio en Punto no está operativa (por ejemplo, plan vencido). Lo resuelve el comercio con Punto.
register_unavailableLa caja no existe, está inactiva o la key no tiene acceso a su sucursal. Revisá el registerId con GET /v1/integrations/registers.
item_unavailableUn artículo no existe o no está a la venta. details.line dice cuál. Volvé a sincronizar el catálogo.
total_mismatchEl total no coincide con lo que suman las líneas. Revisá precios, cantidades y cargos (ver Precios, impuestos y totales).
payment_method_unknownEl medio de pago no existe en el comercio. Usá un id de GET /v1/payment-methods.
payment_method_cashSe mandó efectivo. Una venta online no se cobra en el cajón del comercio: usá el medio con el que cobró tu sistema.
customer_not_foundEl cliente nombrado por id o externalId no existe en el comercio. Crealo primero o mandá sus datos.
order_pending_acceptanceLa orden todavía no fue aceptada. Esperá y consultá su estado.
invoice_range_exhaustedLa caja se quedó sin números de factura. El comercio tiene que cargar un timbrado nuevo.

Cada operación lista todos sus motivos en su sección.

Idempotencia

Las altas llevan el identificador del registro en tu sistema: orderId en ventas y órdenes, externalId en clientes, channelRef en citas. Es lo que garantiza que nada se registre dos veces, aunque la conexión se corte.

Un ejemplo. Tu sistema manda la venta del pedido WEB-10245, la conexión se corta y no ves la respuesta. ¿Se registró o no? No hace falta averiguarlo: mandá exactamente la misma llamada otra vez.

  • Si la primera había llegado, recibís la venta que ya existía, con el mismo número de factura, duplicated: true y status 200.
  • Si no había llegado, se registra ahora y recibís 201 con duplicated: false.

En los dos casos queda una sola venta y una sola factura. Lo mismo vale si dos llamadas del mismo pedido llegan a la vez: se registra una y las dos reciben el mismo resultado. Un reintento devuelve lo registrado aunque el catálogo haya cambiado después: nunca te va a decir que falló un pedido que ya está facturado.

Reglas del identificador: letras, números, punto, guion y guion bajo; hasta 45 caracteres en orderId y 64 en externalId. Usá siempre el identificador estable de tu sistema, nunca uno generado en cada intento.

Ventas y órdenes tienen cada una su espacio de orderId: el mismo pedido puede entrar primero como orden y después como venta con el mismo identificador.

Precios, impuestos y totales

Cada línea nombra un artículo del catálogo de Punto (por itemId o por sku), la cantidad y el precio unitario que cobró tu sistema. Punto compara ese precio con el de su lista (la del cliente, la de la sucursal o la del catálogo, en ese orden):

  • Por debajo de la lista, la diferencia queda registrada como descuento de la línea. Por ejemplo: el artículo vale 50.000 en Punto y tu tienda lo vendió a 45.000; la línea se registra a 50.000 con un descuento de 5.000. Así los reportes del comercio siguen mostrando el margen real.
  • Por encima de la lista, se registra el precio que mandaste.
  • Agregados (addons): se nombran por optionId y cantidad; el recargo lo pone el catálogo. El unitPrice de la línea es el precio con los agregados incluidos.
  • Envíos y recargos van en charges, como línea propia con su descripción. Nunca sumados al precio de un producto.

Los impuestos los calcula Punto con las mismas reglas que una venta en el local: tu sistema no manda impuestos ni totales por tasa. Tampoco manda el número de comprobante: lo asigna Punto. El impuesto de un cargo es el que indiques en taxId (ver GET /v1/taxes) o, si no indicás ninguno, el predeterminado del comercio.

El total se compara. En total mandás lo que cobraste (o vas a cobrar) al comprador, y Punto lo compara con lo que suman las líneas y los cargos. Si la diferencia es mayor al redondeo (una unidad de la moneda por línea), el pedido no se registra y responde 422 con reason: total_mismatch, declared (lo que mandaste) y computed (lo que suman las líneas). Nunca se factura algo distinto de lo que vio el comprador.

Los montos van en la moneda del comercio, como números (45000, no "45.000").

Clientes

Punto busca al comprador antes de crear uno nuevo, con la misma regla en todos los lugares donde se nombra a un comprador (el alta de clientes, las órdenes y las ventas). En este orden:

  1. Por su id en Punto, si lo mandás.
  2. Por su externalId, el identificador del cliente en tu sistema.
  3. Por su documento, aunque venga escrito distinto (1.234.567 y 1234567 son el mismo).
  4. Por su teléfono, aunque venga en otro formato.
  5. Si no aparece, lo crea.

Cuando lo encuentra por documento o por teléfono, el cliente queda vinculado a tu externalId, así las próximas veces lo podés nombrar solo con él. matchedBy en la respuesta del alta te dice cómo lo encontró. Un cliente que el comercio ya tenía vinculado a otro externalId tuyo se devuelve igual, con su referencia original.

Documento. document es el documento con el que se le factura. documentType dice de qué clase es: fiscal (el identificador tributario; en Paraguay, el RUC) o personal (el documento de identidad; en Paraguay, la cédula). Si no lo mandás, Punto lo deduce del formato; mandarlo evita errores.

Teléfono. Mandalo con código de país, en formato internacional (+595981000111). Un número sin código de país se interpreta con el país del comercio.

Para no duplicar datos, dos clientes del comercio no pueden tener el mismo teléfono ni el mismo documento personal. Si una actualización choca con otro cliente, responde 409 y details.customerId dice con cuál.

En órdenes y ventas podés nombrar al comprador de tres maneras: con su externalId (si ya lo creaste), con su id de Punto, o con sus datos completos para que Punto lo busque o lo cree en el momento. Sin datos del comprador, la venta sale a consumidor final.

Factura electrónica y KuDE

La factura electrónica depende del país del comercio. Hoy Punto la emite en Paraguay (SIFEN). Si la caja de tu sistema tiene la facturación electrónica configurada, cada venta emite su documento; si no, la venta se registra igual, sin documento electrónico.

En Paraguay el documento se envía a validar en segundo plano, así que la respuesta del alta todavía no trae el resultado: se consulta después.

Cómo seguirlo. Consultá GET /v1/integrations/sales?orderId=<id> (o la orden, si la facturaste desde una orden) hasta que einvoice.sifenVerdict deje de ser pending. Una consulta cada 30 segundos alcanza; la mayoría se resuelve en pocos minutos.

einvoiceQué significa
status: nullLa caja no emite factura electrónica. La venta está registrada igual.
sifenVerdict: pendingEnviada, esperando validación.
sifenVerdict: approved (issued: true)Aprobada. cdc y documentNumber son los datos del documento.
sifenVerdict: rejectedRechazada; sifenReason dice por qué. La corrige el comercio desde su panel. No reenvíes el pedido: ya está registrado.

issued: true significa que el documento fue aprobado, no solo enviado: un documento puede enviarse y ser rechazado después.

El KuDE. Es la representación imprimible de la factura electrónica, la que recibe el comprador. Queda disponible en el portal de Punto: einvoice.portalUrl es el link que tu sistema le muestra al comprador, por ejemplo en la confirmación del pedido o en el correo que le mandás. Esta API no devuelve el PDF. einvoice.qrUrl es el link de consulta pública del documento, por si querés imprimir su código QR en tu comprobante.

Órdenes: del pedido a la factura

Una orden es un pedido que el comercio prepara y entrega. Entra pendiente: en las cajas de la sucursal suena un aviso y alguien del comercio la acepta —y puede indicar en cuánto va a estar lista— o la rechaza con un motivo (out_of_stock, closed, too_busy, out_of_area u other). Recién aceptada pasa a la cola del equipo. Punto no te avisa de los cambios: consultá GET /v1/integrations/orders y mirá acceptance (pending; accepted, con etaMinutes y estimatedReadyAt; o rejected, con reason y note). Crear una orden no factura.

Cada orden trae trackingUrl: el link público donde tu comprador sigue su pedido desde el celular y se entera cuando está listo o en camino. Mostráselo en la confirmación de la compra.

Una orden aceptada se factura de dos maneras:

  1. En el local, como cualquier orden (por ejemplo, un envío que se cobra al entregarlo). Consultando GET /v1/integrations/orders vas a ver la venta en sale cuando se cobre.
  2. Desde tu sistema, con POST /v1/integrations/sales y fromOrderId: Punto registra la venta y la vincula a la orden. La orden se cierra cuando está facturada y entregada: un envío facturado antes de salir sigue abierto (y el comprador lo sigue en trackingUrl) hasta que se entrega. Si no mandás comprador, la venta toma el de la orden.

Una orden pendiente no se puede facturar (409 con order_pending_acceptance), y una rechazada tampoco (409 con order_rejected).

Para recibir órdenes, el comercio tiene que tener activado el módulo de órdenes; si no, la orden no entra y responde 422 con orders_module_disabled. Una orden con scheduledFor en otro día no le aparece al equipo hasta ese día. Una orden con envío (fulfillment: delivery) necesita al comprador y la dirección; para cobrar el envío con la tarifa del comercio, cotizalo con GET /v1/integrations/delivery-fee y mandalo como una línea más.

Agenda: servicios, horarios y citas

Si el comercio vende el tiempo de sus profesionales, tu sistema o tu bot pueden agendar con las mismas reglas que su caja. Necesita la agenda activada: sin ella, estas llamadas responden calendar_module_disabled.

  1. Qué se puede agendar. GET /v1/integrations/services da los servicios, cuánto duran, cuánto salen y quién los hace.
  2. Cuándo. GET /v1/integrations/availability da los horarios libres de un día. Sin userId vale cualquier profesional.
  3. Agendar. POST /v1/integrations/appointments con tu channelRef. El cliente se nombra igual que en una orden: por teléfono o documento, Punto lo encuentra o lo crea, sin duplicarlo.
  4. Mandarle el link. La respuesta trae manageUrl: ahí su cliente ve la cita, la cambia de horario o la cancela, sin cuenta. Y icsUrl, para sumarla a su calendario. Punto no se los manda: lo hacés vos.

Si el horario no entra, la respuesta es 422 con el motivo en error.details.reason y los horarios libres más cercanos en error.details.suggestions. Ofrecéselos a tu cliente en vez de pedirle que pruebe otra vez.

Anticipación. El comercio define con cuánto tiempo mínimo y hasta cuánto por adelantado se agenda por acá (por defecto, una hora y 60 días). Un horario fuera de esa ventana da booking_too_soon o booking_too_far.

Las fechas viajan en la hora del comercio, sin zona: 2026-11-10 10:30. Una cita a las 10:30 es a las 10:30 en el reloj del local.

Punto no te avisa de los cambios: el comercio puede mover o cancelar una cita desde su agenda, y su cliente desde el link. Consultá GET /v1/integrations/appointments cuando lo necesites.

Glosario

  • Comercio: el negocio que usa Punto y te autorizó a conectar tu sistema.
  • Sucursal: cada local del comercio. Tiene su stock, sus cajas y sus órdenes.
  • Caja: el punto de venta contra el que se registra cada pedido. Decide si la venta sale con factura electrónica y con qué numeración.
  • Punto de expedición: el código de establecimiento y punto de emisión que encabeza el número de factura (por ejemplo, 001-003). Lo tiene cargado cada caja que factura.
  • Timbrado: la autorización fiscal con la que la caja numera sus facturas. Cuando se agota o vence, la caja deja de facturar hasta que el comercio cargue uno nuevo.
  • KuDE: la representación imprimible de una factura electrónica en Paraguay, la que recibe el comprador.
  • CDC: el código de control que identifica a una factura electrónica en Paraguay.
  • Orden: un pedido que el comercio prepara y entrega, y que se factura después.
  • Venta: un pedido cobrado, que se registra y se factura en el acto.
  • Consumidor final: una venta sin comprador identificado.

Clientes

Alta, actualización y consulta de los clientes del comercio.

Consultar o listar clientes

GET/v1/integrations/customers

Con id o externalId, devuelve ese cliente. Sin ninguno de los dos, lista los clientes activos del comercio por páginas, ordenados por nombre, con búsqueda opcional por nombre, documento, teléfono o email.

Parámetros

CampoTipoDescripción
id
string (uuid)Identificador del cliente en Punto. Mandá este o externalId.
externalId
stringIdentificador del cliente en tu sistema. Mandá este o id.
q
stringBúsqueda por nombre, documento, teléfono o email (sin distinguir tildes ni mayúsculas).
limit
integerClientes por página (máximo 100).
offset
integerCuántos saltear (para la página siguiente, sumá limit).

Ejemplo de llamado

Un cliente por tu identificador

curl "$PUNTO_API/v1/integrations/customers?externalId=cliente-4812" \
  -H "Authorization: Bearer $PUNTO_KEY"

Buscar clientes

curl "$PUNTO_API/v1/integrations/customers?q=gomez&limit=50&offset=0" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
customer
objectEl cliente pedido por id o externalId.
customer.id
string (uuid)Identificador del cliente en Punto.
customer.externalId
string | nullTu identificador del cliente, si lo vinculaste.
customer.name
string
customer.fiscalName
string | null
customer.document
string | null
customer.documentType
string | nullValores: fiscal, personal.
customer.personalDocument
string | nullCédula guardada en la ficha.
customer.fiscalDocument
string | nullRUC guardado en la ficha.
customer.email
string | null
customer.phone
string | nullEn formato internacional, con +.
customer.address
string | null
customer.createdAt
string | nullFecha de alta en Punto.
customers
array<object>Una página del listado.
customers[].id
string (uuid)Identificador del cliente en Punto.
customers[].externalId
string | nullTu identificador del cliente, si lo vinculaste.
customers[].name
string
customers[].fiscalName
string | null
customers[].document
string | null
customers[].documentType
string | nullValores: fiscal, personal.
customers[].personalDocument
string | nullCédula guardada en la ficha.
customers[].fiscalDocument
string | nullRUC guardado en la ficha.
customers[].email
string | null
customers[].phone
string | nullEn formato internacional, con +.
customers[].address
string | null
customers[].createdAt
string | nullFecha de alta en Punto.
total
integerCantidad de clientes que coinciden con la búsqueda.
limit
integer
offset
integer

Respuesta de ejemplo

200 · El cliente (con id o externalId), o una página del listado.

{
  "ok": true,
  "data": {
    "customers": [
      {
        "id": "5d0c8a2e-3f41-4b7a-9e62-1c8d7f3a9b10",
        "externalId": "cliente-4812",
        "name": "Ana Gómez",
        "fiscalName": null,
        "document": "4567890",
        "documentType": "personal",
        "email": "[email protected]",
        "phone": "+595981000111",
        "address": "Av. España 1234",
        "createdAt": "2026-10-03 10:14:22"
      }
    ],
    "total": 1,
    "limit": 50,
    "offset": 0
  }
}

Códigos de respuesta

200El cliente (con id o externalId), o una página del listado.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
404No hay nada con ese identificador en este comercio.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Crear un cliente

POST/v1/integrations/customers

Da de alta un cliente del comercio con tu identificador (externalId). Antes de crearlo, Punto lo busca por externalId, documento y teléfono (ver Clientes): si ya existía, te lo devuelve sin cambiar sus datos y lo vincula a tu externalId. Para cambiar sus datos, usá PATCH.

Responde 201 si lo creó y 200 si ya existía.

Motivos de rechazo (error.details.reason, todos 422): customer_name_required (no existía y falta el nombre), customer_invalid (un dato con formato inválido, por ejemplo un teléfono que no es de ningún país), customer_not_found.

Cuerpo de la petición

CampoTipoDescripción
externalIdobligatorio
stringIdentificador del cliente en tu sistema. Ver Idempotencia. Hasta 64 caracteres.
name
stringNombre y apellido. Hasta 120 caracteres.
fiscalName
stringRazón social para la factura, si es distinta del nombre. Hasta 160 caracteres.
document
stringDocumento con el que se le factura. Al actualizarlo, reemplaza al anterior. Para guardar cédula y RUC a la vez usá personalDocument y fiscalDocument. Hasta 30 caracteres.
documentType
stringfiscal (en Paraguay, RUC) o personal (en Paraguay, cédula). Sin él, Punto lo deduce del formato. Valores: fiscal, personal.
personalDocument
stringCédula (documento personal) de la persona. Va junto al RUC en la misma ficha: mandá los dos cuando los tengas.
fiscalDocument
stringRUC (documento fiscal) con el que se factura, junto a fiscalName. Va junto a la cédula en la misma ficha.
email
string (email)Hasta 120 caracteres.
phone
stringCon código de país, en formato internacional (+595981000111). Sin código, se toma el país del comercio. Hasta 30 caracteres.
address
stringDirección principal. Hasta 250 caracteres.

Ejemplo de llamado

Alta de un cliente

curl -X POST "$PUNTO_API/v1/integrations/customers" \
  -H "Authorization: Bearer $PUNTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "externalId": "cliente-4812",
    "name": "Ana Gómez",
    "document": "4567890",
    "documentType": "personal",
    "email": "[email protected]",
    "phone": "+595981000111",
    "address": "Av. España 1234"
  }'

Respuesta (dentro de data)

CampoTipoDescripción
customer
objectUn cliente del comercio.
customer.id
string (uuid)Identificador del cliente en Punto.
customer.externalId
string | nullTu identificador del cliente, si lo vinculaste.
customer.name
string
customer.fiscalName
string | null
customer.document
string | null
customer.documentType
string | nullValores: fiscal, personal.
customer.personalDocument
string | nullCédula guardada en la ficha.
customer.fiscalDocument
string | nullRUC guardado en la ficha.
customer.email
string | null
customer.phone
string | nullEn formato internacional, con +.
customer.address
string | null
customer.createdAt
string | nullFecha de alta en Punto.
duplicated
booleantrue si ya existía un cliente con ese externalId.
matchedBy
string | nullSi el cliente ya existía en el comercio, cómo se lo encontró. null si se creó o si coincidió la externalId. Valores: document, phone.

Respuesta de ejemplo

200 · El cliente ya existía (por la misma externalId, o por documento o teléfono).

{
  "ok": true,
  "data": {
    "customer": {
      "id": "5d0c8a2e-3f41-4b7a-9e62-1c8d7f3a9b10",
      "externalId": "cliente-4812",
      "name": "Ana Gómez",
      "fiscalName": null,
      "document": "4567890",
      "documentType": "personal",
      "email": "[email protected]",
      "phone": "+595981000111",
      "address": "Av. España 1234",
      "createdAt": "2026-10-03 10:14:22"
    },
    "duplicated": false,
    "matchedBy": "document"
  }
}

201 · Cliente creado.

{
  "ok": true,
  "data": {
    "customer": {
      "id": "5d0c8a2e-3f41-4b7a-9e62-1c8d7f3a9b10",
      "externalId": "cliente-4812",
      "name": "Ana Gómez",
      "fiscalName": null,
      "document": "4567890",
      "documentType": "personal",
      "email": "[email protected]",
      "phone": "+595981000111",
      "address": "Av. España 1234",
      "createdAt": "2026-10-03 10:14:22"
    },
    "duplicated": false,
    "matchedBy": null
  }
}

Códigos de respuesta

200El cliente ya existía (por la misma externalId, o por documento o teléfono).
201Cliente creado.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Actualizar un cliente

PATCH/v1/integrations/customers

Cambia los datos de un cliente, nombrado por id o por externalId. Solo cambia lo que mandás.

Motivos de rechazo (error.details.reason): customer_not_found, customer_name_required (el cliente quedaría sin nombre), customer_invalid (422); customer_phone_taken, customer_document_taken (409: otro cliente ya tiene ese teléfono o documento; details.customerId dice cuál).

Parámetros

CampoTipoDescripción
id
string (uuid)Identificador del cliente en Punto. Mandá este o externalId.
externalId
stringIdentificador del cliente en tu sistema. Mandá este o id.

Cuerpo de la petición

CampoTipoDescripción
name
stringNombre y apellido. Hasta 120 caracteres.
fiscalName
stringRazón social para la factura, si es distinta del nombre. Hasta 160 caracteres.
document
stringDocumento con el que se le factura. Al actualizarlo, reemplaza al anterior. Para guardar cédula y RUC a la vez usá personalDocument y fiscalDocument. Hasta 30 caracteres.
documentType
stringfiscal (en Paraguay, RUC) o personal (en Paraguay, cédula). Sin él, Punto lo deduce del formato. Valores: fiscal, personal.
personalDocument
stringCédula (documento personal) de la persona. Va junto al RUC en la misma ficha: mandá los dos cuando los tengas.
fiscalDocument
stringRUC (documento fiscal) con el que se factura, junto a fiscalName. Va junto a la cédula en la misma ficha.
email
string (email)Hasta 120 caracteres.
phone
stringCon código de país, en formato internacional (+595981000111). Sin código, se toma el país del comercio. Hasta 30 caracteres.
address
stringDirección principal. Hasta 250 caracteres.

Ejemplo de llamado

Cambiar email y teléfono

curl -X PATCH "$PUNTO_API/v1/integrations/customers?externalId=cliente-4812" \
  -H "Authorization: Bearer $PUNTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "[email protected]", "phone": "+595981000222" }'

Respuesta (dentro de data)

CampoTipoDescripción
customer
objectUn cliente del comercio.
customer.id
string (uuid)Identificador del cliente en Punto.
customer.externalId
string | nullTu identificador del cliente, si lo vinculaste.
customer.name
string
customer.fiscalName
string | null
customer.document
string | null
customer.documentType
string | nullValores: fiscal, personal.
customer.personalDocument
string | nullCédula guardada en la ficha.
customer.fiscalDocument
string | nullRUC guardado en la ficha.
customer.email
string | null
customer.phone
string | nullEn formato internacional, con +.
customer.address
string | null
customer.createdAt
string | nullFecha de alta en Punto.

Respuesta de ejemplo

200 · El cliente actualizado.

{
  "ok": true,
  "data": {
    "customer": {
      "id": "5d0c8a2e-3f41-4b7a-9e62-1c8d7f3a9b10",
      "externalId": "cliente-4812",
      "name": "Ana Gómez",
      "fiscalName": null,
      "document": "4567890",
      "documentType": "personal",
      "email": "[email protected]",
      "phone": "+595981000222",
      "address": "Av. España 1234",
      "createdAt": "2026-10-03 10:14:22"
    }
  }
}

Códigos de respuesta

200El cliente actualizado.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
404No hay nada con ese identificador en este comercio.
409El estado del comercio impide la operación. No reintentes igual: ver reason.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Órdenes

Pedidos que el comercio prepara y entrega, y que se facturan después.

Consultar una orden

GET/v1/integrations/orders

En qué etapa está la orden (status), qué decidió el comercio (acceptance) y, si ya se facturó —desde tu sistema o en el local—, la venta con su estado fiscal en sale.

Parámetros

CampoTipoDescripción
orderIdobligatorio
stringEl identificador del pedido en tu sistema, el mismo que mandaste al registrarlo.

Ejemplo de llamado

Estado de la orden

curl "$PUNTO_API/v1/integrations/orders?orderId=WEB-10246" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
orderId
stringTu identificador del pedido.
id
string (uuid)Identificador de la orden en Punto.
number
integer | nullNúmero de la orden en la sucursal (el que ve el equipo).
status
stringpending: pendiente (el comercio todavía no decidió); sent: en espera (aceptada, nadie la empezó); in_progress: en proceso; ready: lista; out_for_delivery: en camino; delivered: entregada; closed: facturada y entregada; cancelled: cancelada o rechazada. Valores: pending, open, sent, in_progress, ready, out_for_delivery, delivered, closed, cancelled.
fulfillment
stringValores: delivery, takeaway, dine_in.
scheduledFor
string | null
createdAt
string
registerId
string (uuid) | null
customerId
string (uuid) | nullIdentificador del comprador en Punto.
delivery
object | nullLa dirección con la que se registró la orden.
delivery.address
string | null
delivery.reference
string | null
delivery.lat
number | null
delivery.lng
number | null
total
numberTotal de la orden según sus líneas.
acceptance
objectQué decidió el comercio sobre el pedido. status sigue el ciclo completo de la orden; esto contesta si se aceptó.
acceptance.status
stringpending: todavía no decidió; accepted: la aceptó; rejected: la rechazó (la orden queda cancelled). Valores: pending, accepted, rejected.
acceptance.decidedAt
string | nullCuándo la aceptó o rechazó.
acceptance.etaMinutes
integer | nullMinutos que el comercio estimó al aceptar (opcional).
acceptance.estimatedReadyAt
string | nullCuándo estaría lista. Viene en toda orden aceptada; null mientras está pendiente o si se rechazó.
acceptance.reason
string | nullMotivo del rechazo. Valores: out_of_stock, closed, too_busy, out_of_area, other.
acceptance.reasonLabel
string | nullEl motivo en palabras, para mostrarle al comprador.
acceptance.note
string | nullDetalle que escribió el comercio al rechazar.
trackingUrl
string (uri) | nullLink público de seguimiento para tu comprador. Muestra la etapa del pedido, nunca los productos, los montos, la dirección ni el nombre. Deja de responder 12 horas después del último movimiento.
duplicated
booleanSolo en el alta: true si la orden ya estaba registrada.
invoiced
booleanSi ya tiene una venta.
sale
object | nullLa venta que la facturó, con su estado fiscal.
sale.transactionId
string (uuid)
sale.uid
string
sale.document
object | nullEl comprobante de la venta.
sale.document.number
string | nullNúmero de comprobante con su prefijo, listo para mostrar.
sale.document.invoiceNo
integer | nullEl número, sin prefijo.
sale.document.prefix
string | nullPunto de expedición (ver Glosario).
sale.document.serie
string | null
sale.document.timbrado
string | nullTimbrado con el que se numeró.
sale.document.date
string | nullFecha y hora de la venta.
sale.document.total
number | null
sale.einvoice
objectEstado de la factura electrónica. Ver Factura electrónica y KuDE.
sale.einvoice.issued
booleantrue solo si fue aprobada.
sale.einvoice.status
string | nullEstado del envío. null: la caja no emite factura electrónica. Valores: pending, sending, issued, error, cancelled, skipped.
sale.einvoice.sifenVerdict
string | nullResultado de la validación fiscal (en Paraguay, SIFEN). Valores: pending, approved, rejected.
sale.einvoice.sifenReason
string | nullMotivo del rechazo.
sale.einvoice.cdc
string | nullCódigo de control del documento.
sale.einvoice.documentNumber
string | null
sale.einvoice.portalUrl
string (uri) | nullLink donde el comprador descarga su KuDE.
sale.einvoice.qrUrl
string (uri) | nullLink de consulta pública del documento, para imprimir su QR. null mientras el documento no se aprobó.

Respuesta de ejemplo

200 · La orden. En el ejemplo, aceptada y ya facturada.

{
  "ok": true,
  "data": {
    "orderId": "WEB-10246",
    "id": "9b2e4c71-0d6a-4f38-b5e2-3a1c8d7e6f90",
    "number": 87,
    "status": "closed",
    "fulfillment": "delivery",
    "scheduledFor": null,
    "createdAt": "2026-10-03 10:15:02",
    "registerId": "81c541da-640e-4891-a1a0-b32841e64c75",
    "customerId": "5d0c8a2e-3f41-4b7a-9e62-1c8d7f3a9b10",
    "delivery": {
      "address": "Av. España 1234",
      "reference": "Portón negro",
      "lat": -25.2867,
      "lng": -57.647
    },
    "total": 52000,
    "acceptance": {
      "status": "accepted",
      "decidedAt": "2026-10-03 10:16:40",
      "etaMinutes": 25,
      "estimatedReadyAt": "2026-10-03 10:41:40",
      "reason": null,
      "reasonLabel": null,
      "note": null
    },
    "trackingUrl": "https://app.punto.la/pedido/q3Zt8wLx0VbN4kRp2sYc7A",
    "invoiced": true,
    "sale": {
      "transactionId": "c3f1a7d2-8e45-4b90-a6c1-7d2e9f0b4a58",
      "uid": "ecom:WEB-10246",
      "document": {
        "number": "001-003-0000154",
        "invoiceNo": 154,
        "prefix": "001-003",
        "serie": null,
        "timbrado": "16234567",
        "date": "2026-10-03 10:20:05",
        "total": 52000
      },
      "einvoice": {
        "issued": true,
        "status": "issued",
        "sifenVerdict": "approved",
        "sifenReason": null,
        "cdc": "01800123456001003000015412026100312345678901",
        "documentNumber": "001-003-0000154",
        "portalUrl": "https://app.punto.la/factura/eyJ0IjoiYzNmMWE3ZDIifQ.Xb2k",
        "qrUrl": "https://ekuatia.set.gov.py/consultas/qr?nVersion=150&Id=01800123456001003000015412026100312345678901"
      }
    }
  }
}

Códigos de respuesta

200La orden. En el ejemplo, aceptada y ya facturada.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
404No hay nada con ese identificador en este comercio.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Registrar una orden

POST/v1/integrations/orders

Registra un pedido que el comercio prepara y entrega. Entra pendiente (status: pending): el equipo no lo empieza hasta que alguien del comercio lo acepta, y puede rechazarlo con un motivo. Consultá acceptance con GET /v1/integrations/orders. No se factura: se factura después en el local o con POST /v1/integrations/sales y fromOrderId (ver Órdenes: del pedido a la factura).

Motivos de rechazo (error.details.reason, todos 422): register_unavailable, item_unavailable, addons_invalid, price_below_addons, total_mismatch, tax_unavailable, customer_not_found, customer_name_required, customer_invalid, delivery_requires_customer, delivery_address_required, order_invalid, orders_module_disabled (el comercio no tiene activado el módulo de órdenes: la orden no entra hasta que lo active).

Cuerpo de la petición

CampoTipoDescripción
orderIdobligatorio
stringIdentificador del pedido en tu sistema. Ver Idempotencia. Hasta 45 caracteres.
registerIdobligatorio
string (uuid)La caja; la orden queda en su sucursal.
fulfillmentobligatorio
stringCómo llega al comprador: se envía, lo retira o se consume en el local. Valores: delivery, takeaway, dine_in.
itemsobligatorio
array<object>Hasta 200 elementos.
items[].itemId
string (uuid)Identificador del artículo en Punto.
items[].sku
stringCódigo del artículo, si no mandás itemId. Hasta 120 caracteres.
items[].quantityobligatorio
number
items[].unitPriceobligatorio
numberPrecio por unidad que cobró tu sistema, con los agregados incluidos.
items[].note
stringNota de la línea (en una orden, la ve el equipo que la prepara). Hasta 200 caracteres.
items[].addons
array<object>Agregados del artículo (opciones de sus grupos). El recargo lo pone el catálogo. Hasta 50 elementos.
items[].addons[].optionIdobligatorio
string (uuid)La opción, de los addonGroups del artículo en el catálogo.
items[].addons[].quantity
integerPor defecto: 1.
charges
array<object>Hasta 200 elementos.
charges[].descriptionobligatorio
stringHasta 120 caracteres.
charges[].amountobligatorio
number
charges[].taxId
string (uuid)Impuesto del cargo (GET /v1/taxes). Sin él, el predeterminado del comercio.
totalobligatorio
numberLo que el comprador aprobó. Se compara con lo que suman las líneas.
customer
objectObligatorio con fulfillment: delivery.
customer.externalId
stringIdentificador del cliente en tu sistema. Hasta 64 caracteres.
customer.id
string (uuid)Identificador del cliente en Punto.
customer.document
stringDocumento con el que se le factura. Para guardar cédula y RUC a la vez usá personalDocument y fiscalDocument. Hasta 30 caracteres.
customer.documentType
stringfiscal (en Paraguay, RUC) o personal (en Paraguay, cédula). Sin él, Punto lo deduce del formato. Valores: fiscal, personal.
customer.personalDocument
stringCédula (documento personal) de la persona. Va junto al RUC en la misma ficha: mandá los dos cuando los tengas.
customer.fiscalDocument
stringRUC (documento fiscal) con el que se factura, junto a fiscalName. Va junto a la cédula en la misma ficha.
customer.name
stringNombre y apellido. Hace falta (o fiscalName) para crear un cliente nuevo. Hasta 120 caracteres.
customer.fiscalName
stringRazón social para la factura, si es distinta del nombre. Hasta 160 caracteres.
customer.email
string (email)
customer.phone
stringCon código de país, en formato internacional (por ejemplo +595981000111).
delivery
objectObligatorio con fulfillment: delivery.
delivery.addressobligatorio
stringHasta 250 caracteres.
delivery.reference
stringCómo encontrar el lugar. Hasta 250 caracteres.
delivery.lat
numberVan juntas con lng, o ninguna. Entre -90 y 90.
delivery.lng
numberEntre -180 y 180.
delivery.name
stringNombre de la dirección en la ficha del comprador (por ejemplo, Casa). Hasta 80 caracteres.
delivery.city
stringHasta 80 caracteres.
scheduledFor
stringPara cuándo es: fecha (AAAA-MM-DD) o fecha y hora ISO 8601. Sin él, es para ahora.
note
stringHasta 500 caracteres.

Ejemplo de llamado

Orden con envío

curl -X POST "$PUNTO_API/v1/integrations/orders" \
  -H "Authorization: Bearer $PUNTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "WEB-10246",
    "registerId": "81c541da-640e-4891-a1a0-b32841e64c75",
    "fulfillment": "delivery",
    "items": [
      {
        "sku": "HAMB-CLASICA",
        "quantity": 1,
        "unitPrice": 42000,
        "note": "Sin cebolla",
        "addons": [ { "optionId": "c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f604", "quantity": 1 } ]
      }
    ],
    "charges": [ { "description": "Envío a domicilio", "amount": 10000 } ],
    "total": 52000,
    "customer": { "externalId": "cliente-4812" },
    "delivery": {
      "address": "Av. España 1234",
      "reference": "Portón negro",
      "lat": -25.2867,
      "lng": -57.647
    }
  }'

Respuesta (dentro de data)

CampoTipoDescripción
orderId
stringTu identificador del pedido.
id
string (uuid)Identificador de la orden en Punto.
number
integer | nullNúmero de la orden en la sucursal (el que ve el equipo).
status
stringpending: pendiente (el comercio todavía no decidió); sent: en espera (aceptada, nadie la empezó); in_progress: en proceso; ready: lista; out_for_delivery: en camino; delivered: entregada; closed: facturada y entregada; cancelled: cancelada o rechazada. Valores: pending, open, sent, in_progress, ready, out_for_delivery, delivered, closed, cancelled.
fulfillment
stringValores: delivery, takeaway, dine_in.
scheduledFor
string | null
createdAt
string
registerId
string (uuid) | null
customerId
string (uuid) | nullIdentificador del comprador en Punto.
delivery
object | nullLa dirección con la que se registró la orden.
delivery.address
string | null
delivery.reference
string | null
delivery.lat
number | null
delivery.lng
number | null
total
numberTotal de la orden según sus líneas.
acceptance
objectQué decidió el comercio sobre el pedido. status sigue el ciclo completo de la orden; esto contesta si se aceptó.
acceptance.status
stringpending: todavía no decidió; accepted: la aceptó; rejected: la rechazó (la orden queda cancelled). Valores: pending, accepted, rejected.
acceptance.decidedAt
string | nullCuándo la aceptó o rechazó.
acceptance.etaMinutes
integer | nullMinutos que el comercio estimó al aceptar (opcional).
acceptance.estimatedReadyAt
string | nullCuándo estaría lista. Viene en toda orden aceptada; null mientras está pendiente o si se rechazó.
acceptance.reason
string | nullMotivo del rechazo. Valores: out_of_stock, closed, too_busy, out_of_area, other.
acceptance.reasonLabel
string | nullEl motivo en palabras, para mostrarle al comprador.
acceptance.note
string | nullDetalle que escribió el comercio al rechazar.
trackingUrl
string (uri) | nullLink público de seguimiento para tu comprador. Muestra la etapa del pedido, nunca los productos, los montos, la dirección ni el nombre. Deja de responder 12 horas después del último movimiento.
duplicated
booleanSolo en el alta: true si la orden ya estaba registrada.
invoiced
booleanSi ya tiene una venta.
sale
object | nullLa venta que la facturó, con su estado fiscal.
sale.transactionId
string (uuid)
sale.uid
string
sale.document
object | nullEl comprobante de la venta.
sale.document.number
string | nullNúmero de comprobante con su prefijo, listo para mostrar.
sale.document.invoiceNo
integer | nullEl número, sin prefijo.
sale.document.prefix
string | nullPunto de expedición (ver Glosario).
sale.document.serie
string | null
sale.document.timbrado
string | nullTimbrado con el que se numeró.
sale.document.date
string | nullFecha y hora de la venta.
sale.document.total
number | null
sale.einvoice
objectEstado de la factura electrónica. Ver Factura electrónica y KuDE.
sale.einvoice.issued
booleantrue solo si fue aprobada.
sale.einvoice.status
string | nullEstado del envío. null: la caja no emite factura electrónica. Valores: pending, sending, issued, error, cancelled, skipped.
sale.einvoice.sifenVerdict
string | nullResultado de la validación fiscal (en Paraguay, SIFEN). Valores: pending, approved, rejected.
sale.einvoice.sifenReason
string | nullMotivo del rechazo.
sale.einvoice.cdc
string | nullCódigo de control del documento.
sale.einvoice.documentNumber
string | null
sale.einvoice.portalUrl
string (uri) | nullLink donde el comprador descarga su KuDE.
sale.einvoice.qrUrl
string (uri) | nullLink de consulta pública del documento, para imprimir su QR. null mientras el documento no se aprobó.

Respuesta de ejemplo

200 · La orden ya estaba registrada (duplicated: true).

{
  "ok": true,
  "data": {
    "orderId": "WEB-10246",
    "id": "9b2e4c71-0d6a-4f38-b5e2-3a1c8d7e6f90",
    "number": 87,
    "status": "pending",
    "fulfillment": "delivery",
    "scheduledFor": null,
    "createdAt": "2026-10-03 10:15:02",
    "registerId": "81c541da-640e-4891-a1a0-b32841e64c75",
    "customerId": "5d0c8a2e-3f41-4b7a-9e62-1c8d7f3a9b10",
    "delivery": {
      "address": "Av. España 1234",
      "reference": "Portón negro",
      "lat": -25.2867,
      "lng": -57.647
    },
    "total": 52000,
    "acceptance": {
      "status": "pending",
      "decidedAt": null,
      "etaMinutes": null,
      "estimatedReadyAt": null,
      "reason": null,
      "reasonLabel": null,
      "note": null
    },
    "trackingUrl": "https://app.punto.la/pedido/q3Zt8wLx0VbN4kRp2sYc7A",
    "duplicated": true,
    "invoiced": false,
    "sale": null
  }
}

201 · Orden registrada, pendiente de aceptación.

{
  "ok": true,
  "data": {
    "orderId": "WEB-10246",
    "id": "9b2e4c71-0d6a-4f38-b5e2-3a1c8d7e6f90",
    "number": 87,
    "status": "pending",
    "fulfillment": "delivery",
    "scheduledFor": null,
    "createdAt": "2026-10-03 10:15:02",
    "registerId": "81c541da-640e-4891-a1a0-b32841e64c75",
    "customerId": "5d0c8a2e-3f41-4b7a-9e62-1c8d7f3a9b10",
    "delivery": {
      "address": "Av. España 1234",
      "reference": "Portón negro",
      "lat": -25.2867,
      "lng": -57.647
    },
    "total": 52000,
    "acceptance": {
      "status": "pending",
      "decidedAt": null,
      "etaMinutes": null,
      "estimatedReadyAt": null,
      "reason": null,
      "reasonLabel": null,
      "note": null
    },
    "trackingUrl": "https://app.punto.la/pedido/q3Zt8wLx0VbN4kRp2sYc7A",
    "duplicated": false,
    "invoiced": false,
    "sale": null
  }
}

Códigos de respuesta

200La orden ya estaba registrada (duplicated: true).
201Orden registrada, pendiente de aceptación.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Cotizar un envío

GET/v1/integrations/delivery-fee

Cuánto cuesta enviar un pedido desde una sucursal hasta una ubicación, con las mismas tarifas por distancia que usa el comercio en el local.

La distancia es la de manejo por calles; si no se puede calcular, se usa la línea recta con un factor de corrección (method: road o straight). Con status: ok, itemId es el artículo de envío que corresponde y price su precio: mandalo como una línea más del pedido, igual que cualquier artículo. Con no_coverage, la ubicación está fuera del área de entrega del comercio. no_rules: el comercio no configuró tarifas de envío. no_origin: la sucursal no tiene su ubicación cargada. not_sold_here: el artículo de envío que corresponde a esa distancia no está asignado a esa sucursal (o ya no está a la venta), así que no se cotiza.

Parámetros

CampoTipoDescripción
outletIdobligatorio
string (uuid)Sucursal desde la que sale el envío (outletId de GET /v1/integrations/registers).
latobligatorio
numberLatitud del destino.
lngobligatorio
numberLongitud del destino.

Ejemplo de llamado

Cotizar el envío

curl "$PUNTO_API/v1/integrations/delivery-fee?outletId=1a282724-6073-49c3-8bc3-0114a132e349&lat=-25.2867&lng=-57.6470" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
statusobligatorio
stringResultado de la cotización. Valores: ok, no_coverage, no_rules, no_origin, not_sold_here.
method
string | nullCómo se midió la distancia. Valores: road, straight.
distanceMeters
integer | nullDistancia en metros.
maxMeters
integer | nullHasta dónde llega la tarifa que corresponde.
itemId
string (uuid) | nullArtículo de envío a cobrar.
itemName
string | nullNombre del artículo de envío.
price
number | nullPrecio del artículo de envío.
quotedAt
string (date-time) | nullMomento de la cotización.

Respuesta de ejemplo

200 · La cotización.

{
  "ok": true,
  "data": {
    "status": "ok",
    "method": "road",
    "distanceMeters": 3840,
    "maxMeters": 5000,
    "itemId": "4f6a1c2e-9b37-4d80-a5e1-2c7d9f3b8a61",
    "itemName": "Envío hasta 5 km",
    "price": 10000,
    "quotedAt": "2026-10-03T10:12:44-03:00"
  }
}

Códigos de respuesta

200La cotización.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Agenda

Servicios, horarios libres y citas con los profesionales del comercio.

Consultar los servicios

GET/v1/integrations/services

Los servicios que se pueden agendar: los que hace algún profesional de la agenda, en las sucursales a las que la key tiene acceso. Cada uno con su duración, su precio y quién lo hace. Con outletId, solo lo de esa sucursal y el precio de su lista.

El precio es de referencia: la cita se cobra al precio vigente el día que se atiende.

Motivos de rechazo (error.details.reason, 422): calendar_module_disabled (el comercio no tiene activada la agenda), outlet_unavailable.

Parámetros

CampoTipoDescripción
outletId
string (uuid)La sucursal (outletId de GET /v1/integrations/registers).

Ejemplo de llamado

Servicios de una sucursal

curl "$PUNTO_API/v1/integrations/services?outletId=1a282724-6073-49c3-8bc3-0114a132e349" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
services
array<object>
services[].itemId
string (uuid)
services[].name
string
services[].sku
string | null
services[].durationMinutes
integerCuánto dura. Sin duración cargada, 30 minutos.
services[].price
numberPrecio de lista, de referencia. La cita se cobra al precio vigente el día que se atiende.
services[].professionals
array<object>
services[].professionals[].userId
string (uuid)
services[].professionals[].name
string
services[].professionals[].outletIds
array<string (uuid)>Dónde lo hace.

Respuesta de ejemplo

200 · Los servicios.

{
  "ok": true,
  "data": {
    "services": [
      {
        "itemId": "61230b1e-90e8-4018-ac59-865ca957b293",
        "name": "Servicio de ejemplo",
        "sku": "SERV-01",
        "durationMinutes": 45,
        "price": 80000,
        "professionals": [
          {
            "userId": "3e52da17-74a2-49c3-9d07-8d4806671fd5",
            "name": "Ana Gómez",
            "outletIds": [
              "1a282724-6073-49c3-8bc3-0114a132e349"
            ]
          }
        ]
      }
    ]
  }
}

Códigos de respuesta

200Los servicios.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Consultar horarios libres

GET/v1/integrations/availability

Los horarios en que se puede agendar uno o varios servicios un día, en una sucursal. Los servicios van seguidos, en el orden pedido. Tiene en cuenta el horario de la sucursal y de cada profesional, sus bloqueos y citas, las unidades libres de lo que pide cada servicio y la anticipación que configuró el comercio: un día fuera de esa ventana viene sin horarios.

Sin userId vale cualquier profesional, y cada horario dice a quién le toca: el libre con menos citas ese día. Es lo que Punto va a elegir si agendás sin nombrar a nadie, salvo que alguien tome ese lugar antes.

Motivos de rechazo (error.details.reason, todos 422): calendar_module_disabled, outlet_unavailable, appointment_invalid, service_unavailable, service_not_offered (nadie de esa sucursal hace el servicio, o el profesional pedido no lo hace).

Parámetros

CampoTipoDescripción
outletIdobligatorio
string (uuid)La sucursal.
itemIdsobligatorio
stringLos servicios, separados por coma, en el orden en que se hacen (hasta 20).
dateobligatorio
string (date)El día, AAAA-MM-DD.
userId
string (uuid)El profesional, si el cliente eligió uno. Sin él, cualquiera.

Ejemplo de llamado

Horarios de un servicio

curl "$PUNTO_API/v1/integrations/availability?outletId=1a282724-6073-49c3-8bc3-0114a132e349&itemIds=61230b1e-90e8-4018-ac59-865ca957b293&date=2026-11-10" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
outletId
string (uuid)
date
string (date)
slotMinutes
integerCada cuántos minutos arranca un horario en esta sucursal.
slots
array<object>
slots[].startsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
slots[].endsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
slots[].lines
array<object>Cada servicio, con su profesional y su horario.
slots[].lines[].itemId
string (uuid)
slots[].lines[].userId
string (uuid)A quién le toca.
slots[].lines[].userName
string
slots[].lines[].startsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
slots[].lines[].endsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.

Respuesta de ejemplo

200 · Los horarios libres de ese día.

{
  "ok": true,
  "data": {
    "outletId": "1a282724-6073-49c3-8bc3-0114a132e349",
    "date": "2026-11-10",
    "slotMinutes": 15,
    "slots": [
      {
        "startsAt": "2026-11-10 10:30:00",
        "endsAt": "2026-11-10 11:15:00",
        "lines": [
          {
            "itemId": "61230b1e-90e8-4018-ac59-865ca957b293",
            "userId": "3e52da17-74a2-49c3-9d07-8d4806671fd5",
            "userName": "Ana Gómez",
            "startsAt": "2026-11-10 10:30:00",
            "endsAt": "2026-11-10 11:15:00"
          }
        ]
      }
    ]
  }
}

Códigos de respuesta

200Los horarios libres de ese día.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Consultar una cita

GET/v1/integrations/appointments

Una cita por su id o por tu channelRef: en qué estado está, a qué hora, con quién, y si ya se cobró (charged). manageUrl e icsUrl vienen solo si el usuario dueño de la key puede gestionar la agenda, porque con ese link se puede cancelar la cita. Punto no te avisa de los cambios: consultala cuando lo necesites.

Motivos de rechazo (error.details.reason, 422): calendar_module_disabled.

Parámetros

CampoTipoDescripción
id
string (uuid)El identificador de la cita en Punto. Mandá este o channelRef.
channelRef
stringLa referencia que mandaste al agendarla.

Ejemplo de llamado

Por tu referencia

curl "$PUNTO_API/v1/integrations/appointments?channelRef=BOT-7781" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
id
string (uuid)
channelRef
string | nullTu referencia. Las citas que no agendaste por la API no la tienen.
outletId
string (uuid)
status
stringReservada, confirmada, presente, en proceso, finalizada, no vino o cancelada. Valores: booked, confirmed, arrived, in_progress, done, no_show, cancelled.
source
stringPor dónde se agendó.
version
integerSube con cada cambio. Mandala como expectedVersion para no pisar un cambio ajeno.
startsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
endsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
customerId
string (uuid) | null
note
string | null
cancelReason
string | null
charged
booleanSi ya se cobró. Una cita cobrada no se cambia.
manageUrl
string | nullLa página donde el cliente ve su cita, la cambia de horario o la cancela. Mandásela vos. Con ese link se puede cancelar la cita: null si el usuario dueño de la key no puede gestionar la agenda.
icsUrl
string | nullEl archivo para sumarla a su calendario. null en el mismo caso que manageUrl.
duplicated
booleanSolo al agendar: true si esa channelRef ya existía.
lines
array<object>Cada servicio de la cita, con su profesional y su horario.
lines[].itemId
string (uuid) | null
lines[].name
string | null
lines[].userId
string (uuid)
lines[].userName
string
lines[].startsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
lines[].endsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
lines[].resources
array<object>Lo que ocupa: de qué tipo y qué unidad.
lines[].resources[].type
string
lines[].resources[].unit
string

Respuesta de ejemplo

200 · La cita.

{
  "ok": true,
  "data": {
    "id": "5d7c1e42-8a3b-4f6d-9c21-7e4b0a6d2f13",
    "channelRef": "BOT-7781",
    "outletId": "1a282724-6073-49c3-8bc3-0114a132e349",
    "status": "booked",
    "source": "api",
    "version": 1,
    "startsAt": "2026-11-10 10:30:00",
    "endsAt": "2026-11-10 11:15:00",
    "customerId": "9b2e4c71-0d6a-4f38-b5e2-3a1c8d7e6f90",
    "note": null,
    "cancelReason": null,
    "charged": false,
    "manageUrl": "https://app.punto.la/cita/q3Zk8vT1mN0xYpL5sW2aBg",
    "icsUrl": "https://app.punto.la/cita/q3Zk8vT1mN0xYpL5sW2aBg/cita.ics",
    "lines": [
      {
        "itemId": "61230b1e-90e8-4018-ac59-865ca957b293",
        "name": "Servicio de ejemplo",
        "userId": "3e52da17-74a2-49c3-9d07-8d4806671fd5",
        "userName": "Ana Gómez",
        "startsAt": "2026-11-10 10:30:00",
        "endsAt": "2026-11-10 11:15:00",
        "resources": [
          {
            "type": "Sala",
            "unit": "Sala 2"
          }
        ]
      }
    ]
  }
}

Códigos de respuesta

200La cita.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
404No hay nada con ese identificador en este comercio.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Agendar una cita

POST/v1/integrations/appointments

Agenda una cita con las mismas reglas que la caja del comercio, y aparece sola en su agenda. Sin userId le toca al profesional libre con menos citas ese día. El mismo channelRef dos veces devuelve la cita que ya existe (duplicated: true).

Si el horario no entra responde 422 con el motivo y error.details.suggestions, los horarios libres más cercanos, con la misma forma que GET /v1/integrations/availability. Un pedido rechazado no crea al cliente.

Cada cita trae manageUrl, la página donde su cliente la ve, la cambia de horario o la cancela, e icsUrl, el archivo para sumarla a su calendario: mandáselos vos.

Motivos de rechazo (error.details.reason, 422 salvo que se indique): calendar_module_disabled, outlet_unavailable, appointment_invalid, booking_too_soon, booking_too_far (fuera de la anticipación que configuró el comercio), service_unavailable, service_not_offered, staff_not_agendable, outside_staff_hours, outside_outlet_hours, overlap (el profesional ya tiene algo a esa hora, una cita o un bloqueo), no_staff_free (pediste cualquier profesional y ninguno está libre), resource_busy (no hay una unidad libre de lo que pide el servicio), customer_not_found, customer_name_required, customer_invalid, channel_ref_taken (409: esa referencia ya la usó otra cita de una sucursal a la que la key no tiene acceso).

Cuerpo de la petición

CampoTipoDescripción
channelRefobligatorio
stringEl identificador de la cita en tu sistema. Es lo que evita que se agende dos veces. Hasta 45 caracteres.
outletIdobligatorio
string (uuid)La sucursal.
itemIdsobligatorio
array<string (uuid)>Los servicios, en el orden en que se hacen. Van seguidos. Hasta 20 elementos.
userId
string (uuid)El profesional. Sin él, el libre con menos citas ese día.
startsAtobligatorio
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM.
customer
objectEl comprador. Nombralo con externalId o id si ya existe en Punto, o mandá sus datos para que Punto lo busque y, si no lo encuentra, lo cree (ver Clientes). Sin comprador, la venta sale a consumidor final.
customer.externalId
stringIdentificador del cliente en tu sistema. Hasta 64 caracteres.
customer.id
string (uuid)Identificador del cliente en Punto.
customer.document
stringDocumento con el que se le factura. Para guardar cédula y RUC a la vez usá personalDocument y fiscalDocument. Hasta 30 caracteres.
customer.documentType
stringfiscal (en Paraguay, RUC) o personal (en Paraguay, cédula). Sin él, Punto lo deduce del formato. Valores: fiscal, personal.
customer.personalDocument
stringCédula (documento personal) de la persona. Va junto al RUC en la misma ficha: mandá los dos cuando los tengas.
customer.fiscalDocument
stringRUC (documento fiscal) con el que se factura, junto a fiscalName. Va junto a la cédula en la misma ficha.
customer.name
stringNombre y apellido. Hace falta (o fiscalName) para crear un cliente nuevo. Hasta 120 caracteres.
customer.fiscalName
stringRazón social para la factura, si es distinta del nombre. Hasta 160 caracteres.
customer.email
string (email)
customer.phone
stringCon código de país, en formato internacional (por ejemplo +595981000111).
note
stringNota de la cita. Hasta 500 caracteres.

Ejemplo de llamado

Cita con cualquier profesional

curl -X POST "$PUNTO_API/v1/integrations/appointments" \
  -H "Authorization: Bearer $PUNTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channelRef": "BOT-7781",
    "outletId": "1a282724-6073-49c3-8bc3-0114a132e349",
    "itemIds": ["61230b1e-90e8-4018-ac59-865ca957b293"],
    "startsAt": "2026-11-10 10:30",
    "customer": { "phone": "+595981123456", "name": "María Pérez" }
  }'

Respuesta (dentro de data)

CampoTipoDescripción
id
string (uuid)
channelRef
string | nullTu referencia. Las citas que no agendaste por la API no la tienen.
outletId
string (uuid)
status
stringReservada, confirmada, presente, en proceso, finalizada, no vino o cancelada. Valores: booked, confirmed, arrived, in_progress, done, no_show, cancelled.
source
stringPor dónde se agendó.
version
integerSube con cada cambio. Mandala como expectedVersion para no pisar un cambio ajeno.
startsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
endsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
customerId
string (uuid) | null
note
string | null
cancelReason
string | null
charged
booleanSi ya se cobró. Una cita cobrada no se cambia.
manageUrl
string | nullLa página donde el cliente ve su cita, la cambia de horario o la cancela. Mandásela vos. Con ese link se puede cancelar la cita: null si el usuario dueño de la key no puede gestionar la agenda.
icsUrl
string | nullEl archivo para sumarla a su calendario. null en el mismo caso que manageUrl.
duplicated
booleanSolo al agendar: true si esa channelRef ya existía.
lines
array<object>Cada servicio de la cita, con su profesional y su horario.
lines[].itemId
string (uuid) | null
lines[].name
string | null
lines[].userId
string (uuid)
lines[].userName
string
lines[].startsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
lines[].endsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
lines[].resources
array<object>Lo que ocupa: de qué tipo y qué unidad.
lines[].resources[].type
string
lines[].resources[].unit
string

Respuesta de ejemplo

200 · La cita ya estaba registrada (duplicated: true).

{
  "ok": true,
  "data": {
    "id": "5d7c1e42-8a3b-4f6d-9c21-7e4b0a6d2f13",
    "channelRef": "BOT-7781",
    "outletId": "1a282724-6073-49c3-8bc3-0114a132e349",
    "status": "booked",
    "source": "api",
    "version": 1,
    "startsAt": "2026-11-10 10:30:00",
    "endsAt": "2026-11-10 11:15:00",
    "customerId": "9b2e4c71-0d6a-4f38-b5e2-3a1c8d7e6f90",
    "note": null,
    "cancelReason": null,
    "charged": false,
    "manageUrl": "https://app.punto.la/cita/q3Zk8vT1mN0xYpL5sW2aBg",
    "icsUrl": "https://app.punto.la/cita/q3Zk8vT1mN0xYpL5sW2aBg/cita.ics",
    "lines": [
      {
        "itemId": "61230b1e-90e8-4018-ac59-865ca957b293",
        "name": "Servicio de ejemplo",
        "userId": "3e52da17-74a2-49c3-9d07-8d4806671fd5",
        "userName": "Ana Gómez",
        "startsAt": "2026-11-10 10:30:00",
        "endsAt": "2026-11-10 11:15:00",
        "resources": [
          {
            "type": "Sala",
            "unit": "Sala 2"
          }
        ]
      }
    ],
    "duplicated": true
  }
}

201 · La cita se agendó.

{
  "ok": true,
  "data": {
    "id": "5d7c1e42-8a3b-4f6d-9c21-7e4b0a6d2f13",
    "channelRef": "BOT-7781",
    "outletId": "1a282724-6073-49c3-8bc3-0114a132e349",
    "status": "booked",
    "source": "api",
    "version": 1,
    "startsAt": "2026-11-10 10:30:00",
    "endsAt": "2026-11-10 11:15:00",
    "customerId": "9b2e4c71-0d6a-4f38-b5e2-3a1c8d7e6f90",
    "note": null,
    "cancelReason": null,
    "charged": false,
    "manageUrl": "https://app.punto.la/cita/q3Zk8vT1mN0xYpL5sW2aBg",
    "icsUrl": "https://app.punto.la/cita/q3Zk8vT1mN0xYpL5sW2aBg/cita.ics",
    "lines": [
      {
        "itemId": "61230b1e-90e8-4018-ac59-865ca957b293",
        "name": "Servicio de ejemplo",
        "userId": "3e52da17-74a2-49c3-9d07-8d4806671fd5",
        "userName": "Ana Gómez",
        "startsAt": "2026-11-10 10:30:00",
        "endsAt": "2026-11-10 11:15:00",
        "resources": [
          {
            "type": "Sala",
            "unit": "Sala 2"
          }
        ]
      }
    ],
    "duplicated": false
  }
}

Códigos de respuesta

200La cita ya estaba registrada (duplicated: true).
201La cita se agendó.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
409El estado del comercio impide la operación. No reintentes igual: ver reason.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Reprogramar o cancelar una cita

PATCH/v1/integrations/appointments

Con startsAt la cambia de horario: cada servicio conserva su duración y su profesional, salvo que mandes userId. Con status: cancelled la cancela, con cancelReason si querés. Una de las dos.

Si mandás expectedVersion y alguien la cambió desde que la leíste, responde 409 en vez de pisar el cambio. Cancelar dos veces no es un error.

Motivos de rechazo (error.details.reason, 422 salvo que se indique): calendar_module_disabled, appointment_invalid, booking_too_soon, booking_too_far (fuera de la anticipación que configuró el comercio), service_unavailable, service_not_offered, staff_not_agendable, outside_staff_hours, outside_outlet_hours, overlap (el profesional ya tiene algo a esa hora, una cita o un bloqueo), no_staff_free (pediste cualquier profesional y ninguno está libre), resource_busy (no hay una unidad libre de lo que pide el servicio), appointment_closed (409: ya se cobró, se canceló o terminó), appointment_changed (409: cambió desde que la leíste), appointment_not_found (404: se borró mientras tanto).

Parámetros

CampoTipoDescripción
id
string (uuid)El identificador de la cita en Punto. Mandá este o channelRef.
channelRef
stringLa referencia que mandaste al agendarla.

Cuerpo de la petición

CampoTipoDescripción
startsAt
stringEl horario nuevo, hora del comercio, sin zona: AAAA-MM-DD HH:MM.
userId
string (uuid)Para cambiar de profesional al reprogramar. Sin él, cada servicio sigue con el suyo.
status
stringPara cancelarla. Valores: cancelled.
cancelReason
stringHasta 500 caracteres.
expectedVersion
integerLa version que leíste. Si cambió desde entonces, responde 409 en vez de pisar el cambio.

Ejemplo de llamado

Cambiar de horario

curl -X PATCH "$PUNTO_API/v1/integrations/appointments?channelRef=BOT-7781" \
  -H "Authorization: Bearer $PUNTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "startsAt": "2026-11-11 16:00" }'

Respuesta (dentro de data)

CampoTipoDescripción
id
string (uuid)
channelRef
string | nullTu referencia. Las citas que no agendaste por la API no la tienen.
outletId
string (uuid)
status
stringReservada, confirmada, presente, en proceso, finalizada, no vino o cancelada. Valores: booked, confirmed, arrived, in_progress, done, no_show, cancelled.
source
stringPor dónde se agendó.
version
integerSube con cada cambio. Mandala como expectedVersion para no pisar un cambio ajeno.
startsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
endsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
customerId
string (uuid) | null
note
string | null
cancelReason
string | null
charged
booleanSi ya se cobró. Una cita cobrada no se cambia.
manageUrl
string | nullLa página donde el cliente ve su cita, la cambia de horario o la cancela. Mandásela vos. Con ese link se puede cancelar la cita: null si el usuario dueño de la key no puede gestionar la agenda.
icsUrl
string | nullEl archivo para sumarla a su calendario. null en el mismo caso que manageUrl.
duplicated
booleanSolo al agendar: true si esa channelRef ya existía.
lines
array<object>Cada servicio de la cita, con su profesional y su horario.
lines[].itemId
string (uuid) | null
lines[].name
string | null
lines[].userId
string (uuid)
lines[].userName
string
lines[].startsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
lines[].endsAt
stringHora del comercio, sin zona: AAAA-MM-DD HH:MM:SS.
lines[].resources
array<object>Lo que ocupa: de qué tipo y qué unidad.
lines[].resources[].type
string
lines[].resources[].unit
string

Respuesta de ejemplo

200 · La cita, ya guardada.

{
  "ok": true,
  "data": {
    "id": "5d7c1e42-8a3b-4f6d-9c21-7e4b0a6d2f13",
    "channelRef": "BOT-7781",
    "outletId": "1a282724-6073-49c3-8bc3-0114a132e349",
    "status": "booked",
    "source": "api",
    "version": 2,
    "startsAt": "2026-11-11 16:00:00",
    "endsAt": "2026-11-11 16:45:00",
    "customerId": "9b2e4c71-0d6a-4f38-b5e2-3a1c8d7e6f90",
    "note": null,
    "cancelReason": null,
    "charged": false,
    "manageUrl": "https://app.punto.la/cita/q3Zk8vT1mN0xYpL5sW2aBg",
    "icsUrl": "https://app.punto.la/cita/q3Zk8vT1mN0xYpL5sW2aBg/cita.ics",
    "lines": [
      {
        "itemId": "61230b1e-90e8-4018-ac59-865ca957b293",
        "name": "Servicio de ejemplo",
        "userId": "3e52da17-74a2-49c3-9d07-8d4806671fd5",
        "userName": "Ana Gómez",
        "startsAt": "2026-11-10 10:30:00",
        "endsAt": "2026-11-10 11:15:00",
        "resources": [
          {
            "type": "Sala",
            "unit": "Sala 2"
          }
        ]
      }
    ]
  }
}

Códigos de respuesta

200La cita, ya guardada.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
404No hay nada con ese identificador en este comercio.
409El estado del comercio impide la operación. No reintentes igual: ver reason.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Ventas

Pedidos cobrados, que se registran y se facturan en el acto.

Consultar una venta

GET/v1/integrations/sales

Devuelve la venta registrada para un pedido y el estado de su factura electrónica. Consultala hasta que einvoice.sifenVerdict deje de ser pending (ver Factura electrónica y KuDE).

Parámetros

CampoTipoDescripción
orderIdobligatorio
stringEl identificador del pedido en tu sistema, el mismo que mandaste al registrarlo.

Ejemplo de llamado

Estado fiscal

curl "$PUNTO_API/v1/integrations/sales?orderId=WEB-10245" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
orderId
stringTu identificador del pedido.
uid
stringIdentificador de la venta en Punto, derivado de tu orderId.
transactionId
string (uuid)Identificador de la venta en Punto.
duplicated
booleanSolo en el alta: true si el pedido ya estaba registrado.
document
object | nullEl comprobante de la venta.
document.number
string | nullNúmero de comprobante con su prefijo, listo para mostrar.
document.invoiceNo
integer | nullEl número, sin prefijo.
document.prefix
string | nullPunto de expedición (ver Glosario).
document.serie
string | null
document.timbrado
string | nullTimbrado con el que se numeró.
document.date
string | nullFecha y hora de la venta.
document.total
number | null
einvoice
objectEstado de la factura electrónica. Ver Factura electrónica y KuDE.
einvoice.issued
booleantrue solo si fue aprobada.
einvoice.status
string | nullEstado del envío. null: la caja no emite factura electrónica. Valores: pending, sending, issued, error, cancelled, skipped.
einvoice.sifenVerdict
string | nullResultado de la validación fiscal (en Paraguay, SIFEN). Valores: pending, approved, rejected.
einvoice.sifenReason
string | nullMotivo del rechazo.
einvoice.cdc
string | nullCódigo de control del documento.
einvoice.documentNumber
string | null
einvoice.portalUrl
string (uri) | nullLink donde el comprador descarga su KuDE.
einvoice.qrUrl
string (uri) | nullLink de consulta pública del documento, para imprimir su QR. null mientras el documento no se aprobó.
order
objectSolo con fromOrderId: la orden facturada. Queda cerrada si ya estaba entregada; si no, se cierra al entregarse.
order.orderId
stringTu identificador del pedido.
order.id
string (uuid)Identificador de la orden en Punto.
order.number
integer | nullNúmero de la orden en la sucursal (el que ve el equipo).
order.status
stringpending: pendiente (el comercio todavía no decidió); sent: en espera (aceptada, nadie la empezó); in_progress: en proceso; ready: lista; out_for_delivery: en camino; delivered: entregada; closed: facturada y entregada; cancelled: cancelada o rechazada. Valores: pending, open, sent, in_progress, ready, out_for_delivery, delivered, closed, cancelled.
order.fulfillment
stringValores: delivery, takeaway, dine_in.
order.scheduledFor
string | null
order.createdAt
string
order.registerId
string (uuid) | null
order.customerId
string (uuid) | nullIdentificador del comprador en Punto.
order.delivery
object | nullLa dirección con la que se registró la orden.
order.delivery.address
string | null
order.delivery.reference
string | null
order.delivery.lat
number | null
order.delivery.lng
number | null
order.total
numberTotal de la orden según sus líneas.
order.acceptance
objectQué decidió el comercio sobre el pedido. status sigue el ciclo completo de la orden; esto contesta si se aceptó.
order.acceptance.status
stringpending: todavía no decidió; accepted: la aceptó; rejected: la rechazó (la orden queda cancelled). Valores: pending, accepted, rejected.
order.acceptance.decidedAt
string | nullCuándo la aceptó o rechazó.
order.acceptance.etaMinutes
integer | nullMinutos que el comercio estimó al aceptar (opcional).
order.acceptance.estimatedReadyAt
string | nullCuándo estaría lista. Viene en toda orden aceptada; null mientras está pendiente o si se rechazó.
order.acceptance.reason
string | nullMotivo del rechazo. Valores: out_of_stock, closed, too_busy, out_of_area, other.
order.acceptance.reasonLabel
string | nullEl motivo en palabras, para mostrarle al comprador.
order.acceptance.note
string | nullDetalle que escribió el comercio al rechazar.
order.trackingUrl
string (uri) | nullLink público de seguimiento para tu comprador. Muestra la etapa del pedido, nunca los productos, los montos, la dirección ni el nombre. Deja de responder 12 horas después del último movimiento.

Respuesta de ejemplo

200 · La venta y su estado fiscal. En el ejemplo, la factura ya fue aprobada.

{
  "ok": true,
  "data": {
    "orderId": "WEB-10245",
    "uid": "ecom:WEB-10245",
    "transactionId": "c3f1a7d2-8e45-4b90-a6c1-7d2e9f0b4a58",
    "document": {
      "number": "001-003-0000154",
      "invoiceNo": 154,
      "prefix": "001-003",
      "serie": null,
      "timbrado": "16234567",
      "date": "2026-10-03 10:20:05",
      "total": 105000
    },
    "einvoice": {
      "issued": true,
      "status": "issued",
      "sifenVerdict": "approved",
      "sifenReason": null,
      "cdc": "01800123456001003000015412026100312345678901",
      "documentNumber": "001-003-0000154",
      "portalUrl": "https://app.punto.la/factura/eyJ0IjoiYzNmMWE3ZDIifQ.Xb2k",
      "qrUrl": "https://ekuatia.set.gov.py/consultas/qr?nVersion=150&Id=01800123456001003000015412026100312345678901"
    }
  }
}

Códigos de respuesta

200La venta y su estado fiscal. En el ejemplo, la factura ya fue aprobada.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
404No hay nada con ese identificador en este comercio.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Registrar una venta

POST/v1/integrations/sales

Registra un pedido cobrado como venta: descuenta stock, calcula los impuestos, numera el comprobante y, si la caja factura electrónicamente, emite la factura.

Con fromOrderId, factura una orden que entró antes por POST /v1/integrations/orders y la cierra.

Motivos de rechazo (error.details.reason): register_unavailable, item_unavailable, addons_invalid, price_below_addons, total_mismatch, tax_unavailable, payment_method_unknown, payment_method_cash, credit_requires_customer, credit_not_enabled, customer_not_found, customer_name_required, customer_invalid, order_not_found, order_outlet_mismatch, register_in_use_by_device (la caja la usa un dispositivo del comercio: pedí una caja aparte para tu sistema) (422); order_pending_acceptance, order_rejected, order_cancelled, order_already_invoiced, invoice_range_exhausted, invoice_number_taken (409). Un timbrado vencido responde 422 con details.code: invoice_auth_expired: el comercio tiene que cargar uno vigente.

Cuerpo de la petición

CampoTipoDescripción
orderIdobligatorio
stringIdentificador del pedido en tu sistema. Ver Idempotencia. Hasta 45 caracteres.
registerIdobligatorio
string (uuid)La caja contra la que se registra (GET /v1/integrations/registers).
itemsobligatorio
array<object>Hasta 200 elementos.
items[].itemId
string (uuid)Identificador del artículo en Punto.
items[].sku
stringCódigo del artículo, si no mandás itemId. Hasta 120 caracteres.
items[].quantityobligatorio
number
items[].unitPriceobligatorio
numberPrecio por unidad que cobró tu sistema, con los agregados incluidos.
items[].note
stringNota de la línea (en una orden, la ve el equipo que la prepara). Hasta 200 caracteres.
items[].addons
array<object>Agregados del artículo (opciones de sus grupos). El recargo lo pone el catálogo. Hasta 50 elementos.
items[].addons[].optionIdobligatorio
string (uuid)La opción, de los addonGroups del artículo en el catálogo.
items[].addons[].quantity
integerPor defecto: 1.
charges
array<object>Hasta 200 elementos.
charges[].descriptionobligatorio
stringHasta 120 caracteres.
charges[].amountobligatorio
number
charges[].taxId
string (uuid)Impuesto del cargo (GET /v1/taxes). Sin él, el predeterminado del comercio.
totalobligatorio
numberLo que cobraste al comprador. Se compara con lo que suman las líneas.
payment
objectObligatorio al contado; no va en una venta a crédito.
payment.methodobligatorio
stringMedio de pago del comercio: su id de GET /v1/payment-methods (también se acepta su código o su nombre). El efectivo se rechaza: una venta online no se cobra en el cajón del comercio. Hasta 120 caracteres.
payment.reference
stringReferencia del cobro en tu pasarela, para conciliar. Hasta 120 caracteres.
customer
objectEl comprador. Nombralo con externalId o id si ya existe en Punto, o mandá sus datos para que Punto lo busque y, si no lo encuentra, lo cree (ver Clientes). Sin comprador, la venta sale a consumidor final.
customer.externalId
stringIdentificador del cliente en tu sistema. Hasta 64 caracteres.
customer.id
string (uuid)Identificador del cliente en Punto.
customer.document
stringDocumento con el que se le factura. Para guardar cédula y RUC a la vez usá personalDocument y fiscalDocument. Hasta 30 caracteres.
customer.documentType
stringfiscal (en Paraguay, RUC) o personal (en Paraguay, cédula). Sin él, Punto lo deduce del formato. Valores: fiscal, personal.
customer.personalDocument
stringCédula (documento personal) de la persona. Va junto al RUC en la misma ficha: mandá los dos cuando los tengas.
customer.fiscalDocument
stringRUC (documento fiscal) con el que se factura, junto a fiscalName. Va junto a la cédula en la misma ficha.
customer.name
stringNombre y apellido. Hace falta (o fiscalName) para crear un cliente nuevo. Hasta 120 caracteres.
customer.fiscalName
stringRazón social para la factura, si es distinta del nombre. Hasta 160 caracteres.
customer.email
string (email)
customer.phone
stringCon código de país, en formato internacional (por ejemplo +595981000111).
note
stringHasta 500 caracteres.
credit
booleanVenta a crédito: sin payment, con dueDate y un comprador que tenga crédito habilitado en Punto. Por defecto: false.
dueDate
string (date)Vencimiento de una venta a crédito.
issuedAt
string (date-time)Momento de la venta. Sin él, el de la llamada.
fromOrderId
stringorderId de una orden registrada con POST /v1/integrations/orders que esta venta factura. La orden tiene que ser de la misma sucursal que la caja. Hasta 45 caracteres.

Ejemplo de llamado

Venta cobrada con envío

curl -X POST "$PUNTO_API/v1/integrations/sales" \
  -H "Authorization: Bearer $PUNTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "WEB-10245",
    "registerId": "81c541da-640e-4891-a1a0-b32841e64c75",
    "items": [
      { "sku": "CAFE-250", "quantity": 2, "unitPrice": 45000 }
    ],
    "charges": [
      { "description": "Envío a domicilio", "amount": 15000 }
    ],
    "total": 105000,
    "payment": { "method": "e7a1c3d5-2b49-4f60-8d17-5c9e0a3b6f24", "reference": "psp_7f3a91" },
    "customer": { "externalId": "cliente-4812" }
  }'

Facturar una orden

curl -X POST "$PUNTO_API/v1/integrations/sales" \
  -H "Authorization: Bearer $PUNTO_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "orderId": "WEB-10246",
    "fromOrderId": "WEB-10246",
    "registerId": "81c541da-640e-4891-a1a0-b32841e64c75",
    "items": [
      { "sku": "HAMB-CLASICA", "quantity": 1, "unitPrice": 42000,
        "addons": [ { "optionId": "c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f604", "quantity": 1 } ] }
    ],
    "charges": [ { "description": "Envío a domicilio", "amount": 10000 } ],
    "total": 52000,
    "payment": { "method": "e7a1c3d5-2b49-4f60-8d17-5c9e0a3b6f25" }
  }'

Respuesta (dentro de data)

CampoTipoDescripción
orderId
stringTu identificador del pedido.
uid
stringIdentificador de la venta en Punto, derivado de tu orderId.
transactionId
string (uuid)Identificador de la venta en Punto.
duplicated
booleanSolo en el alta: true si el pedido ya estaba registrado.
document
object | nullEl comprobante de la venta.
document.number
string | nullNúmero de comprobante con su prefijo, listo para mostrar.
document.invoiceNo
integer | nullEl número, sin prefijo.
document.prefix
string | nullPunto de expedición (ver Glosario).
document.serie
string | null
document.timbrado
string | nullTimbrado con el que se numeró.
document.date
string | nullFecha y hora de la venta.
document.total
number | null
einvoice
objectEstado de la factura electrónica. Ver Factura electrónica y KuDE.
einvoice.issued
booleantrue solo si fue aprobada.
einvoice.status
string | nullEstado del envío. null: la caja no emite factura electrónica. Valores: pending, sending, issued, error, cancelled, skipped.
einvoice.sifenVerdict
string | nullResultado de la validación fiscal (en Paraguay, SIFEN). Valores: pending, approved, rejected.
einvoice.sifenReason
string | nullMotivo del rechazo.
einvoice.cdc
string | nullCódigo de control del documento.
einvoice.documentNumber
string | null
einvoice.portalUrl
string (uri) | nullLink donde el comprador descarga su KuDE.
einvoice.qrUrl
string (uri) | nullLink de consulta pública del documento, para imprimir su QR. null mientras el documento no se aprobó.
order
objectSolo con fromOrderId: la orden facturada. Queda cerrada si ya estaba entregada; si no, se cierra al entregarse.
order.orderId
stringTu identificador del pedido.
order.id
string (uuid)Identificador de la orden en Punto.
order.number
integer | nullNúmero de la orden en la sucursal (el que ve el equipo).
order.status
stringpending: pendiente (el comercio todavía no decidió); sent: en espera (aceptada, nadie la empezó); in_progress: en proceso; ready: lista; out_for_delivery: en camino; delivered: entregada; closed: facturada y entregada; cancelled: cancelada o rechazada. Valores: pending, open, sent, in_progress, ready, out_for_delivery, delivered, closed, cancelled.
order.fulfillment
stringValores: delivery, takeaway, dine_in.
order.scheduledFor
string | null
order.createdAt
string
order.registerId
string (uuid) | null
order.customerId
string (uuid) | nullIdentificador del comprador en Punto.
order.delivery
object | nullLa dirección con la que se registró la orden.
order.delivery.address
string | null
order.delivery.reference
string | null
order.delivery.lat
number | null
order.delivery.lng
number | null
order.total
numberTotal de la orden según sus líneas.
order.acceptance
objectQué decidió el comercio sobre el pedido. status sigue el ciclo completo de la orden; esto contesta si se aceptó.
order.acceptance.status
stringpending: todavía no decidió; accepted: la aceptó; rejected: la rechazó (la orden queda cancelled). Valores: pending, accepted, rejected.
order.acceptance.decidedAt
string | nullCuándo la aceptó o rechazó.
order.acceptance.etaMinutes
integer | nullMinutos que el comercio estimó al aceptar (opcional).
order.acceptance.estimatedReadyAt
string | nullCuándo estaría lista. Viene en toda orden aceptada; null mientras está pendiente o si se rechazó.
order.acceptance.reason
string | nullMotivo del rechazo. Valores: out_of_stock, closed, too_busy, out_of_area, other.
order.acceptance.reasonLabel
string | nullEl motivo en palabras, para mostrarle al comprador.
order.acceptance.note
string | nullDetalle que escribió el comercio al rechazar.
order.trackingUrl
string (uri) | nullLink público de seguimiento para tu comprador. Muestra la etapa del pedido, nunca los productos, los montos, la dirección ni el nombre. Deja de responder 12 horas después del último movimiento.

Respuesta de ejemplo

200 · El pedido ya estaba registrado (duplicated: true): es la venta original.

{
  "ok": true,
  "data": {
    "orderId": "WEB-10245",
    "uid": "ecom:WEB-10245",
    "transactionId": "c3f1a7d2-8e45-4b90-a6c1-7d2e9f0b4a58",
    "duplicated": true,
    "document": {
      "number": "001-003-0000154",
      "invoiceNo": 154,
      "prefix": "001-003",
      "serie": null,
      "timbrado": "16234567",
      "date": "2026-10-03 10:20:05",
      "total": 105000
    },
    "einvoice": {
      "issued": false,
      "status": "sending",
      "sifenVerdict": "pending",
      "sifenReason": null,
      "cdc": null,
      "documentNumber": "001-003-0000154",
      "portalUrl": "https://app.punto.la/factura/eyJ0IjoiYzNmMWE3ZDIifQ.Xb2k",
      "qrUrl": null
    }
  }
}

201 · Venta registrada. La factura electrónica todavía se está validando.

{
  "ok": true,
  "data": {
    "orderId": "WEB-10245",
    "uid": "ecom:WEB-10245",
    "transactionId": "c3f1a7d2-8e45-4b90-a6c1-7d2e9f0b4a58",
    "duplicated": false,
    "document": {
      "number": "001-003-0000154",
      "invoiceNo": 154,
      "prefix": "001-003",
      "serie": null,
      "timbrado": "16234567",
      "date": "2026-10-03 10:20:05",
      "total": 105000
    },
    "einvoice": {
      "issued": false,
      "status": "sending",
      "sifenVerdict": "pending",
      "sifenReason": null,
      "cdc": null,
      "documentNumber": "001-003-0000154",
      "portalUrl": "https://app.punto.la/factura/eyJ0IjoiYzNmMWE3ZDIifQ.Xb2k",
      "qrUrl": null
    }
  }
}

Códigos de respuesta

200El pedido ya estaba registrado (duplicated: true): es la venta original.
201Venta registrada. La factura electrónica todavía se está validando.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
409El estado del comercio impide la operación. No reintentes igual: ver reason.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Datos del comercio

Cajas, catálogo, stock, medios de pago e impuestos, para armar pedidos válidos.

Listar las cajas

GET/v1/integrations/registers

Las cajas que tu key puede usar en un pedido: activas, de sucursales activas y de las sucursales a las que tiene acceso el usuario que emitió la key. Es el primer llamado para probar una key nueva.

Ejemplo de llamado

Cajas disponibles

curl "$PUNTO_API/v1/integrations/registers" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
registers
array<object>
registers[].registerId
string (uuid)
registers[].name
string
registers[].outletId
string (uuid)La sucursal de la caja.
registers[].outletName
string
registers[].electronicInvoice
booleanSi una venta registrada en esta caja sale con factura electrónica.
registers[].available
booleanSi tu sistema puede usar esta caja. false: la usa un dispositivo del comercio y la venta responde 422 con register_in_use_by_device.

Respuesta de ejemplo

200 · Las cajas.

{
  "ok": true,
  "data": {
    "registers": [
      {
        "registerId": "81c541da-640e-4891-a1a0-b32841e64c75",
        "name": "Tienda online",
        "outletId": "1a282724-6073-49c3-8bc3-0114a132e349",
        "outletName": "Casa central",
        "electronicInvoice": true,
        "available": true
      },
      {
        "registerId": "81c541da-640e-4891-a1a0-b32841e64c76",
        "name": "Mostrador",
        "outletId": "1a282724-6073-49c3-8bc3-0114a132e349",
        "outletName": "Casa central",
        "electronicInvoice": true,
        "available": false
      }
    ]
  }
}

Códigos de respuesta

200Las cajas.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Consultar stock

GET/v1/integrations/stock

Cuántas unidades hay de cada artículo en una sucursal, hasta 200 artículos por consulta. Es el mismo saldo que ve el comercio en el local. Responde 403 si la key no tiene acceso a esa sucursal.

Parámetros

CampoTipoDescripción
outletIdobligatorio
string (uuid)La sucursal (outletId de GET /v1/integrations/registers).
itemIdsobligatorio
stringLos artículos, separados por coma (hasta 200).

Ejemplo de llamado

Stock de dos artículos

curl "$PUNTO_API/v1/integrations/stock?outletId=1a282724-6073-49c3-8bc3-0114a132e349&itemIds=10223f3b-2e3d-4339-8496-9f288d8be65b,61230b1e-90e8-4018-ac59-865ca957b293" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
outletId
string (uuid)
items
array<object>Un artículo que no existe en el comercio no viene.
items[].itemId
string (uuid)
items[].tracksStock
booleanSi el comercio controla el stock de este artículo. Si no, se vende sin límite.
items[].quantity
number | nullUnidades en la sucursal. Puede ser negativa (se vendió más de lo cargado). null si el artículo no controla stock.

Respuesta de ejemplo

200 · El saldo de cada artículo.

{
  "ok": true,
  "data": {
    "outletId": "1a282724-6073-49c3-8bc3-0114a132e349",
    "items": [
      {
        "itemId": "10223f3b-2e3d-4339-8496-9f288d8be65b",
        "tracksStock": true,
        "quantity": 34
      },
      {
        "itemId": "61230b1e-90e8-4018-ac59-865ca957b293",
        "tracksStock": false,
        "quantity": null
      }
    ]
  }
}

Códigos de respuesta

200El saldo de cada artículo.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
422Hay un dato que corregir. Ver reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Listar el catálogo

GET/v1/items

El catálogo del comercio, por páginas. Para recorrerlo entero, pedí la primera página con after vacío y seguí con el valor de next hasta que venga null.

Cada artículo trae más campos que los documentados; usá solo los documentados, los demás pueden cambiar sin aviso.

Parámetros

CampoTipoDescripción
after
stringRecorrido por páginas: vacío para la primera, después el next de la respuesta anterior.
limit
integerArtículos por página (máximo 200).
q
stringBúsqueda por nombre o SKU.

Ejemplo de llamado

Primera página

curl "$PUNTO_API/v1/items?after=&limit=200" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
items
array<object>
items[].itemId
string (uuid)
items[].itemName
string
items[].itemSKU
string | null
items[].itemPrice
numberPrecio de lista del catálogo.
items[].itemStatus
integer1 activo; otro valor, archivado.
items[].itemCanSale
booleanSi se puede vender.
items[].itemTrackInventory
booleanSi el comercio controla su stock.
items[].taxId
string (uuid) | null
items[].addonGroups
array<any> | nullGrupos de agregados con sus opciones (optionId y recargo).
limit
integer
next
string | nullDesde dónde pedir la próxima página; null si fue la última.
total
integer | nullCantidad total de artículos, solo en la primera página.

Respuesta de ejemplo

200 · Una página del catálogo.

{
  "ok": true,
  "data": {
    "items": [
      {
        "itemId": "10223f3b-2e3d-4339-8496-9f288d8be65b",
        "itemName": "Café en grano 250 g",
        "itemSKU": "CAFE-250",
        "itemPrice": 45000,
        "itemStatus": 1,
        "itemCanSale": true,
        "itemTrackInventory": true,
        "taxId": "3cf780bb-51d6-4b41-b52d-1e77bfb60969",
        "addonGroups": null
      },
      {
        "itemId": "61230b1e-90e8-4018-ac59-865ca957b293",
        "itemName": "Hamburguesa clásica",
        "itemSKU": "HAMB-CLASICA",
        "itemPrice": 38000,
        "itemStatus": 1,
        "itemCanSale": true,
        "itemTrackInventory": false,
        "taxId": "3cf780bb-51d6-4b41-b52d-1e77bfb60969",
        "addonGroups": [
          {
            "groupId": "c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f600",
            "name": "Extras",
            "options": [
              {
                "optionId": "c1a2b3c4-d5e6-4f70-8a91-b2c3d4e5f604",
                "name": "Queso cheddar",
                "priceDelta": 4000
              }
            ]
          }
        ]
      }
    ],
    "limit": 200,
    "next": null,
    "total": 2
  }
}

Códigos de respuesta

200Una página del catálogo.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Listar los medios de pago

GET/v1/payment-methods

Los medios de pago del comercio. En una venta, nombrá el medio con el que cobró tu sistema por su id. El efectivo (cash: true) no se acepta en ventas de esta API.

Ejemplo de llamado

Medios de pago

curl "$PUNTO_API/v1/payment-methods" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
paymentMethods
array<object>
paymentMethods[].id
string (uuid)Lo que mandás en payment.method.
paymentMethods[].name
string
paymentMethods[].code
stringCódigo corto del medio, si el comercio le asignó uno.
paymentMethods[].cash
booleanSi es efectivo. No se acepta en ventas de esta API.
paymentMethods[].requiresIdentifier
booleanSi el comercio pide un número de comprobante del cobro (mandalo en payment.reference).
paymentMethods[].identifierLabel
stringCómo se llama ese número para el comercio.

Respuesta de ejemplo

200 · Los medios de pago.

{
  "ok": true,
  "data": {
    "paymentMethods": [
      {
        "id": "e7a1c3d5-2b49-4f60-8d17-5c9e0a3b6f26",
        "name": "Efectivo",
        "code": "A",
        "cash": true,
        "requiresIdentifier": false,
        "identifierLabel": ""
      },
      {
        "id": "e7a1c3d5-2b49-4f60-8d17-5c9e0a3b6f24",
        "name": "Tarjeta de crédito",
        "code": "S",
        "cash": false,
        "requiresIdentifier": true,
        "identifierLabel": "N.º de autorización"
      },
      {
        "id": "e7a1c3d5-2b49-4f60-8d17-5c9e0a3b6f25",
        "name": "Transferencia",
        "code": "T",
        "cash": false,
        "requiresIdentifier": false,
        "identifierLabel": ""
      }
    ]
  }
}

Códigos de respuesta

200Los medios de pago.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.

Listar los impuestos

GET/v1/taxes

Los impuestos del comercio, para elegir el de un envío o recargo (charges[].taxId). Los impuestos de los artículos los aplica Punto solo.

Ejemplo de llamado

Impuestos

curl "$PUNTO_API/v1/taxes" \
  -H "Authorization: Bearer $PUNTO_KEY"

Respuesta (dentro de data)

CampoTipoDescripción
taxes
array<object>
taxes[].id
string (uuid)Lo que mandás en charges[].taxId.
taxes[].name
string
taxes[].rate
number | nullTasa en porcentaje. 0 en un exento.
taxes[].kind
string | nullrate: con tasa; exempt: exento. Valores: rate, exempt.
taxes[].isDefault
booleanEl que se aplica a un cargo sin taxId.

Respuesta de ejemplo

200 · Los impuestos, en el orden del comercio.

{
  "ok": true,
  "data": {
    "taxes": [
      {
        "id": "3cf780bb-51d6-4b41-b52d-1e77bfb60969",
        "name": "IVA 10%",
        "rate": 10,
        "kind": "rate",
        "isDefault": true
      },
      {
        "id": "8a4d2c6e-1f37-4b85-9c20-6e3a7d1b5f42",
        "name": "IVA 5%",
        "rate": 5,
        "kind": "rate",
        "isDefault": false
      },
      {
        "id": "8a4d2c6e-1f37-4b85-9c20-6e3a7d1b5f43",
        "name": "Exento",
        "rate": 0,
        "kind": "exempt",
        "isDefault": false
      }
    ]
  }
}

Códigos de respuesta

200Los impuestos, en el orden del comercio.
401Falta la key o no es válida.
403La key no puede hacer esto. reason: addon_api_required (el comercio no tiene activa la API de integraciones), api_key_scope_missing (la key no es de integración), account_blocked, account_suspended o account_inactive (la cuenta del comercio no está operativa). Si el usuario que emitió la key no tiene permiso para vender, o si pedís una sucursal (outletId) a la que la key no tiene acceso, responde 403 sin reason.
429Pasaste el límite de uso de la key (60 por minuto, 5.000 por día). Esperá un minuto.