Mesalto · API pública (v1)

Download OpenAPI specification:

Lectura de ventas, cuadraturas y movimientos de caja, documentos tributarios, carta y stock de tus sucursales.

La API pública de Mesalto deja que otro sistema (tu contabilidad, tu planilla de control, un tablero de BI o el ERP de tu grupo) lea los datos de tus sucursales sin pasar por la administración. Es de solo lectura: ninguna llave puede crear, cambiar ni borrar nada.

Todas las rutas cuelgan de https://app.mesalto.com/api/v1 y responden JSON en UTF-8.

Para consultar y administrar los locales desde un asistente (Claude, ChatGPT u otro compatible con MCP) no hace falta una llave: Mesalto tiene un servidor MCP en https://app.mesalto.com/mcp, al que cada persona entra con su cuenta del panel. Se conecta en la sección Asistentes del panel.

Crear una llave

  1. Entra a la administración con un usuario Dueño que tenga a cargo todas las sucursales.
  2. Abre Integraciones y completa «Crear una llave»: un nombre que diga qué sistema la usa, qué puede leer, a qué sucursales llega y, si quieres, desde qué día deja de funcionar.
  3. Copia la llave apenas aparezca. Mesalto la muestra una sola vez y guarda solo una huella; si la pierdes, revócala y crea otra.

Crea una llave por cada sistema que conectes. Así puedes revocar una sin cortar las demás, y cada una deja su último uso a la vista en Integraciones.

Autenticación

Cada solicitud lleva la llave completa en el header Authorization:

GET /api/v1/locations HTTP/1.1
Host: app.mesalto.com
Authorization: Bearer msk_0192f5a8-6c1e-7b3a-9d42-1f8e3c5a7b90.Zp4dQ9xW2mTn8vKc5LrB1yHs7eJf3uAg6oNi0pVb2Qw

Una llave tiene la forma msk_<id>.<secreto>. Trátala como una contraseña: no la pongas en una página web, en una app que instala otra persona ni en un repositorio. Una llave revocada o que pasó su fecha responde 401.

Permisos

Cada llave lleva uno o más permisos (scopes). Un endpoint que pide un permiso que la llave no tiene responde 403 con code = forbidden_scope.

Permiso En la administración Endpoints
sales:read Ventas /sales/summary, /orders, /orders/{id}
cash:read Cuadraturas de caja y movimientos /shifts, /movements
documents:read Documentos tributarios /fiscal-documents
catalog:read Carta y stock /products, /stock

/locations responde con cualquier permiso.

Alcance por sucursal

Una llave llega a todas las sucursales de la organización, también a las que se abran después, o solo a las que se marcaron al crearla. GET /locations lista las que alcanza. En los endpoints con location_id, omitirlo equivale a todas las que alcanza la llave; una sucursal fuera de su alcance, o que no existe, responde 404.

Límites

Cada llave puede hacer 60 solicitudes por minuto (ventana fija que parte en el segundo 0 de cada minuto). Todas las respuestas autenticadas traen:

Header Qué dice
RateLimit-Limit Solicitudes por minuto de la llave (60).
RateLimit-Remaining Cuántas le quedan en el minuto en curso.
RateLimit-Reset Segundos que faltan para que el contador vuelva a cero.

Pasado el límite, la respuesta es 429 con Retry-After en segundos. Espera ese tiempo antes de reintentar.

Errores

Los errores siguen el formato Problem Details (RFC 9457) con Content-Type: application/problem+json. title y detail están en español y pueden cambiar de redacción; code es estable y es el que conviene usar en tu código.

{
  "type": "https://app.mesalto.com/docs/api#section/Errores",
  "title": "La llave no tiene el permiso necesario",
  "status": 403,
  "detail": "Esta llave no tiene el permiso cash:read.",
  "code": "forbidden_scope"
}
HTTP code Cuándo
400 invalid_request Un parámetro falta o no es válido, o el rango de fechas pasa de 31 días.
401 unauthorized Falta la llave, está mal escrita, fue revocada o ya dejó de funcionar.
403 forbidden_scope La llave no tiene el permiso del endpoint.
403 organization_suspended La cuenta de Mesalto de la organización está suspendida.
404 not_found La sucursal o la orden no existe o la llave no llega a ella.
429 rate_limited La llave pasó las 60 solicitudes del minuto.

Paginación

