API REST de Mindy POS
Integra tu negocio con sistemas externos. Consulta y gestiona productos, stock, ventas, egresos, reportes, CRM y más — de forma programática con autenticación por API Key. Soporta GET, POST, PUT y DELETE.
URL base
https://app.mindy.gt/api/v1/Métodos
GET POST PUT DELETE
Formato
JSON · UTF-8
Auth
Bearer token (API Key)
Cómo comenzar
Genera tu API Key
Ve a Ajustes › API en el panel de Mindy y crea una nueva key. Se muestra una sola vez — guárdala. Formato:
mk_live_xxxxxxxx.Configura los permisos
Al crear la key elige los scopes necesarios y el límite de requests por minuto (30 – 300).
Haz tu primera petición
curl -X GET "https://app.mindy.gt/api/v1/products?per_page=1" \ -H "Authorization: Bearer mk_live_tu_api_key"Verifica la respuesta
{ "success": true, "data": [ ... ], "meta": { "page": 1, "per_page": 1, "total": 150, "total_pages": 150 } }
Inicio rápido
Reemplaza mk_live_tu_api_key con tu key real.
curl -X GET "https://app.mindy.gt/api/v1/products?per_page=5" \
-H "Authorization: Bearer mk_live_tu_api_key"const res = await fetch(
"https://app.mindy.gt/api/v1/products?per_page=5",
{ headers: { Authorization: "Bearer mk_live_tu_api_key" } }
);
const data = await res.json();import requests
r = requests.get(
"https://app.mindy.gt/api/v1/products",
params={"per_page": 5},
headers={"Authorization": "Bearer mk_live_tu_api_key"}
)
print(r.json())$ch = curl_init("https://app.mindy.gt/api/v1/products?per_page=5");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer mk_live_tu_api_key"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = json_decode(curl_exec($ch), true);Respuesta · 200 OK
{
"success": true,
"data": [
{ "id": 42, "name": "Laptop HP 15", "sale_price": 4500.00,
"category_id": 5, "is_active": true, "has_variants": false }
],
"meta": { "page": 1, "per_page": 5, "total": 150, "total_pages": 30 }
}Autenticación
Incluye tu API Key en el header Authorization de cada petición:
Authorization: Bearer mk_live_tu_api_key Scopes disponibles
| Scope | Descripción |
|---|---|
catalog:read | Siempre incluido. Productos, categorías, paquetes, sucursales, proveedores, marcas, unidades, clientes, stock y recordatorios |
sales:read | Leer historial de ventas y detalles |
sales:write | Crear ventas y anularlas |
products:write | Crear, editar y eliminar productos |
clients:write | Crear y editar clientes |
expenses:write | Crear egresos |
reminders:write | Crear, editar y eliminar recordatorios |
stock:write | Ajustar stock de productos |
reports:read | Egresos, resumen del período y cierres de caja |
employees:read | Leer lista de empleados |
subscriptions:read | Leer suscripciones, cargos y planes |
webhooks:manage | Crear, editar, eliminar y listar webhooks |
crm:read | Leer leads, mensajes, notas, bitácora, embudos y tags |
crm:write | Crear leads, actualizarlos y agregar notas |
crm:messages:send | Enviar mensajes a leads por cualquier canal |
Errores
Todas las respuestas de error tienen esta estructura:
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Producto no encontrado",
"status": 404
}
} | HTTP | Código | Descripción |
|---|---|---|
| 400 | VALIDATION_ERROR | Campos requeridos faltantes o formato inválido |
| 400 | INSUFFICIENT_STOCK | Stock insuficiente para la operación |
| 401 | UNAUTHORIZED | API Key inválida, revocada o expirada |
| 403 | FORBIDDEN | La key no tiene el scope requerido |
| 404 | NOT_FOUND | Recurso no encontrado |
| 409 | CONFLICT | Duplicado (barcode, SKU, teléfono) o estado ya aplicado |
| 422 | UNSUPPORTED_MEDIA_TYPE_FOR_CHANNEL | El canal no soporta ese tipo de media |
| 429 | RATE_LIMIT_EXCEEDED | Demasiadas peticiones |
| 502 | CHANNEL_API_ERROR | Canal externo rechazó el envío |
Rate Limiting
Límite configurable por API Key: 30, 60, 120 o 300 req/min. Cada respuesta incluye:
| Header | Descripción |
|---|---|
X-RateLimit-Limit | Límite total por minuto |
X-RateLimit-Remaining | Requests restantes en la ventana |
X-RateLimit-Reset | Timestamp Unix de reinicio |
Al exceder el límite recibirás un 429. Espera al reinicio antes de reintentar.
Paginación
| Parámetro | Default | Descripción |
|---|---|---|
page | 1 | Número de página |
per_page | 20 | Resultados por página (máx 100) |
{
"success": true,
"data": [...],
"meta": { "page": 1, "per_page": 20, "total": 150, "total_pages": 8 }
} Mi cuenta
/v1/meInfo de la API key actual — cualquier key válidaÚtil para introspección y debugging: muestra los scopes, el límite y el comercio asociado a la key.
{
"success": true,
"data": {
"key_prefix": "mk_live_a1b2",
"name": "Tienda online",
"commerce_id": 42,
"commerce_name": "Mi Negocio GT",
"scopes": ["catalog:read", "sales:read", "products:write"],
"rate_limit": 60,
"is_active": true,
"last_used_at": "2026-06-03 14:22:11",
"expires_at": null,
"created_at": "2026-05-10 09:00:00"
}
} Productos
/v1/productsLista con filtros y paginación| Parámetro | Tipo | Descripción |
|---|---|---|
search | string | Buscar por nombre o descripción |
barcode / sku | string | Coincidencia exacta |
category_id | int | Filtrar por categoría |
active_only | 0/1 | Solo activos (default 1) |
include | string | variants, stock (separados por coma) |
curl "https://app.mindy.gt/api/v1/products?per_page=5&include=stock" \
-H "Authorization: Bearer mk_live_tu_api_key"const res = await fetch(
"https://app.mindy.gt/api/v1/products?per_page=5&include=stock",
{ headers: { Authorization: "Bearer mk_live_tu_api_key" } }
);
const { data, meta } = await res.json();import requests
r = requests.get(
"https://app.mindy.gt/api/v1/products",
params={"per_page": 5, "include": "stock"},
headers={"Authorization": "Bearer mk_live_tu_api_key"}
)
print(r.json())$ch = curl_init(
"https://app.mindy.gt/api/v1/products?per_page=5&include=stock"
);
curl_setopt($ch, CURLOPT_HTTPHEADER,
["Authorization: Bearer mk_live_tu_api_key"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = json_decode(curl_exec($ch), true);/v1/products/{id}Producto individual con variantes y stock/v1/products/{id}/variantsVariantes con stock por sucursal/v1/products/{id}/stockStock por sucursal/v1/products/{id}/movementsHistorial de movimientos de stock — paginado| Parámetro | Descripción |
|---|---|
per_page | Máx 200 (default 50) |
branch_id | Filtrar por sucursal |
date_from / date_to | Rango de fechas (YYYY-MM-DD) |
{
"id": 5821,
"type": "-",
"quantity": 3.0,
"description": "Por venta API. R#000142",
"date": "2026-06-01 10:35:00",
"branch_id": 1,
"branch_name": "Sucursal Central"
} type es "+" (entrada) o "-" (salida)./v1/productsCrear — scope: products:write · 201curl -X POST "https://app.mindy.gt/api/v1/products" \
-H "Authorization: Bearer mk_live_tu_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Camiseta Polo",
"sale_price": 150.00,
"barcode": "7501234567890",
"category_id": 3
}'const res = await fetch(
"https://app.mindy.gt/api/v1/products",
{
method: "POST",
headers: {
Authorization: "Bearer mk_live_tu_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Camiseta Polo",
sale_price: 150.00,
barcode: "7501234567890",
category_id: 3,
}),
}
);
const product = await res.json();import requests
r = requests.post(
"https://app.mindy.gt/api/v1/products",
headers={"Authorization": "Bearer mk_live_tu_api_key"},
json={
"name": "Camiseta Polo",
"sale_price": 150.00,
"barcode": "7501234567890",
"category_id": 3,
}
)
print(r.json()) # 201 Created$ch = curl_init("https://app.mindy.gt/api/v1/products");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer mk_live_tu_api_key",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"name" => "Camiseta Polo",
"sale_price" => 150.00,
"barcode" => "7501234567890",
"category_id" => 3,
]),
CURLOPT_RETURNTRANSFER => true,
]);
$res = json_decode(curl_exec($ch), true);201 Created
// 201 Created
{
"success": true,
"data": {
"id": 55, "name": "Camiseta Polo", "sale_price": 150.00,
"barcode": "7501234567890", "category_id": 3,
"is_active": true, "has_variants": false,
"stock": [{ "branch_id": 1, "branch_name": "Central", "quantity": 0 }]
}
}/v1/products/batchCrear/actualizar hasta 50 — scope: products:writeCada item se procesa de forma independiente; un error en uno no cancela los demás. Modos: create (falla por item si el barcode existe) o upsert (actualiza si existe, crea si no).
{
"mode": "upsert",
"products": [
{
"name": "Agua 500ml",
"sale_price": 3.50,
"purchase_price": 1.80,
"barcode": "7501234567890",
"sku": "AGU-500",
"category_id": 5,
"min_stock": 10
}
]
} Respuesta
{
"success": true,
"data": {
"mode": "upsert",
"processed": 3, "succeeded": 3, "failed": 0,
"results": [
{ "index": 0, "success": true, "id": 201, "name": "Agua 500ml", "action": "created" },
{ "index": 1, "success": true, "id": 88, "name": "Coca-Cola 1L", "action": "updated" },
{ "index": 2, "success": false, "error": "sale_price es requerido y debe ser > 0" }
]
}
}/v1/products/{id}Editar — scope: products:write · Partial update/v1/products/{id}Eliminar (soft delete) — dispara product.deleted{ "success": true, "data": { "id": 88, "deleted": true } } Stock
/v1/stockConsulta masiva — hasta 100 productos| Parámetro | Descripción |
|---|---|
product_ids * | IDs separados por coma (máx 100) |
branch_id | Filtrar por sucursal |
{
"success": true,
"data": [{
"product_id": 1,
"stock": [{ "branch_id": 1, "branch_name": "Central", "quantity": 25 }],
"total_stock": 25, "low_stock": false, "minimum_stock": 5
}]
} /v1/stock/adjustAjustar stock — scope: stock:writeCantidad positiva = entrada, negativa = salida. Valida que no quede stock negativo. Dispara stock.adjusted.
{
"product_id": 88,
"branch_id": 1,
"quantity": -5,
"reason": "Merma por daño en almacén"
} Respuesta
{
"success": true,
"data": {
"product_id": 88, "product_name": "Coca-Cola 1L",
"branch_id": 1, "adjustment": -5,
"new_stock": 45.0, "adjusted_at": "2026-06-03 11:00:00"
}
}Categorías
/v1/categoriesLista de categorías/v1/categories/{id}Categoría individual/v1/categories/{id}/productsProductos de una categoríaPaquetes
/v1/packagesLista de paquetes/combos/v1/packages/{id}Paquete con componentes{
"id": 10, "name": "Combo Almuerzo", "sale_price": 45.00,
"components": [
{ "product_id": 20, "product_name": "Hamburguesa", "quantity": 1 },
{ "product_id": 31, "product_name": "Papas fritas", "quantity": 1 }
]
} Sucursales
/v1/branchesLista de sucursales{
"id": 1, "name": "Sucursal Central",
"address": "6ta Avenida 10-20, Zona 1",
"phone": "22334455", "email": "central@negocio.gt", "is_default": true
} Proveedores
/v1/providersLista — filtros: search, active_only/v1/providers/{id}Proveedor individual{
"id": 7, "name": "Distribuidora XYZ",
"address": "7a Av. 15-30, Guatemala",
"email": "ventas@xyz.gt", "phone": "24441234",
"phone_2": null, "nit": "12345678", "is_active": true
} Marcas y Unidades
/v1/brands · /v1/brands/{id}Marcas de productos{ "id": 3, "name": "Coca-Cola", "is_active": true } /v1/units · /v1/units/{id}Unidades de medida{ "id": 2, "name": "Litro", "is_active": true } /v1/expense-categoriesCategorías de egresos[
{ "id": 12, "name": "Servicios básicos", "is_active": true },
{ "id": 13, "name": "Transporte", "is_active": true }
] Clientes
/v1/clientsLista — filtros: search, phone/v1/clients/{id}Cliente individual/v1/clients/{id}/creditSaldo de crédito y desglose{
"success": true,
"data": {
"client_id": 55,
"total_credit": 850.00, "total_paid": 500.00, "total_balance": 350.00,
"credits": [
{ "id": 14, "sale_id": 3021, "amount": 500.00, "paid": 500.00,
"balance": 0.00, "expires_at": "2026-07-01", "is_paid": true },
{ "id": 18, "sale_id": 3105, "amount": 350.00, "paid": 0.00,
"balance": 350.00, "expires_at": "2026-08-15", "is_paid": false }
]
}
} /v1/clientsCrear — scope: clients:write · 201| Campo | Req. | Descripción |
|---|---|---|
name | * | Nombre (máx 250) |
phone | * | Teléfono (8 dígitos exactos) |
nit | NIT fiscal | |
email / address / dpi | Opcionales |
{ "name": "María López", "phone": "55001234", "nit": "123456-7" } 201 Created
// 201 Created
{
"success": true,
"data": {
"id": 100, "name": "María López", "nit": "123456-7",
"phone": "55001234", "is_active": true,
"has_credit": false, "credit_limit": 0
}
}/v1/clients/{id}Editar — scope: clients:write · Partial updateRecordatorios
/v1/remindersLista — filtros: fecha, sucursal, cliente, estado/v1/reminders/{id}Recordatorio individual/v1/remindersCrear — scope: reminders:write · 201{
"start_date": "2026-06-10 10:00:00",
"end_date": "2026-06-10 11:00:00",
"description": "Cita de seguimiento",
"branch_id": 1,
"client_id": 55,
"category_id": 3
} /v1/reminders/{id}Editar — campos: start_date, end_date, description, category_id, client_id, is_cancelled/v1/reminders/{id}Eliminar (soft delete)Ventas
/v1/salesHistorial — scope: sales:read| Parámetro | Descripción |
|---|---|
date_from / date_to | Rango de fechas (YYYY-MM-DD) |
branch_id / client_id | Filtrar por sucursal o cliente |
payment_method | Búsqueda parcial en método de pago |
status | 1 = activa, 0 = anulada |
{
"id": 3105, "sale_number": "3105",
"date": "2026-06-03 10:12:00",
"payment_method": "Efectivo", "document_type": "VEN",
"invoice_number": null,
"total": 125.50, "total_discount": 0.00,
"total_profit": 45.20, "total_cost": 80.30,
"status": 1,
"client_id": 55, "client_name": "Juan Pérez",
"branch_id": 1, "branch_name": "Sucursal Central",
"credit_amount": 0.00, "paid_amount": 0.00
} /v1/sales/{id}Detalle con artículos{
"...campos de la venta...",
"items": [
{
"id": 9021, "product_id": 88, "product_name": "Coca-Cola 1L",
"quantity": 2.0, "unit_price": 12.00, "cost_price": 7.50,
"discount": 0.00, "line_total": 24.00
}
]
} /v1/salesCrear — scope: sales:write · 201Descuenta stock automáticamente. payment_method: efectivo, tarjeta o transferencia.
curl -X POST "https://app.mindy.gt/api/v1/sales" \
-H "Authorization: Bearer mk_live_tu_api_key" \
-H "Content-Type: application/json" \
-d '{
"branch_id": 1,
"payment_method": "efectivo",
"client_id": 55,
"items": [
{ "product_id": 88, "quantity": 2, "unit_price": 12.00 }
]
}'const res = await fetch(
"https://app.mindy.gt/api/v1/sales",
{
method: "POST",
headers: {
Authorization: "Bearer mk_live_tu_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
branch_id: 1,
payment_method: "efectivo",
client_id: 55,
items: [{ product_id: 88, quantity: 2, unit_price: 12.00 }],
}),
}
);
const sale = await res.json(); // 201 Createdimport requests
r = requests.post(
"https://app.mindy.gt/api/v1/sales",
headers={"Authorization": "Bearer mk_live_tu_api_key"},
json={
"branch_id": 1,
"payment_method": "efectivo",
"client_id": 55,
"items": [{"product_id": 88, "quantity": 2, "unit_price": 12.00}],
}
)
print(r.status_code, r.json())$ch = curl_init("https://app.mindy.gt/api/v1/sales");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer mk_live_tu_api_key",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"branch_id" => 1,
"payment_method" => "efectivo",
"items" => [
["product_id" => 88, "quantity" => 2, "unit_price" => 12.00],
],
]),
CURLOPT_RETURNTRANSFER => true,
]);
$res = json_decode(curl_exec($ch), true);/v1/sales/{id}/cancelAnular y restaurar stock — scope: sales:writeSin body. Dispara sale.canceled. Devuelve 409 si ya está anulada.
{
"success": true,
"data": {
"id": 3105, "sale_number": "3105",
"status": "canceled", "canceled_at": "2026-06-03 15:00:00"
}
} Egresos
/v1/expenses · /v1/expenses/{id}scope: reports:readFiltros: date_from, date_to, branch_id, type.
{
"id": 210, "description": "Pago de energía eléctrica",
"date": "2026-06-01 09:00:00", "type": "Gasto",
"amount": 350.00, "reference": "Factura #4521", "notes": "",
"category_id": 12, "branch_id": 1, "branch_name": "Sucursal Central"
} /v1/expensesCrear — scope: expenses:write · 201date, type, reference, notes y category_id son opcionales.
{
"description": "Pago de energía eléctrica",
"amount": 350.00,
"branch_id": 1,
"type": "Gasto",
"reference": "Factura #4521",
"category_id": 12
} Reportes
/v1/reports/summaryResumen financiero — scope: reports:readVentas, costos, utilidad, egresos, resultado neto y desglose por día y método de pago. Filtros: date_from, date_to, branch_id (default: hoy).
{
"success": true,
"data": {
"period": { "from": "2026-06-01", "to": "2026-06-03" },
"sales": {
"count": 87, "revenue": 12450.00, "cost": 7200.00,
"profit": 5250.00, "discounts": 125.00
},
"expenses": { "count": 5, "total": 1800.00 },
"net_result": 10650.00,
"by_payment_method": [
{ "payment_method": "Efectivo", "count": 60, "total": 8200.00 },
{ "payment_method": "Tarjeta de crédito / débito", "count": 27, "total": 4250.00 }
],
"by_day": [
{ "date": "2026-06-01", "count": 30, "total": 4100.00 },
{ "date": "2026-06-02", "count": 28, "total": 3900.00 }
]
}
} Cierres de Caja
/v1/cash-closingsHistorial — scope: reports:readFiltros: branch_id, date_from, date_to.
{
"id": 88,
"opened_at": "2026-06-03 08:00:00",
"closed_at": "2026-06-03 20:00:00",
"opening_amount": 500.00, "cash_withdrawal": 200.00,
"cash_counted": 4350.00, "cash_calculated": 4350.00,
"cash_difference": 0.00, "is_closed": true,
"notes": "Cierre sin novedad",
"opened_by_id": 4, "opened_by_name": "María García",
"closed_by_id": 4, "closed_by_name": "María García",
"branch_id": 1, "branch_name": "Sucursal Central",
"cash_register_id": 2
} /v1/cash-closings/{id}Detalle con sales_summary del período{
"...campos del cierre...",
"sales_summary": { "count": 47, "total": 5800.00 }
} Webhooks
Recibe eventos en tiempo real en tu propia URL. Requiere scope webhooks:manage. Máximo 10 webhooks por comercio.
/v1/webhooks · /v1/webhooks/{id}Lista y detalle/v1/webhooks/eventsEvent types disponibles{
"id": 3,
"url": "https://mi-sistema.com/webhook",
"events": ["sale.created", "client.created"],
"description": "Sync con ERP",
"is_active": true, "fail_streak": 0, "disabled_at": null,
"created_at": "2026-05-01 09:00:00", "updated_at": null
} secret no se retorna en GET — solo se muestra una vez al crear el webhook (POST)./v1/webhooksCrear · 201 — el secret se muestra una sola vez{
"url": "https://mi-sistema.com/webhook",
"events": ["sale.created", "product.updated"],
"description": "Sync con ERP",
"is_active": true
} 201 Created
// 201 Created — el secret se muestra UNA sola vez
{
"success": true,
"data": {
"id": 3,
"url": "https://mi-sistema.com/webhook",
"events": ["sale.created", "product.updated"],
"secret": "a1b2c3d4e5f6...",
"is_active": true
}
}/v1/webhooks/{id}Editar url, events, description o is_active/v1/webhooks/{id}Eliminar webhook e historialEventos disponibles
| Evento | Descripción |
|---|---|
sale.created | Se creó una venta (POS o API) |
sale.refunded | Se procesó una devolución |
sale.canceled | Se anuló una venta |
product.created | Se creó un producto |
product.updated | Se actualizó un producto |
product.deleted | Se eliminó un producto |
client.created | Se creó un cliente |
client.updated | Se actualizó un cliente |
stock.low | Stock bajo detectado (cron diario) |
stock.adjusted | Ajuste manual de stock |
crm.lead.created | Lead nuevo en el CRM |
crm.lead.stage_changed | Cambio de etapa del pipeline |
crm.lead.assigned | Cambio de colaborador asignado |
crm.lead.archived | Lead archivado/ganado/perdido |
crm.message.received | Mensaje entrante recibido |
crm.note.added | Nota interna agregada |
Verificación de firma
Cada POST a tu URL incluye este header. Calcula HMAC-SHA256(secret, body_raw) y compara con tiempo constante para evitar timing attacks.
X-Mindy-Signature: sha256=<hmac-sha256(secret, body)> Empleados
/v1/employees · /v1/employees/{id}scope: employees:readLista de empleados con su rol. No incluye contraseñas ni datos biométricos. Filtros: search, active_only.
{
"id": 4, "name": "María García",
"email": "maria@minegocio.gt", "phone": "50001234",
"photo_url": "https://cdn.mindy.gt/...",
"role_id": 2, "role_name": "Cajero",
"is_active": true,
"created_at": "2025-01-10 08:00:00",
"last_login_at": "2026-06-03 07:45:00"
} Suscripciones
/v1/subscriptions · /v1/subscriptions/{id}scope: subscriptions:read/v1/plansPlanes de suscripción configuradosConsulta las suscripciones recurrentes de tus clientes, sus cargos generados y los planes disponibles. Solo lectura.
CRM — Leads
Cubre WhatsApp, Messenger, Instagram, Telegram, Email y Mindy Chat. Lectura con crm:read, escritura con crm:write.
/v1/crm/leadsLista con filtros| Parámetro | Descripción |
|---|---|
funnel_id | Filtrar por embudo |
pipeline_state | Match exacto contra state_key |
assigned_user_id | 0 = sin asignar |
archived_status | none | archived | won | lost |
tag_id | Filtrar por tag |
search / phone | LIKE en nombre y teléfono |
updated_since | updated_at >= ? |
/v1/crm/leads/{id}Lead con tags, key_dates y notas/v1/crm/leads/{id}/messagesMensajes (incluye multimedia)/v1/crm/leads/{id}/notesNotas internas del equipo/v1/crm/leads/{id}/historyBitácora de cambios{
"id": 123, "phone": "50212345678", "contact_name": "Juan",
"channel_label": "WhatsApp Ventas",
"funnel_id": 1, "pipeline_state": "qualified",
"archived_status": "none", "assigned_user_id": 45,
"last_message_at": "2026-05-19 14:32:00",
"tags": [{ "id": 2, "label": "VIP", "color": "#7c3aed" }],
"created_at": "2026-05-01 09:00:00"
} /v1/crm/leadsCrear — scope: crm:write · 201Solo phone es requerido. Devuelve 409 si ya existe un lead con ese teléfono.
{
"phone": "50212345678",
"contact_name": "Carlos Mendoza",
"funnel_id": 1,
"pipeline_state": "new",
"notes": "Interesado en plan pro"
} /v1/crm/leads/{id}Actualizar — scope: crm:writeDispara webhooks automáticamente: cambiar pipeline_state → crm.lead.stage_changed, archived_status → crm.lead.archived, assigned_user_id → crm.lead.assigned. Usa assigned_user_id: null para desasignar.
{
"contact_name": "Carlos Mendoza Ruiz",
"pipeline_state": "qualified",
"archived_status": "won",
"assigned_user_id": 4,
"notes": "Cerrado exitosamente"
} CRM — Enviar mensaje
/v1/crm/leads/{id}/messagesscope: crm:messages:sendEnruta automáticamente por el canal del lead. type: text, image, video, audio o document.
curl -X POST "https://app.mindy.gt/api/v1/crm/leads/123/messages" \
-H "Authorization: Bearer mk_live_tu_api_key" \
-H "Content-Type: application/json" \
-d '{ "type": "text", "content": "Hola, te confirmo tu pedido." }'const res = await fetch(
"https://app.mindy.gt/api/v1/crm/leads/123/messages",
{
method: "POST",
headers: {
Authorization: "Bearer mk_live_tu_api_key",
"Content-Type": "application/json",
},
body: JSON.stringify({ type: "text", content: "Hola, te confirmo tu pedido." }),
}
);
const msg = await res.json();import requests
r = requests.post(
"https://app.mindy.gt/api/v1/crm/leads/123/messages",
headers={"Authorization": "Bearer mk_live_tu_api_key"},
json={"type": "text", "content": "Hola, te confirmo tu pedido."}
)
print(r.json())$ch = curl_init(
"https://app.mindy.gt/api/v1/crm/leads/123/messages"
);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer mk_live_tu_api_key",
"Content-Type: application/json",
],
CURLOPT_POSTFIELDS => json_encode([
"type" => "text",
"content" => "Hola, te confirmo tu pedido.",
]),
CURLOPT_RETURNTRANSFER => true,
]);
$res = json_decode(curl_exec($ch), true);200 OK
// 200 OK
{
"success": true,
"data": {
"message_id": 9999, "channel": "whatsapp",
"external_id": "EVO_abc123", "sent_at": "2026-05-19 14:32:00"
}
}Soporte por canal
| Canal | text | image | video | audio | document |
|---|---|---|---|---|---|
| ✅ | ✅ | ✅ | ✅ | ✅ | |
| Mindy Chat | ✅ | ✅ | ✅ | ✅ | ✅ |
| ✅ | ✅ | — | — | — | |
| Messenger | ✅ | — | — | — | — |
| Telegram | ✅ | — | — | — | — |
| ✅ | — | — | — | — |
CRM — Notas
/v1/crm/leads/{id}/notesAgregar nota — scope: crm:write · 201Dispara crm.note.added.
{ "content": "Llamó para confirmar reunión del martes." } 201 Created
// 201 Created
{
"id": 88, "id_lead": 14,
"content": "Llamó para confirmar reunión del martes.",
"user_name": null, "created_at": "2026-06-03 11:30:00"
}CRM — Embudos, Etapas y Tags
/v1/crm/funnelsEmbudos con etapas anidadas/v1/crm/pipelinesLista plana de todas las etapas/v1/crm/tagsTags del CRM (id, label, color)Changelog
- Nuevo
GET /v1/me, movimientos de stock, batch de productos, DELETE de productos - Nuevo Ventas:
GET /v1/sales,POST /v1/sales/{id}/cancel - Nuevo Egresos, reportes de período, cierres de caja, ajuste de stock
- Nuevo Webhooks gestionables vía API + 16 eventos · Empleados · Proveedores · Marcas · Unidades
- Nuevo CRM: crear/editar leads, agregar notas
- Nuevo Endpoints CRM: leads, mensajes, notas, historial, embudos, tags
- Nuevo Enviar mensajes multicanal · Scopes
crm:readycrm:messages:send
- Nuevo Crear/editar productos, consulta masiva de stock, crear ventas
- Nuevo Lanzamiento: catálogo, clientes, recordatorios, autenticación y paginación