API de Qubo
v1Crea y descarga códigos QR desde tu propio sistema: tu tienda, tu POS o tu CRM. Con el mismo diseño del Studio, en SVG para imprenta o en PNG, estáticos o dinámicos para cambiar su destino sin reimprimirlos.
Introducción
La API usa JSON y una sola URL base: https://qubo.click/api/v1. Está incluida en los planes con API (hoy Pro y Lifetime) y cada QR que creas cuenta dentro del cupo de tu plan, igual que en el panel.
Con ella creas códigos QR estáticos o dinámicos de cualquier tipo del Studio, con tu diseño; descargas su imagen en SVG o PNG; cambias el destino de un dinámico, lo pausas o lo borras, y consultas sus escaneos. Todo lo que haces por API aparece también en tu panel, y al revés.
Autenticación
Envía tu clave en el encabezado X-API-KEY en cada petición (también sirve Authorization: Bearer). La encuentras en tu panel, en la tarjeta API Token, junto al botón para regenerarla.
X-API-KEY: tu_clave_de_apiTu clave da acceso a tu cuenta: úsala solo desde tu servidor, nunca en código que corra en el navegador o en una app. Qubo la guarda cifrada y te la muestra una sola vez, al crearla o regenerarla; después solo ves su comienzo. Si la pierdes o se filtra, regénérala: la anterior deja de funcionar al instante. La clave no se acepta en la URL (?api_token=), porque quedaría escrita en los registros.
Crear un QR
/api/v1/qr| Campo | Tipo | Descripción |
|---|---|---|
| title | texto, obligatorio | Nombre del QR. Lo ves en tu panel. |
| type | static (por defecto) o dynamic | Un dinámico apunta a un enlace corto y puedes cambiar su destino sin reimprimirlo. Solo para URL. |
| content_type | url (por defecto), wifi, vcard, whatsapp, sms, email, text, crypto | Qué guarda el QR. |
| payload | objeto | Los datos de ese tipo (tabla siguiente). |
| design | objeto, opcional | Colores, formas y logo (más abajo). |
Tipos de contenido
| content_type | payload | Para qué |
|---|---|---|
| url | payload.url | Una página web. Atajo: envía solo "url" sin payload. |
| wifi | payload.ssid, password, encryption (WPA, WEP o nopass) | Conecta al Wi-Fi sin escribir la clave. |
| vcard | payload.vcardName, vcardPhone, vcardEmail, vcardOrg | Guarda un contacto en el celular. |
| payload.smsPhone, smsMessage | Abre un chat de WhatsApp con el mensaje escrito. | |
| sms | payload.smsPhone, smsMessage | Abre un SMS listo para enviar. |
| payload.emailTo, emailSubject | Abre un correo con el asunto escrito. | |
| text | payload.text | Texto libre. |
| crypto | payload.cryptoCoin, cryptoAddress | Dirección de una billetera (BTC, ETH…). |
Diseño
Son las mismas opciones del Studio, y el servidor las dibuja con el mismo algoritmo. Lo que no envíes toma el diseño por defecto del Studio.
| design.* | Valores | Qué cambia |
|---|---|---|
| color | #RRGGBB | Color de los puntos y las esquinas. Por defecto #0F172A. |
| color2 · gradient | #RRGGBB · true/false | Segundo color y degradado en diagonal, como en el Studio. |
| background | #RRGGBB | Fondo. Por defecto blanco. |
| dots_style | rounded, square, dots, extra-rounded, classy, classy-rounded | Forma de los puntos. |
| corners_style | leaf, extra-rounded, square, dot, classy, classy-rounded | Forma del marco de las tres esquinas. Por defecto leaf (gota, con la punta hacia el centro). |
| corner_dot_style | leaf, dot, square | Forma del punto dentro de cada esquina. Por defecto leaf. |
| logo_url · logo_size · logo_clear | https · 0.1 a 0.4 · true/false | Logo al centro (PNG, JPG, WebP o SVG, máx. 500 KB), su tamaño y si se quitan los puntos detrás. |
{
"title": "Mesa 4 - Menú",
"type": "dynamic",
"url": "https://turestaurante.com/menu",
"design": { "color": "#0F172A", "dots_style": "rounded", "corners_style": "leaf" }
}# 1. Crear el QR
curl -X POST https://qubo.click/api/v1/qr \
-H "X-API-KEY: tu_clave_de_api" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{"title": "Mesa 4 - Menú", "type": "dynamic", "url": "https://turestaurante.com/menu"}'
# 2. Descargar su imagen (usa el id de la respuesta)
curl "https://qubo.click/api/v1/qr/1284/image?format=png&size=1024" \
-H "X-API-KEY: tu_clave_de_api" -o mesa-4.pngResponde 201 Created. content es lo que lleva el QR e images trae las direcciones para descargarlo.
{
"success": true,
"data": {
"id": 1284,
"title": "Mesa 4 - Menú",
"type": "dynamic",
"content_type": "url",
"content": "https://qubo.click/r/AB12CD",
"short_code": "AB12CD",
"short_url": "https://qubo.click/r/AB12CD",
"redirect_url": "https://turestaurante.com/menu",
"is_active": true,
"images": {
"svg": "https://qubo.click/api/v1/qr/1284/image?format=svg",
"png": "https://qubo.click/api/v1/qr/1284/image?format=png"
},
"created_at": "2026-10-25T20:42:10+00:00"
}
}La versión anterior, POST /api/v1/qr/generate con title y redirect_url, sigue funcionando igual: crea un QR dinámico de URL.
Descargar la imagen
/api/v1/qr/{id}/image?format=png&size=1024| Parámetro | Valores | Descripción |
|---|---|---|
| format | png (por defecto) o svg | SVG es vectorial: ideal para imprenta y empaques. |
| size | 128 a 4096, por defecto 1024 | Ancho y alto en píxeles. |
Devuelve la imagen directamente (no JSON), con el diseño guardado del QR, también si lo editaste en el Studio. Solo descarga QR de tu cuenta.
Generar sin guardar
/api/v1/qr/renderMismos campos de Crear un QR (sin title ni type), más format y size. Devuelve la imagen y no guarda nada en tu cuenta ni gasta cupo de QR. Sirve para etiquetas, tickets o vCards que imprimes una sola vez.
curl -X POST https://qubo.click/api/v1/qr/render \
-H "X-API-KEY: tu_clave_de_api" \
-H "Content-Type: application/json" \
-d '{"content_type": "wifi", "payload": {"ssid": "Cafe_Invitados", "password": "cafe2026"}, "format": "svg"}' \
-o wifi.svgListar, editar y borrar
| Servicio | Acción | Detalle |
|---|---|---|
| GET /api/v1/qr | Listar | Tus QR, del más nuevo al más viejo. Filtros: type, content_type, tag, q (busca en el nombre), is_active; per_page hasta 100. |
| GET /api/v1/qr/{id} | Consultar | Un QR con su contenido, etiquetas y total de escaneos. |
| PATCH /api/v1/qr/{id} | Editar | Cambia title, is_active (pausar o activar), tags, design y el destino: url si es dinámico, payload si es estático. |
| DELETE /api/v1/qr/{id} | Borrar | Lo borra. Un QR dinámico impreso deja de funcionar. |
La lista responde data con los QR, meta con la página y el total, y links.next con la siguiente página. En el diseño solo se cambia lo que envías: el resto se queda igual. Si editas el contenido de un QR estático, los que ya imprimiste siguen llevando al contenido viejo; para poder cambiarlo después, usa uno dinámico.
# Tus QR dinámicos con la etiqueta "menu", 50 por página
curl "https://qubo.click/api/v1/qr?type=dynamic&tag=menu&per_page=50" -H "X-API-KEY: tu_clave_de_api"
# Cambiar el destino de un QR dinámico (el QR impreso sigue sirviendo)
curl -X PATCH https://qubo.click/api/v1/qr/1284 \
-H "X-API-KEY: tu_clave_de_api" -H "Content-Type: application/json" \
-d '{"url": "https://turestaurante.com/menu-navidad"}'
# Pausarlo: quien lo escanee verá un aviso de "no disponible"
curl -X PATCH https://qubo.click/api/v1/qr/1284 \
-H "X-API-KEY: tu_clave_de_api" -H "Content-Type: application/json" \
-d '{"is_active": false}'
# Escaneos de los últimos 30 días
curl "https://qubo.click/api/v1/qr/1284/stats?days=30" -H "X-API-KEY: tu_clave_de_api"Estadísticas
/api/v1/qr/{id}/stats?days=30Escaneos totales, visitantes únicos, escaneos por día y de dónde vienen: dispositivo y navegador en todos los planes; país, sistema y sitio de origen con analítica avanzada. days se ajusta solo al historial de tu plan (Pro: 365 días). Nunca incluye la IP de nadie.
{
"success": true,
"data": {
"id": 1284,
"days": 30,
"from": "2026-09-27",
"to": "2026-10-26",
"total_scans": 412,
"unique_visitors": 318,
"timeline": [{ "date": "2026-10-25", "scans": 17 }, …],
"breakdowns": {
"device_type": [{ "label": "mobile", "total": 389 }, …],
"country": [{ "label": "Colombia", "total": 371 }, …]
}
}
}Enlaces cortos y links de WhatsApp
| Servicio | Acción | Detalle |
|---|---|---|
| GET /api/v1/links | Listar | Enlaces cortos y links de WhatsApp. Filtros: type (url o whatsapp), tag, q (nombre o alias), is_active; per_page hasta 100. |
| POST /api/v1/links | Crear enlace | destination_url y, opcionales, title, alias y tags. Sin alias, se genera uno de 6 letras. |
| POST /api/v1/whatsapp | Crear link de WhatsApp | phone con indicativo (+57 300 123 4567) y message. Lleva a wa.me con el mensaje escrito. |
| GET /api/v1/links/{id} | Consultar | Un enlace con su destino y sus clics. |
| PATCH /api/v1/links/{id} | Editar | title, is_active, tags y el destino: destination_url en un enlace corto; phone o message en uno de WhatsApp. |
| DELETE /api/v1/links/{id} | Borrar | Lo borra; quien lo abra llegará al inicio de Qubo. |
| GET /api/v1/links/{id}/stats | Estadísticas | Clics por día y de dónde vienen, igual que en los QR. |
Los dos tipos comparten el cupo de enlaces de tu plan, igual que en el panel. El alias no se cambia: es lo que ya imprimiste o compartiste. Si lo necesitas distinto, crea otro enlace.
# Enlace corto con alias propio
curl -X POST https://qubo.click/api/v1/links \
-H "X-API-KEY: tu_clave_de_api" -H "Content-Type: application/json" \
-d '{"destination_url": "https://tutienda.com/promo", "alias": "promo-oct"}'
# Link de WhatsApp con el mensaje ya escrito
curl -X POST https://qubo.click/api/v1/whatsapp \
-H "X-API-KEY: tu_clave_de_api" -H "Content-Type: application/json" \
-d '{"phone": "+57 300 123 4567", "message": "Hola, quiero hacer un pedido"}'
# Cambiar el mensaje (el destino wa.me se arma solo)
curl -X PATCH https://qubo.click/api/v1/links/88 \
-H "X-API-KEY: tu_clave_de_api" -H "Content-Type: application/json" \
-d '{"message": "Hola, quiero reservar"}'BioLinks y contactos
| Servicio | Acción | Detalle |
|---|---|---|
| GET /api/v1/biolinks | Listar | Tus BioLinks con su dirección, visitas, bloques y contactos recibidos. |
| GET /api/v1/biolinks/{id} | Consultar | Un BioLink con sus bloques: tipo, texto y destino público. |
| GET /api/v1/biolinks/{id}/leads | Contactos | Lo que dejaron en sus formularios, lo más nuevo primero, con las respuestas de los campos a medida. since trae solo lo recibido desde una fecha. |
| GET /api/v1/biolinks/{id}/stats | Estadísticas | Visitas, clics en los bloques, porcentaje de clics y de dónde vienen. |
Los BioLinks se diseñan en el editor; por API se consultan. Con since puedes llevar cada contacto nuevo a tu CRM sin repetir los anteriores. Nunca salen datos privados de un bloque, como la clave del Wi-Fi o la dirección a la que envía un formulario. Son datos personales: úsalos solo para lo que autorizó cada persona (Ley 1581).
curl "https://qubo.click/api/v1/biolinks/4/leads?since=2026-10-01T00:00:00-05:00" -H "X-API-KEY: tu_clave_de_api"
{
"success": true,
"data": [{
"id": 312,
"form": "Reserve su viaje",
"name": "Laura Gómez",
"phone": "+57 310 555 1234",
"email": "laura@correo.co",
"answers": { "fecha_del_viaje": "2026-11-02", "numero_de_pasajeros": "3" },
"consented_at": "2026-10-25T14:02:11+00:00",
"created_at": "2026-10-25T14:02:11+00:00"
}],
"meta": { "current_page": 1, "last_page": 1, "per_page": 50, "total": 1 }
}Tarjetas de lealtad, cupones, tarjetas de regalo y eventos
| Servicio | Acción | Detalle |
|---|---|---|
| GET · POST /api/v1/loyalty-programs | Programas | Listar y crear tarjetas de sellos. GET, PATCH y DELETE /loyalty-programs/{id} para una; PATCH cambia solo lo que envías. |
| GET /api/v1/loyalty-programs/{id}/cards | Clientes | Las tarjetas de tus clientes con sus sellos y premios. since trae solo las nuevas. |
| POST …/cards/{code}/stamp | Sellar | Suma un sello desde tu punto de venta, igual que la caja. result reward si con ese completó la tarjeta. |
| POST …/cards/{code}/redeem | Premio | Entrega un premio ganado. 422 si no tiene premios por reclamar. |
| GET · POST …/coupons | Cupones | Listar y crear cupones con condiciones, vencimiento y cupo. PATCH y DELETE …/coupons/{couponId} para editar, pausar o borrar. |
| POST …/coupons/{couponId}/claims | Dar | Le da el cupón a un cliente con tarjeta (card_code) y devuelve el enlace de su cupón para mandárselo. |
| POST …/coupon-claims/{code}/redeem | Canjear | Canjea el cupón de un cliente una sola vez. 422 si ya se usó o venció. |
| GET · POST …/gift-cards | Regalo | Listar y emitir tarjetas de regalo con su valor en pesos. Cóbralas antes por tu cuenta: Qubo no cobra, solo lleva el saldo. |
| POST …/gift-cards/{code}/charge | Cobrar | Descuenta una compra del saldo (amount, note). Si no alcanza responde 422 y no descuenta nada. GET …/gift-cards/{code} da saldo y movimientos. |
| POST …/gift-cards/{code}/void | Anular | Deja el saldo en cero, por ejemplo si devolviste el dinero. |
| GET · POST …/events | Eventos | Listar y crear eventos con fecha, lugar, cupo y localidades. mode signup es gratis con inscripción; counter, entradas vendidas en tu mostrador. GET, PATCH y DELETE …/events/{eventId}. |
| GET · POST …/events/{eventId}/tickets | Entradas | Listar las entradas o emitirlas tras cobrar (tier_id, quantity, holder_name). order_url abre todas las de la venta. |
| POST …/tickets/{code}/check-in | Puerta | Registra la entrada una sola vez. 422 si ya entró y dice a qué hora. …/tickets/{code}/void la anula y devuelve el cupo. |
El code de una tarjeta o de un cupón es lo que va en su QR: tu sistema lo lee y llama a la API. Sellos, premios y cupones canjeados por API quedan en la estadística con el nombre API y se actualizan solos en Google Wallet.
# Tu punto de venta suma un sello al leer el QR del cliente
curl -X POST https://qubo.click/api/v1/loyalty-programs/3/cards/k2Jd9xQ.../stamp -H "X-API-KEY: tu_clave_de_api"
# {"success": true, "data": {"result": "reward", "card": {"stamps": 0, "rewards_available": 1, ...}}}
# Un cupón que vence a fin de mes, con cupo de 200
curl -X POST https://qubo.click/api/v1/loyalty-programs/3/coupons \
-H "X-API-KEY: tu_clave_de_api" -H "Content-Type: application/json" \
-d '{"title": "2x1 en boletas 2D", "expires_on": "2026-11-30", "max_claims": 200}'
# Dáselo a un cliente y mándale el enlace (data.url) por WhatsApp
curl -X POST https://qubo.click/api/v1/loyalty-programs/3/coupons/7/claims \
-H "X-API-KEY: tu_clave_de_api" -H "Content-Type: application/json" -d '{"card_code": "k2Jd9xQ..."}'Catálogos
| Servicio | Acción | Detalle |
|---|---|---|
| GET · POST /api/v1/catalogs | Catálogos | Listar y crear catálogos o menús. GET /catalogs/{id} trae sus productos en orden; PATCH y DELETE para editarlo o borrarlo. |
| POST /api/v1/catalogs/{id}/products | Productos | Agrega un producto con categoría, precio y foto. PATCH y DELETE …/products/{productId} para cambiarlo, pausarlo o borrarlo. |
| GET /api/v1/catalogs/{id}/stats | Estadísticas | Visitas y pedidos enviados por WhatsApp, con su total. |
Los pedidos se arman con los precios que tiene Qubo y llegan solo por WhatsApp. Mantén precios y existencias al día desde tu inventario o tu POS.
# Tu inventario cambió un precio: actualízalo en el menú
curl -X PATCH https://qubo.click/api/v1/catalogs/5/products/42 \
-H "X-API-KEY: tu_clave_de_api" -H "Content-Type: application/json" -d '{"price": 9000}'
# Se agotó: pausarlo sin borrarlo
curl -X PATCH https://qubo.click/api/v1/catalogs/5/products/42 \
-H "X-API-KEY: tu_clave_de_api" -H "Content-Type: application/json" -d '{"is_active": false}'Clientes
| Servicio | Acción | Detalle |
|---|---|---|
| GET /api/v1/customers | Listar | La misma lista de Clientes del panel: tarjetas, formularios e importados unidos por WhatsApp o correo. segment filtra new, returning o lapsed; q busca. |
| POST /api/v1/customers | Traer | Hasta 500 contactos por llamada desde tu CRM, tu tienda o tus concursos. La misma fuente con el mismo correo o WhatsApp solo completa lo que faltaba. |
| DELETE /api/v1/customers | Borrar | Todos los datos de una persona en tu cuenta (tarjetas, sellos, formularios e importados), por key, email o whatsapp. |
Cada contacto necesita correo o WhatsApp y consented_at: la fecha en que autorizó el tratamiento de sus datos en tu sistema. Sin esa fecha no se guarda. Usa source para saber de dónde llegó; así se ve en Clientes.
# Traer clientes de tu CRM o de un concurso (hasta 500 por llamada; repetir no duplica)
curl -X POST https://qubo.click/api/v1/customers \
-H "X-API-KEY: tu_clave_de_api" -H "Content-Type: application/json" \
-d '{"customers": [{"name": "Ana Gómez", "email": "ana@ejemplo.co", "whatsapp": "300 123 4567",
"source": "Concurso de octubre", "consented_at": "2026-10-04T14:03:36-05:00"}]}'
# {"success": true, "data": {"created": 1, "updated": 0, "unchanged": 0}}
# Los que no vienen hace 30 días
curl "https://qubo.click/api/v1/customers?segment=lapsed" -H "X-API-KEY: tu_clave_de_api"
# Alguien pidió que borren sus datos
curl -X DELETE https://qubo.click/api/v1/customers -H "X-API-KEY: tu_clave_de_api" \
-H "Content-Type: application/json" -d '{"email": "ana@ejemplo.co"}'Errores
Todos los errores responden con la misma forma: success: false y un error.code estable para que tu código decida qué hacer.
{
"success": false,
"message": "Revisa los datos enviados: El campo title es obligatorio.",
"error": { "code": "validation", "message": "Revisa los datos enviados: El campo title es obligatorio." },
"errors": { "title": ["El campo title es obligatorio."] }
}Falta la clave, no corresponde a ninguna cuenta activa o llegó en la URL en vez del encabezado.
Tu plan no incluye la API, o ya usaste el cupo de QR de tu plan.
Ese QR no existe en tu cuenta.
Algún dato no es válido. El campo errors dice cuál y por qué.
Superaste tu cupo de solicitudes por minuto. Espera el tiempo que indica Retry-After.
Límites
Cada clave tiene su propio cupo de solicitudes por minuto según el plan (Pro: 120 por minuto). Las respuestas traen X-RateLimit-Remaining y, si te pasas, Retry-After. La cantidad de QR que puedes crear es la de tu plan; en tu panel ves cuántas solicitudes llevas este mes.
OpenAPI y Postman
Toda la API está descrita en openapi.json (OpenAPI 3.1), el formato estándar que leen las herramientas de desarrollo. Con él generas un cliente en tu lenguaje o pruebas cada servicio sin escribir código.
- Postman: Import → Link → pega
https://qubo.click/openapi.json. Crea la colección con todos los servicios; en Authorization elige API Key,X-API-KEY, Header. - Insomnia o Bruno: importa el mismo enlace.
- Un cliente en tu lenguaje: OpenAPI Generator o el que use tu equipo, con el mismo archivo.
Webhooks
Con el plan Pro, Qubo envía una petición POST a tu URL cada vez que ocurre un evento. Los configuras en tu panel, en Webhooks: hasta 5 URLs https, cada una con los eventos que elijas.
| qr.scanned | Alguien escaneó un código QR dinámico. |
| link.clicked | Alguien abrió un enlace corto. |
| whatsapp.clicked | Alguien abrió un enlace de WhatsApp. |
| biolink.viewed | Alguien visitó un BioLink. |
| biolink.clicked | Alguien tocó un botón de un BioLink. |
| biolink.lead | Alguien envió un formulario de un BioLink (con sus respuestas). |
{
"id": "2f0c7a8e-5c1b-4a0e-9f4e-3d2b1a0c9e8f",
"event": "qr.scanned",
"occurred_at": "2026-10-07T20:42:10+00:00",
"data": {
"asset": { "type": "qr", "id": 1284, "title": "Mesa 4 - Menú", "short_code": "AB12CD" },
"visitor": { "country": "CO", "device_type": "mobile", "browser": "Chrome", "platform": "Android" }
}
}Cada petición lleva el encabezado X-Qubo-Signature: sha256=…: el HMAC-SHA256 del cuerpo con el secreto de ese webhook. Compruébalo antes de confiar en el evento. Responde con un código 2xx en menos de 5 segundos; si falla, Qubo reintenta hasta 3 veces. Por privacidad, los eventos nunca incluyen la IP del visitante.
// Node.js (Express): compara la firma con el cuerpo sin modificar
const crypto = require('crypto');
app.post('/webhooks/qubo', express.raw({ type: 'application/json' }), (req, res) => {
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.QUBO_WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
const received = req.get('X-Qubo-Signature') || '';
if (received.length !== expected.length ||
!crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected))) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body);
// ... tu lógica
res.sendStatus(200);
});