Los listados se recorren con cursor. Pide limit filas (1 a 200, 50 por defecto) y, si la respuesta trae next_cursor, pásalo como cursor en la siguiente solicitud con los mismos filtros. Cuando next_cursor es null no quedan más filas. Las filas vienen ordenadas por id, que crece con el tiempo de creación, así que el recorrido no repite ni salta filas aunque entren datos nuevos mientras lo haces.

{ "data": [ … ], "next_cursor": "0192f5b1-2d44-7c10-8a9e-5b3f6d2e8c41" }

Fechas y montos

  • Fecha de negocio (business_date, from, to): AAAA-MM-DD, el día del turno en la hora de la sucursal. Una venta de las 00:30 de un turno que abrió la noche anterior cuenta para el día anterior. from y to son inclusivos y el rango admite hasta 31 días.
  • Instantes (opened_at, closed_at, issued_at…): RFC 3339 en UTC, por ejemplo 2026-09-26T16:05:12.000Z.
  • Montos: pesos chilenos (CLP) enteros, sin decimales y con IVA incluido, salvo net (neto afecto), vat (IVA) y exempt (exento), que desglosan el total: net + vat + exempt = total.
  • La propina no es venta: va aparte en tip y tip_total, y nunca entra en total, gross_total ni en la base del IVA.
  • Cantidades: qty_milli en milésimas de unidad (1500 es una unidad y media).

Versionado

La versión va en la ruta (/api/v1). Dentro de v1 solo se agregan campos, parámetros opcionales y endpoints: tu integración debe ignorar los campos que no conoce. Un cambio que rompa compatibilidad sale como v2, con aviso previo y un tiempo de convivencia.

Sucursales

Las sucursales que alcanza la llave.

Listar sucursales

Las sucursales que alcanza la llave, activas o no, ordenadas por nombre. Responde con cualquier permiso.

Authorizations:
apiKey

Responses

Response samples

Content type
application/json
{
  • "data": [
    • {
      },
    • {
      }
    ]
}

Ventas

Resumen de ventas y órdenes con sus líneas, descuentos y pagos. Permiso sales:read.

Resumen de ventas

Ventas por sucursal y por fecha de negocio, con el total de cada sucursal y el consolidado de todas. Usa los mismos criterios que el reporte del día de la administración:

  • Cuenta las órdenes cerradas de los turnos del rango. Las abiertas y las anuladas no son venta, y una mesa juntada con otra se cuenta una sola vez, en la orden que quedó.
  • gross_total es la suma de los totales de esas órdenes (con IVA, sin propina); net, vat y exempt lo desglosan.
  • tip_total y by_payment_method suman los pagos cobrados de los turnos del rango.
  • Un día sin turno no aparece en days.
Authorizations:
apiKey
query Parameters
from
required
string <date>
Example: from=2026-09-01

Primer día de negocio del rango, inclusivo.

to
required
string <date>
Example: to=2026-09-30

Último día de negocio del rango, inclusivo. Hasta 31 días contando from y to.

location_id
string <uuid>

Solo esta sucursal. Sin él, todas las que alcanza la llave.

Responses

Response samples

Content type
application/json
{
  • "from": "2026-09-26",
  • "to": "2026-09-26",
  • "totals": {
    • "orders_closed": 42,
    • "gross_total": 387600,
    • "net": 325714,
    • "vat": 61886,
    • "exempt": 0,
    • "discount_total": 12400,
    • "tip_total": 31200,
    • "average_ticket": 9229,
    • "guests": 71,
    • "by_payment_method": [
      ]
    },
  • "locations": [
    • {
      }
    ]
}

Listar órdenes

Órdenes cuya fecha de negocio está en el rango, con todo su detalle. Trae también las abiertas y las anuladas: filtra con status=closed para quedarte solo con las ventas. Una orden anulada con merged_into_id es una mesa que se juntó con otra, no una anulación.

Authorizations:
apiKey
query Parameters
from
required
string <date>
Example: from=2026-09-01

Primer día de negocio del rango, inclusivo.

to
required
string <date>
Example: to=2026-09-30

Último día de negocio del rango, inclusivo. Hasta 31 días contando from y to.

location_id
string <uuid>

Solo esta sucursal. Sin él, todas las que alcanza la llave.

status
string (OrderStatus)
Enum: "open" "closed" "voided"

Solo las órdenes en este estado.

limit
integer [ 1 .. 200 ]
Default: 50

Filas por página.

cursor
string <uuid>

El next_cursor de la página anterior.

Responses

Response samples

Content type
application/json
{
  • "data": [
    • {
      }
    ],
  • "next_cursor": null
}

Obtener una orden

Authorizations:
apiKey
path Parameters
id
required
string <uuid>

Id de la orden.

Responses

Response samples

Content type
application/json
{
  • "id": "0192f5b0-1a2b-7c3d-8e4f-5a6b7c8d9e01",
  • "location_id": "0192f5a8-6c1e-7b3a-9d42-1f8e3c5a7b01",
  • "shift_id": "0192f5a9-0a00-7d11-9c20-3e4f5a6b7c01",
  • "number": 57,
  • "label": null,
  • "type": "dine_in",
  • "channel": "waiter",
  • "status": "closed",
  • "business_date": "2026-09-26",
  • "opened_at": "2026-09-26T16:05:12.000Z",
  • "closed_at": "2026-09-26T16:48:27.000Z",
  • "voided": false,
  • "void_reason": null,
  • "merged_into_id": null,
  • "guests": 2,
  • "waiter": {
    • "id": "0192f5a8-9c00-7b11-8d20-1c2d3e4f5a03",
    • "name": "Garzón Demo"
    },
  • "tables": [
    • {
      }
    ],
  • "delivery": null,
  • "agreement": {
    • "agreement_id": "0192f5a8-6f00-7c11-8d20-4e5f6a7b8c01",
    • "name": "Constructora Andes",
    • "member_short_name": "María P.",
    • "credit": false
    },
  • "totals": {
    • "subtotal": 13700,
    • "discount_total": 0,
    • "surcharge_total": 0,
    • "total": 13700,
    • "paid_total": 13700,
    • "tip_total": 1370,
    • "net": 11513,
    • "vat": 2187,
    • "exempt": 0
    },
  • "lines": [
    • {
      },
    • {
      }
    ],
  • "discounts": [ ],
  • "payments": [
    • {
      }
    ],
  • "refunds": [
    • {
      }
    ]
}

Caja

Turnos, cuadraturas de caja y movimientos (cobros, anulaciones, devoluciones y movimientos de caja). Permiso cash:read.

Listar turnos y cuadraturas

Turnos cuya fecha de negocio está en el rango, cada uno con sus sesiones de caja: fondo inicial, lo que debería haber (expected), lo que se contó al cerrar (counted), la diferencia en efectivo y los ingresos, egresos y retiros. En una caja abierta, expected es lo que debería haber en ese momento y counted es null.

Authorizations:
apiKey
query Parameters
from
required
string <date>
Example: from=2026-09-01

Primer día de negocio del rango, inclusivo.

to
required
string <date>
Example: to=2026-09-30

Último día de negocio del rango, inclusivo. Hasta 31 días contando from y to.

location_id
string <uuid>

Solo esta sucursal. Sin él, todas las que alcanza la llave.

limit
integer [ 1 .. 200 ]
Default: 50

Filas por página.

cursor
string <uuid>

El next_cursor de la página anterior.

Responses

Response samples

Content type
application/json
{
  • "data": [
    • {
      }
    ],
  • "next_cursor": null
}

Listar movimientos

Lo mismo que Movimientos en la administración, de una sucursal y de los turnos cuya fecha de negocio está en el rango: un movimiento por fila, con su documento y su caja. Sirve para cuadrar las ventas con la contabilidad y los depósitos de cada forma de pago.

  • Venta (sale): cada pago cobrado, también el que después se anuló, con su boleta, factura o guía.
  • Anulación (void): el pago anulado, en negativo y a la hora de la anulación, con su nota de crédito. Su id es el del pago seguido de -anulacion, así que va justo después de su venta.
  • Devolución (refund): en negativo, con su nota de crédito.
  • Ingreso (pay_in), retiro (drop) y egreso (payout) de caja, en efectivo, con el motivo y la nota en comment.

No entran los pagos que no se alcanzaron a cobrar ni las aperturas y cierres de caja (están en /shifts).

Como la anulación lleva el id de su pago, una que se haga mientras recorres un turno abierto puede caer en una página que ya pasaste. Para cuadrar, pide los días ya cerrados.

Authorizations:
apiKey
query Parameters
location_id
required
string <uuid>

La sucursal, una de las que alcanza la llave.

from
required
string <date>
Example: from=2026-09-01

Primer día de negocio del rango, inclusivo.

to
required
string <date>
Example: to=2026-09-30

Último día de negocio del rango, inclusivo. Hasta 31 días contando from y to.

limit
integer [ 1 .. 200 ]
Default: 50

Filas por página.

cursor
string
Example: cursor=0192f5b0-1c00-7c3d-8e4f-5a6b7c8d9e21-anulacion

El next_cursor de la página anterior.

Responses

Response samples

Content type
application/json
{
  • "data": [
    • {
      },
    • {
      },
    • {
      }
    ],
  • "next_cursor": "0192f5b3-1c00-7e11-8f20-4a5b6c7d8e01"
}

Documentos tributarios

Boletas (39), facturas (33), guías de despacho (52) y notas de crédito (61). Permiso documents:read.

Listar documentos tributarios

Boletas (39), facturas (33), guías de despacho (52) y notas de crédito (61) electrónicas cuya fecha de negocio está en el rango. Una nota de crédito apunta al documento que anula con reference_id y, si es de una devolución, a esa devolución con refund_id. Solo cuentan ante el SII los documentos con emission_status = issued; los demás todavía se están emitiendo o no se emitieron. Si la emisión falló o el SII rechazó el documento, error resume por qué.

Authorizations:
apiKey
query Parameters
from
required
string <date>
Example: from=2026-09-01

Primer día de negocio del rango, inclusivo.

to
required
string <date>
Example: to=2026-09-30

Último día de negocio del rango, inclusivo. Hasta 31 días contando from y to.

location_id
string <uuid>

Solo esta sucursal. Sin él, todas las que alcanza la llave.

doc_type
integer
Enum: 39 33 52 61

Solo boletas (39), solo facturas (33), solo guías de despacho (52) o solo notas de crédito (61).

emission_status
string (EmissionStatus)
Enum: "pending_send" "sending" "issued" "failed_retryable" "failed_permanent" "cancelled"

Solo los documentos en este estado de emisión; por ejemplo, failed_permanent para los que esperan revisión.

limit
integer [ 1 .. 200 ]
Default: 50

Filas por página.

cursor
string <uuid>

El next_cursor de la página anterior.

Responses

Response samples

Content type
application/json
{
  • "data": [
    • {
      },
    • {
      }
    ],
  • "next_cursor": "0192f5b0-5100-7f11-9a20-5b6c7d8e9f03"
}

Carta

Productos con su precio y disponibilidad por sucursal, y el stock de cada sucursal. Permiso catalog:read.

Listar productos

Toda la carta de la organización, activa o no. Cada producto trae su precio base y, por cada sucursal que alcanza la llave (o solo la de location_id), si se vende ahí, a qué precio y si está disponible o agotado. Con q o category_id trae solo los productos que coinciden.

Authorizations:
apiKey
query Parameters
location_id
string <uuid>

Solo esta sucursal. Sin él, todas las que alcanza la llave.

q
string <= 100 characters
Example: q=cortado

Busca en el nombre, el código y el código fiscal, sin distinguir mayúsculas. Hasta 100 caracteres.

category_id
string <uuid>

Solo los productos de esta categoría (no los de sus subcategorías).

limit
integer [ 1 .. 200 ]
Default: 50

Filas por página.

cursor
string <uuid>

El next_cursor de la página anterior.

Responses

Response samples

Content type
application/json
{
  • "data": [
    • {
      },
    • {
      }
    ],
  • "next_cursor": null
}

Stock de una sucursal

Lo mismo que Stock en la administración: cuántas unidades quedan de cada producto en la bodega de Defontana de la sucursal, según la última lectura (read_at), y el mínimo que se le puso.

  • Trae los productos activos que la sucursal vende y que siguen stock: los que tienen código fiscal y un saldo leído en la bodega, aunque sea 0. Un pack cuenta los que alcanzan a armarse con sus componentes.
  • untracked cuenta los que no siguen stock: sin código fiscal, sin saldo leído o combos (su stock es el de lo que se elige en cada paso).
  • low_stock marca lo que está por pedir: tiene mínimo y le quedan esas unidades o menos.
  • Si la sucursal todavía no tiene una lectura, read_at es null y products viene vacío.
Authorizations:
apiKey
query Parameters
location_id
required
string <uuid>

La sucursal, una de las que alcanza la llave.

Responses

Response samples

Content type
application/json
{
  • "location_id": "0192f5a8-6c1e-7b3a-9d42-1f8e3c5a7b01",
  • "read_at": "2026-09-26T14:14:00.000Z",
  • "untracked": 18,
  • "products": [
    • {
      },
    • {
      }
    ]
}