API de ConvoChat

Una API REST para gestionar enlaces, QR, páginas bio, dominios, proyectos y catálogos de presentación desde tu propio código, y leer sus estadísticas.

La API requiere un plan de pago. El plan Free no la incluye: sin ella toda petición responde 403 api_access_denied, y ni el panel deja emitir API keys.

Autenticación

Crea una API key en /app/api-keys (menú «Desarrollo» → «API keys»), elige sus scopes y envíala en cada petición como cabecera Authorization: Bearer <token>. El token se muestra una sola vez al crearlo; cópialo entonces. Todas las respuestas son JSON.

curl -s https://convo.chat/api/v1/links \
  -H "Authorization: Bearer TU_API_KEY" \
  -H "Accept: application/json"

Scopes

Hay 19 scopes, agrupados por recurso. Cada API key concede solo los que elijas; si a un token le falta el scope de la ruta, esta responde 403 missing_ability.

Enlaces cortos links:read · links:write Listar, crear, actualizar y eliminar enlaces cortos.
Analítica stats:read Leer las estadísticas de un enlace. También destapa clicks y unique_clicks dentro del propio recurso enlace.
Códigos QR qr:read · qr:write Listar, crear, actualizar y eliminar códigos QR dinámicos.
Páginas bio bio:read · bio:write Listar, crear, actualizar y eliminar páginas bio.
Dominios domains:read · domains:write Listar, dar de alta, verificar y eliminar tus dominios de marca.
Proyectos projects:read · projects:write Las carpetas con las que agrupas tus enlaces.
Píxeles pixels:read · pixels:write Píxeles de seguimiento.
Páginas de espera splash:read · splash:write Splash pages intersticiales previas al destino.
Overlays overlays:read · overlays:write Barras overlay que se muestran sobre el destino.
Datos capturados data:read · data:write Leer y eliminar los envíos de tus formularios bio. El alta llega desde la página pública, así que data:write solo borra.

Endpoints

Base: https://convo.chat/api/v1. Son 45 endpoints repartidos en 9 recursos; despliega el que te interese. Todas las listas se paginan con per_page (1–100, por defecto 15) y devuelven data, links y meta. Cada DELETE responde 204 sin cuerpo.

Enlaces cortos 6 endpoints

El {id} es el public_id del enlace.

GET /links Lista tus enlaces cortos, paginado (per_page entre 1 y 100; por defecto 15). links:read
POST /links Crea un enlace (201) o te devuelve uno igual que ya tienes (200). Lee «Creación y reutilización». links:write
GET /links/{id} Obtiene un enlace. links:read
PATCH /links/{id} Actualiza un enlace. El slug no se puede cambiar. links:write
DELETE /links/{id} Elimina un enlace. links:write
GET /links/{id}/stats Totales, serie diaria y horaria y desgloses (days: 7, 30 o 90). stats:read
Códigos QR 5 endpoints

El {id} es el public_id del QR.

GET /qr-codes Lista tus códigos QR, paginado. qr:read
POST /qr-codes Crea un código QR: dinámico (tipo url, con destino editable) o con el contenido dentro del dibujo. qr:write
GET /qr-codes/{id} Obtiene un código QR, con su content y su design. qr:read
PATCH /qr-codes/{id} Actualiza un código QR: solo lo que mandas, incluido el destino de uno dinámico. qr:write
DELETE /qr-codes/{id} Elimina un código QR. qr:write
Páginas bio 5 endpoints

El {id} es el public_id de la página.

GET /bio-pages Lista tus páginas bio, paginado. bio:read
POST /bio-pages Crea una página bio. bio:write
GET /bio-pages/{id} Obtiene una página bio. bio:read
PATCH /bio-pages/{id} Actualiza una página bio. bio:write
DELETE /bio-pages/{id} Elimina una página bio. bio:write
Dominios 6 endpoints

El {id} es el id numérico. El dominio principal no está en la lista: en un enlace o una página es domain_id 0. Con GET /domains?include=shared la lista añade, al final, los dominios compartidos del operador que tu plan deja usar —solo id, host, is_shared y status—, y su id vale como domain_id; no se pueden abrir, cambiar ni borrar. default_link_id es el id (public_id) del enlace exclusivo. Cada dominio trae su ciclo de verificación: verification_last_checked_at y verification_last_result (found, missing, mismatch o dns_error) dicen qué vimos la última vez; con mismatch, verification_last_values trae hasta 3 valores TXT que encontramos en lugar del token, recortados a 100 caracteres y con cada carácter invisible marcado con �, y fuera de mismatch es una lista vacía; verification_failed_at con estado pending es que ya no lo comprobamos solos, y disabled_by (owner u operator) dice quién lo desactivó. not_found_url tiene que ser de otro dominio y root_redirect_url no puede ser la portada del propio dominio (sí una ruta suya, como /mi-bio); con default_link_id, root_redirect_url se guarda vacío.

GET /domains Lista tus dominios propios, paginado. Con ?include=shared, también los compartidos que puedes usar. domains:read
POST /domains Da de alta un dominio; queda en estado pending. domains:write
GET /domains/{id} Obtiene un dominio y su token de verificación. domains:read
PATCH /domains/{id} Actualiza un dominio. domains:write
POST /domains/{id}/verify Comprueba ahora el TXT del dominio y devuelve el dominio. 429 si agotaste las comprobaciones a mano; 409 si lo desactivó el operador. domains:write
DELETE /domains/{id} Elimina un dominio. Devuelve 409 si aún conserva enlaces o si lo desactivó el operador. domains:write
Proyectos 5 endpoints

El {id} es el id numérico.

GET /projects Lista tus proyectos, paginado. projects:read
POST /projects Crea un proyecto. projects:write
GET /projects/{id} Obtiene un proyecto. projects:read
PATCH /projects/{id} Actualiza un proyecto. projects:write
DELETE /projects/{id} Elimina un proyecto. projects:write
Píxeles, páginas de espera y overlays 15 endpoints

Catálogos de presentación. Un enlace los usa con splash_page_id, overlay_id y pixel_ids, en POST y PATCH /links. El {id} es el id numérico.

GET /pixels Lista tus píxeles de seguimiento. pixels:read
POST /pixels Crea un píxel. pixels:write
GET /pixels/{id} Obtiene un píxel. pixels:read
PATCH /pixels/{id} Actualiza un píxel. pixels:write
DELETE /pixels/{id} Elimina un píxel. pixels:write
GET /splash-pages Lista tus páginas de espera. splash:read
POST /splash-pages Crea una página de espera. splash:write
GET /splash-pages/{id} Obtiene una página de espera. splash:read
PATCH /splash-pages/{id} Actualiza una página de espera. splash:write
DELETE /splash-pages/{id} Elimina una página de espera. splash:write
GET /overlays Lista tus overlays. overlays:read
POST /overlays Crea un overlay. overlays:write
GET /overlays/{id} Obtiene un overlay. overlays:read
PATCH /overlays/{id} Actualiza un overlay. overlays:write
DELETE /overlays/{id} Elimina un overlay. overlays:write
Datos capturados 3 endpoints

Los envíos entran desde tus páginas bio, no por API. El {id} es el id numérico; link_id y block_id son los id (public_id) de la página y del bloque que los recogió. Salen los mismos que ves en el panel.

GET /data-submissions Lista los envíos recibidos. Filtro opcional link_id: el id (public_id) de la página, el de GET /bio-pages. data:read
GET /data-submissions/{id} Obtiene un envío. data:read
DELETE /data-submissions/{id} Elimina un envío. data:write

Qué se manda en cada petición

Los POST y PATCH reciben un objeto JSON (Content-Type: application/json). En un PATCH solo cambia lo que mandas. Un campo que no está en estas tablas se ignora: los de solo lectura que devuelve un GET, como id, short_url o clicks, no estorban si los devuelves en un PATCH.

POST /links Crear un enlace corto

Si ya tienes uno igual y en servicio, te devuelve ese (lee «Creación y reutilización»).

Campo Tipo Hace falta Para qué
destination_url URL (http o https) obligatorio A dónde lleva el enlace.
domain_id número opcional El dominio de la dirección corta. 0, o no mandarlo, es el principal; si no, el id de un dominio tuyo activo o de uno compartido de tu plan (los da GET /domains?include=shared).
slug texto opcional El alias, lo que va tras el dominio. Requiere un plan con alias propios; si no lo mandas, se genera uno.
password texto opcional Contraseña para abrirlo, hasta 72 caracteres. Requiere un plan que la incluya.
status active o paused opcional Por defecto active.
starts_at fecha ISO 8601 opcional Desde cuándo funciona. Con zona horaria (2026-11-01T09:00:00-06:00); se guarda en UTC.
ends_at fecha ISO 8601 opcional Hasta cuándo funciona. Tiene que ser posterior a starts_at.
click_limit número opcional Tope de clics.
expiration_url URL (http o https) opcional A dónde lleva cuando vence o llega al tope.
is_sensitive booleano opcional Avisa de contenido sensible antes de abrir el destino.
http_status 301, 302, 307 o 308 opcional El tipo de redirección. Por defecto 301.
method direct, frame, splash, overlay opcional Cómo se abre: directo, dentro de un marco con barra, tras una página de espera o con un overlay encima. Por defecto direct.
splash_page_id número opcional La página de espera (id de GET /splash-pages). Solo cuenta con method=splash; sin ella se usa la predeterminada.
overlay_id número opcional El overlay (id de GET /overlays). Obligatorio con method=overlay; con otro método no se guarda.
pixel_ids lista de números opcional Los píxeles que se ejecutan en sus páginas intermedias (ids de GET /pixels), hasta 50. Una lista vacía o null los quita.
project_id número opcional El proyecto (id de GET /projects); null lo deja sin proyecto.
ads_enabled booleano opcional Anuncios en las páginas intermedias. Si tu plan los quita, no salen aunque esté activo.
forward_query booleano opcional Pasa al destino los parámetros que trae la visita. Por defecto true.
block_bots booleano opcional Responde 403 a los bots detectados. Por defecto false.
social_breakout booleano opcional Ayuda a abrir el destino fuera de las redes sociales. Por defecto false.
parameters objeto opcional Hasta 20 pares nombre → valor que se añaden al destino (ver «Parámetros y apertura de enlaces»).
utm objeto opcional Las cinco UTM de abajo. Se añaden al destino y ganan a cualquier otro parámetro con el mismo nombre.
utm.utm_source texto opcional Fuente, hasta 128 caracteres.
utm.utm_medium texto opcional Medio, hasta 128 caracteres.
utm.utm_campaign texto opcional Campaña, hasta 128 caracteres.
utm.utm_term texto opcional Término, hasta 128 caracteres.
utm.utm_content texto opcional Contenido, hasta 128 caracteres.

Ejemplo:

POST https://convo.chat/api/v1/links
{
    "destination_url": "https://ejemplo.com/rebajas",
    "slug": "rebajas-otono",
    "utm": {
        "utm_source": "instagram",
        "utm_campaign": "otono"
    },
    "method": "splash",
    "splash_page_id": 12,
    "pixel_ids": [
        3,
        7
    ],
    "project_id": 5
}
PATCH /links/{id} Cambiar un enlace corto

Los mismos campos que el alta, todos opcionales: solo cambia lo que mandas.

Campo Tipo Hace falta Para qué
destination_url URL (http o https) opcional A dónde lleva el enlace.
domain_id número opcional El dominio de la dirección corta. 0, o no mandarlo, es el principal; si no, el id de un dominio tuyo activo o de uno compartido de tu plan (los da GET /domains?include=shared).
slug texto no se puede cambiar Se fija al crear. Si lo mandas, tiene que ser el que ya tiene; otro responde 422.
password texto opcional Contraseña para abrirlo, hasta 72 caracteres. Requiere un plan que la incluya.
status active o paused opcional Por defecto active.
starts_at fecha ISO 8601 opcional Desde cuándo funciona. Con zona horaria (2026-11-01T09:00:00-06:00); se guarda en UTC.
ends_at fecha ISO 8601 opcional Hasta cuándo funciona. Tiene que ser posterior a starts_at.
click_limit número opcional Tope de clics.
expiration_url URL (http o https) opcional A dónde lleva cuando vence o llega al tope.
is_sensitive booleano opcional Avisa de contenido sensible antes de abrir el destino.
http_status 301, 302, 307 o 308 opcional El tipo de redirección. Por defecto 301.
method direct, frame, splash, overlay opcional Cómo se abre: directo, dentro de un marco con barra, tras una página de espera o con un overlay encima. Por defecto direct.
splash_page_id número opcional La página de espera (id de GET /splash-pages). Solo cuenta con method=splash; sin ella se usa la predeterminada.
overlay_id número opcional El overlay (id de GET /overlays). Obligatorio con method=overlay; con otro método no se guarda.
pixel_ids lista de números opcional Los píxeles que se ejecutan en sus páginas intermedias (ids de GET /pixels), hasta 50. Una lista vacía o null los quita.
project_id número opcional El proyecto (id de GET /projects); null lo deja sin proyecto.
ads_enabled booleano opcional Anuncios en las páginas intermedias. Si tu plan los quita, no salen aunque esté activo.
forward_query booleano opcional Pasa al destino los parámetros que trae la visita. Por defecto true.
block_bots booleano opcional Responde 403 a los bots detectados. Por defecto false.
social_breakout booleano opcional Ayuda a abrir el destino fuera de las redes sociales. Por defecto false.
parameters objeto opcional Hasta 20 pares nombre → valor que se añaden al destino (ver «Parámetros y apertura de enlaces»).
utm objeto opcional Las cinco UTM de abajo. Se añaden al destino y ganan a cualquier otro parámetro con el mismo nombre.
utm.utm_source texto opcional Fuente, hasta 128 caracteres.
utm.utm_medium texto opcional Medio, hasta 128 caracteres.
utm.utm_campaign texto opcional Campaña, hasta 128 caracteres.
utm.utm_term texto opcional Término, hasta 128 caracteres.
utm.utm_content texto opcional Contenido, hasta 128 caracteres.
remove_password booleano opcional Con true quita la contraseña.

Ejemplo:

PATCH https://convo.chat/api/v1/links/{id}
{
    "status": "paused",
    "ends_at": "2026-12-31T23:59:00-06:00"
}
POST /qr-codes Crear un código QR

Qué va en content depende de content_type: lo dice «Tipos de contenido del QR».

Campo Tipo Hace falta Para qué
name texto obligatorio Para reconocerlo en tu lista, hasta 64 caracteres.
content_type texto obligatorio Qué lleva el código: uno de los tipos de la tabla de tipos. No se puede cambiar después.
content objeto opcional Los campos del tipo elegido. Casi todos los tipos tienen alguno obligatorio.
design objeto opcional Colores, forma y tamaño (ver «Diseño del QR»). Lo que no mandes toma su valor por defecto.
project_id número opcional El proyecto (id de GET /projects).

Ejemplo:

POST https://convo.chat/api/v1/qr-codes
{
    "name": "Carta del restaurante",
    "content_type": "url",
    "content": {
        "url": "https://ejemplo.com/carta"
    },
    "design": {
        "foregroundColor": "#1F2937",
        "size": 600
    }
}
PATCH /qr-codes/{id} Cambiar un código QR

Solo cambia lo que mandas. content y design se mandan enteros: lo que devuelve GET /qr-codes/{id}, con tus cambios.

Campo Tipo Hace falta Para qué
name texto opcional Hasta 64 caracteres.
content objeto opcional Sustituye el contenido entero. En un QR dinámico (url) cambia el destino sin reimprimir; en uno estático cambia el dibujo.
design objeto opcional Sustituye el diseño entero.
project_id número opcional El proyecto (id de GET /projects); null lo quita.
remove_logo booleano opcional Con true quita el logotipo que se subió en el panel.

Ejemplo:

PATCH https://convo.chat/api/v1/qr-codes/{id}
{
    "content": {
        "url": "https://ejemplo.com/carta-invierno"
    }
}
POST /bio-pages Crear una página bio

Los bloques de la página se editan en el panel.

Campo Tipo Hace falta Para qué
slug texto opcional La dirección de la página. Requiere un plan con alias propios; si no lo mandas, se genera.
domain_id número opcional Como en los enlaces: 0 es el dominio principal.
password texto opcional Contraseña para verla. Requiere un plan que la incluya.
status active o paused opcional Por defecto active.
is_sensitive booleano opcional Avisa de contenido sensible antes de mostrarla.
seo_noindex booleano opcional Pide a los buscadores que no la indexen.
project_id número opcional El proyecto (id de GET /projects).
theme_id número opcional El tema, el que devuelve GET /bio-pages/{id}. Los temas se eligen en el panel.
display_branding booleano opcional Con false no se muestra la marca de ConvoChat en la página. Solo vale si tu plan lo permite; si no, se muestra igual.
leap_url URL (http o https) opcional Si la pones, la página lleva directamente a esta dirección en vez de mostrarse.
custom_css texto opcional CSS propio para la página.
meta objeto opcional El título y la descripción para buscadores y al compartir.
meta.title texto opcional Hasta 200 caracteres.
meta.description texto opcional Hasta 500 caracteres.
design objeto opcional El fondo, la tipografía, la disposición y los colores (background, typography, layout, colors), con la misma forma que devuelve GET /bio-pages/{id}.

Ejemplo:

POST https://convo.chat/api/v1/bio-pages
{
    "slug": "estudio-norte",
    "meta": {
        "title": "Estudio Norte"
    }
}
PATCH /bio-pages/{id} Cambiar una página bio

Los mismos campos que el alta, todos opcionales: solo cambia lo que mandas.

Campo Tipo Hace falta Para qué
slug texto no se puede cambiar Se fija al crear. Si lo mandas, tiene que ser el que ya tiene; otro responde 422.
domain_id número opcional Como en los enlaces: 0 es el dominio principal.
password texto opcional Contraseña para verla. Requiere un plan que la incluya.
status active o paused opcional Por defecto active.
is_sensitive booleano opcional Avisa de contenido sensible antes de mostrarla.
seo_noindex booleano opcional Pide a los buscadores que no la indexen.
project_id número opcional El proyecto (id de GET /projects).
theme_id número opcional El tema, el que devuelve GET /bio-pages/{id}. Los temas se eligen en el panel.
display_branding booleano opcional Con false no se muestra la marca de ConvoChat en la página. Solo vale si tu plan lo permite; si no, se muestra igual.
leap_url URL (http o https) opcional Si la pones, la página lleva directamente a esta dirección en vez de mostrarse.
custom_css texto opcional CSS propio para la página.
meta objeto opcional El título y la descripción para buscadores y al compartir.
meta.title texto opcional Hasta 200 caracteres.
meta.description texto opcional Hasta 500 caracteres.
design objeto opcional El fondo, la tipografía, la disposición y los colores (background, typography, layout, colors), con la misma forma que devuelve GET /bio-pages/{id}.
remove_password booleano opcional Con true quita la contraseña.

Ejemplo:

PATCH https://convo.chat/api/v1/bio-pages/{id}
{
    "status": "paused"
}
POST /domains Dar de alta un dominio

Queda pendiente hasta que publiques el registro TXT que devuelve y lo verifiques.

Campo Tipo Hace falta Para qué
host texto obligatorio El dominio, sin protocolo ni ruta: links.tumarca.com.
default_link_id texto opcional El enlace exclusivo: el id (public_id) de un enlace o página tuya, que se abre en la portada del dominio; null lo quita. Con él, root_redirect_url se guarda vacío. El id numérico de antes se sigue aceptando.
root_redirect_url URL (http o https) opcional A dónde lleva la portada del dominio. No puede ser la propia portada (sí una ruta suya, como /mi-bio).
not_found_url URL (http o https) opcional A dónde lleva una dirección del dominio que no existe. Tiene que ser de otro dominio.

Ejemplo:

POST https://convo.chat/api/v1/domains
{
    "host": "links.tumarca.com",
    "not_found_url": "https://tumarca.com"
}
PATCH /domains/{id} Cambiar un dominio

Solo el enrutamiento: para usar otro dominio, bórralo y da de alta el nuevo.

Campo Tipo Hace falta Para qué
default_link_id texto opcional El enlace exclusivo: el id (public_id) de un enlace o página tuya, que se abre en la portada del dominio; null lo quita. Con él, root_redirect_url se guarda vacío. El id numérico de antes se sigue aceptando.
root_redirect_url URL (http o https) opcional A dónde lleva la portada del dominio. No puede ser la propia portada (sí una ruta suya, como /mi-bio).
not_found_url URL (http o https) opcional A dónde lleva una dirección del dominio que no existe. Tiene que ser de otro dominio.

Ejemplo:

PATCH https://convo.chat/api/v1/domains/{id}
{
    "default_link_id": "01JA2B3C4D5E6F7G8H9J0K1M2N"
}
POST /projects Crear un proyecto

Un enlace entra en él con project_id.

Campo Tipo Hace falta Para qué
name texto obligatorio Hasta 255 caracteres, sin repetir el de otro proyecto tuyo.
color color #RRGGBB obligatorio El color con el que se distingue en el panel.

Ejemplo:

POST https://convo.chat/api/v1/projects
{
    "name": "Campaña de otoño",
    "color": "#FF5A36"
}
PATCH /projects/{id} Cambiar un proyecto

Solo cambia lo que mandas.

Campo Tipo Hace falta Para qué
name texto opcional Hasta 255 caracteres, sin repetir el de otro proyecto tuyo.
color color #RRGGBB opcional El color con el que se distingue en el panel.

Ejemplo:

PATCH https://convo.chat/api/v1/projects/{id}
{
    "color": "#2563EB"
}
POST /pixels Crear un píxel

Un enlace lo usa con pixel_ids.

Campo Tipo Hace falta Para qué
name texto obligatorio Para reconocerlo, hasta 255 caracteres.
provider facebook, google_analytics, gtm, google_ads, linkedin, x, pinterest, quora, tiktok, snapchat, reddit, bing, adroll obligatorio De quién es el píxel.
pixel_id texto obligatorio El identificador que te da el proveedor, con el formato de ese proveedor.

Ejemplo:

POST https://convo.chat/api/v1/pixels
{
    "name": "Meta de la tienda",
    "provider": "facebook",
    "pixel_id": "123456789012345"
}
PATCH /pixels/{id} Cambiar un píxel

Solo cambia lo que mandas.

Campo Tipo Hace falta Para qué
name texto opcional Para reconocerlo, hasta 255 caracteres.
provider facebook, google_analytics, gtm, google_ads, linkedin, x, pinterest, quora, tiktok, snapchat, reddit, bing, adroll opcional De quién es el píxel.
pixel_id texto opcional El identificador que te da el proveedor, con el formato de ese proveedor.

Ejemplo:

PATCH https://convo.chat/api/v1/pixels/{id}
{
    "name": "Meta (tienda nueva)"
}
POST /splash-pages Crear una página de espera

Un enlace la usa con method=splash y splash_page_id.

Campo Tipo Hace falta Para qué
name texto obligatorio Para reconocerla, hasta 255 caracteres.
title texto obligatorio El título que ve la visita.
content texto opcional El texto, hasta 4000 caracteres.
unlock_seconds número obligatorio Segundos hasta poder seguir, de 0 a 300.
auto_redirect booleano opcional Seguir sola al acabar la cuenta atrás. Por defecto true.
design objeto opcional Los tres colores de abajo.
design.background_color color #RRGGBB opcional Fondo.
design.text_color color #RRGGBB opcional Texto.
design.accent_color color #RRGGBB opcional Botón y detalles.

Ejemplo:

POST https://convo.chat/api/v1/splash-pages
{
    "name": "Aviso de salida",
    "title": "Vas a salir de nuestra web",
    "unlock_seconds": 5
}
PATCH /splash-pages/{id} Cambiar una página de espera

Solo cambia lo que mandas; design se sustituye entero.

Campo Tipo Hace falta Para qué
name texto opcional Para reconocerla, hasta 255 caracteres.
title texto opcional El título que ve la visita.
content texto opcional El texto, hasta 4000 caracteres.
unlock_seconds número opcional Segundos hasta poder seguir, de 0 a 300.
auto_redirect booleano opcional Seguir sola al acabar la cuenta atrás. Por defecto true.
design objeto opcional Los tres colores de abajo.
design.background_color color #RRGGBB opcional Fondo.
design.text_color color #RRGGBB opcional Texto.
design.accent_color color #RRGGBB opcional Botón y detalles.

Ejemplo:

PATCH https://convo.chat/api/v1/splash-pages/{id}
{
    "unlock_seconds": 3
}
POST /overlays Crear un overlay

Un enlace lo usa con method=overlay y overlay_id.

Campo Tipo Hace falta Para qué
name texto obligatorio Para reconocerlo, hasta 255 caracteres.
type contact, poll, message, newsletter, image, coupon obligatorio Qué muestra la barra sobre el destino.
config objeto opcional Los textos de abajo. Sin < ni >.
config.headline texto opcional Titular, hasta 160 caracteres.
config.body texto opcional Texto, hasta 1000 caracteres.
config.cta_label texto opcional Texto del botón, hasta 80 caracteres.
config.image_url URL (http o https) opcional Imagen.
config.coupon_code texto opcional Código de cupón: letras, números, espacios, _ . y -, hasta 64.

Ejemplo:

POST https://convo.chat/api/v1/overlays
{
    "name": "Cupón de bienvenida",
    "type": "coupon",
    "config": {
        "headline": "10 % de descuento",
        "coupon_code": "HOLA10"
    }
}
PATCH /overlays/{id} Cambiar un overlay

Solo cambia lo que mandas; config se sustituye entero.

Campo Tipo Hace falta Para qué
name texto opcional Para reconocerlo, hasta 255 caracteres.
type contact, poll, message, newsletter, image, coupon opcional Qué muestra la barra sobre el destino.
config objeto opcional Los textos de abajo. Sin < ni >.
config.headline texto opcional Titular, hasta 160 caracteres.
config.body texto opcional Texto, hasta 1000 caracteres.
config.cta_label texto opcional Texto del botón, hasta 80 caracteres.
config.image_url URL (http o https) opcional Imagen.
config.coupon_code texto opcional Código de cupón: letras, números, espacios, _ . y -, hasta 64.

Ejemplo:

PATCH https://convo.chat/api/v1/overlays/{id}
{
    "config": {
        "headline": "15 % de descuento",
        "coupon_code": "HOLA15"
    }
}

Tipos de contenido del QR

content_type dice qué lleva el código y content trae sus campos. Solo url es dinámico: el dibujo lleva una dirección corta y el destino se cambia sin reimprimir. Los que van a una página tuya —bio, evento, archivo, tarjeta— se mandan con link_id, el id de esa página. El resto lleva su contenido dentro del dibujo.

content_type Campos de content
url URL dinámica
  • url obligatorio · URL
text Texto
  • text obligatorio · texto
vcard vCard
  • link_id opcional · el id (public_id) de tu página
  • name opcional · texto
  • first_name opcional · texto
  • last_name opcional · texto
  • organization opcional · texto
  • title opcional · texto
  • phone opcional · texto
  • email opcional · texto
  • url opcional · URL
  • address opcional · texto
  • city opcional · texto
  • region opcional · texto
  • postal_code opcional · texto
  • country opcional · texto
  • note opcional · texto
event Evento
  • link_id obligatorio · el id (public_id) de tu página
file Archivo
  • link_id obligatorio · el id (public_id) de tu página
bio Página bio
  • link_id obligatorio · el id (public_id) de tu página
wifi Wi-Fi
  • security obligatorio · WPA, WEP, nopass
  • ssid obligatorio · texto
  • password opcional · texto
  • hidden opcional · true o false
phone Teléfono
  • phone obligatorio · texto
sms SMS
  • phone obligatorio · texto
  • message opcional · texto
email Correo electrónico
  • email obligatorio · texto
  • subject opcional · texto
  • body opcional · texto
whatsapp WhatsApp
  • phone obligatorio · texto
  • text opcional · texto
geo Ubicación
  • latitude obligatorio · texto
  • longitude obligatorio · texto
crypto Criptomoneda
  • currency obligatorio · bitcoin, ethereum
  • address obligatorio · texto
  • amount opcional · texto
  • label opcional · texto
  • message opcional · texto
  • chain_id opcional · número
paypal PayPal.Me
  • username obligatorio · texto
  • amount opcional · texto
  • currency opcional · AUD, BRL, CAD, EUR, GBP, JPY, MXN, USD
upi UPI
  • payee_address obligatorio · texto
  • payee_name obligatorio · texto
  • amount opcional · texto
  • currency obligatorio · INR
  • transaction_reference opcional · texto
  • note opcional · texto
epc Transferencia SEPA
  • bic opcional · texto
  • name obligatorio · texto
  • iban obligatorio · texto
  • amount opcional · texto
  • purpose opcional · texto
  • reference opcional · texto
  • remittance opcional · texto
  • information opcional · texto
pix Pix
  • key_type obligatorio · email, tax_id, phone, random
  • key obligatorio · texto
  • merchant_name obligatorio · texto
  • merchant_city obligatorio · texto
  • amount opcional · texto
  • description opcional · texto
  • txid opcional · texto
deep_link Abrir una app
  • uri obligatorio · texto

Diseño del QR

Las claves de design. Lo que no mandes al crear toma su valor por defecto. El color del código necesita un contraste de 4,5:1 con el fondo para que la cámara lo lea. El logotipo se sube desde el panel.

Clave Valores Por defecto
foregroundColor color #RRGGBB #000000
backgroundColor color #RRGGBB #FFFFFF
gradientEnabled true o false false
gradientColor color #RRGGBB #1D4ED8
gradientType horizontal, vertical, diagonal, inverse_diagonal, radial diagonal
eyeStyle square, rounded square
frameEnabled true o false false
frameText texto ESCANÉAME
moduleStyle square, rounded, dots square
size número de 128 a 2048 300
errorCorrection L, M, Q, H M
logoSizePercent número de 5 a 30 20

Parámetros y apertura de enlaces

POST /links y PATCH /links/{id} aceptan parameters, un objeto de hasta 20 pares de nombre y valor. Los nombres admiten letras ASCII, números y los signos ._~-, hasta 128 caracteres; los valores son texto de hasta 2048 caracteres, sin caracteres de control. Un valor vacío o null produce nombre=. No se aceptan objetos anidados ni los nombres reservados preview, signature, expires, password, gate, confirm. La precedencia es: consulta del destino, parámetros entrantes si forward_query está activo, parameters guardados y, al final, los cinco valores utm configurados. En un PATCH, omitir parameters conserva los valores; enviar null o un objeto vacío los elimina.

block_bots y social_breakout son booleanos y empiezan en false. El primero responde 403 a bots detectados, incluidos los rastreadores de vistas previas sociales, antes de contar una visita. Las vistas previas firmadas del titular siguen disponibles. El segundo añade ayuda para abrir el destino fuera de las redes sociales; con método directo muestra una página manual con los mismos metadatos para humanos y bots permitidos. Android ofrece un intento de apertura en Chrome para destinos HTTPS; copiar el enlace y continuar al destino siguen disponibles. La app y el dispositivo deciden si permiten abrir otro navegador. En un PATCH, omitir estos booleanos conserva su estado; enviar false los desactiva.

Creación y reutilización

POST /links no siempre crea. Si ya tienes un enlace corto igual al que pides y en servicio, la API te devuelve ese en lugar de crear otro: responde 200 (no 201), añade "meta": {"reused": true} y no gasta cupo de tu plan. Así, pedir dos veces el mismo enlace no te llena la lista de copias.

  • Igual es que abre lo mismo: el mismo destination_url en el mismo domain_id, con las mismas utm y parameters, el mismo http_status y method, la misma página de espera u overlay y los mismos interruptores. Y sin reglas de destino —por país, dispositivo, idioma o reparto entre destinos, las que se ponen en el panel—: un enlace con reglas no abre lo mismo para todos y nunca se reutiliza. project_id y pixel_ids cuentan si los mandas.
  • En servicio es activo, ya empezado, sin fecha de fin, sin tope de clics y sin contraseña. Uno pausado, vencido, agotado o programado no se reutiliza: se crea otro.
  • Si tu petición trae password, starts_at, ends_at, click_limit o "status": "paused", pides un enlace propio y siempre se crea.
  • Si mandas slug, cuenta: solo se reutiliza el enlace que ya tiene ese alias. Si tienes uno igual con otro alias, se crea uno nuevo con el tuyo; si el alias ya lo usa otro enlace que no se puede reutilizar, responde 422. Sin slug, recibes el que ya tienes, con el suyo. Para tener dos enlaces al mismo destino, pide otro alias o cambia algo que se note al abrirlo, como una utm_source por canal.
  • Si el igual que te devolvería lo deshabilitó el equipo de ConvoChat o está en revisión, no se hace una copia: responde 409 duplicate_destination_unavailable, con details.id y details.state. Uno retirado que no es igual —en otro dominio o con otras utm— no impide crear.
HTTP/1.1 200 OK
{
  "data": { "id": "...", "slug": "slug-original", "destination_url": "https://ejemplo.com" },
  "meta": { "reused": true }
}

Enlaces que respaldan un QR

Los enlaces cortos que un código QR dinámico usa por dentro no se exponen en esta API: no aparecen en GET /links y cualquier GET, PATCH, DELETE o GET /links/{id}/stats sobre ellos responde 404 resource_not_found, aunque sean tuyos. Gestiónalos por /qr-codes o desde el panel. Tampoco cuenta con ellos si comparas totales con lo que ves en /app.

Repetir una petición sin duplicar

Si una petición se corta y no sabes si llegó, repítela con la misma cabecera Idempotency-Key: no se hace dos veces. Vale en todos los POST. Usa una clave distinta por operación —un UUID, por ejemplo—, de hasta 255 caracteres visibles.

  • La misma clave con la misma petición devuelve la respuesta de la primera vez, con la cabecera Idempotent-Replayed: true, durante 24 horas.
  • La misma clave con otra petición —otra ruta u otros datos— responde 422 idempotency_key_reused.
  • Si la primera todavía no ha terminado, 409 idempotency_request_in_progress: espera un momento y repítela.
  • Solo se guarda una respuesta correcta (2xx). Si la primera falló, repetirla con la misma clave lo vuelve a intentar.
  • Cada credencial tiene sus propias claves: la misma clave desde otra API key u otra app es otra petición.
curl -s https://convo.chat/api/v1/links \
  -H "Authorization: Bearer TU_API_KEY" \
  -H "Idempotency-Key: 6f1c2a9e-1b7d-4f3a-9c2e-8d5b7a1e4c90" \
  -H "Content-Type: application/json" \
  -d '{"destination_url": "https://ejemplo.com/rebajas"}'

Límites

60 peticiones por minuto por cuenta, el mismo número para todos los planes de pago (el límite por plan todavía no está diferenciado). Es el techo de todas tus credenciales juntas —tus API keys y las apps que hayas autorizado—, así que crear más claves no da más cupo.

Si solo una credencial está usando la API, tiene el techo entero. Cuando hay varias a la vez, el minuto se reparte a partes iguales: cada una puede gastar como mucho el techo dividido entre las que están activas —con dos, 30 cada una; con tres, 20—. Así una integración que insiste se frena sola y no deja sin servicio a las demás. Cuenta como activa la credencial que hizo alguna petición en los últimos 60 segundos; la que deja de llamar libera su parte al cabo de ese tiempo. Si una credencial estaba sola y gastó el techo entero, la que llega después puede tener que esperar a que empiece el minuto siguiente; desde entonces cada una tiene su parte.

Cada respuesta trae X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset del límite que te frenará antes: el techo de la cuenta o, con varias credenciales activas, la parte de la tuya. Al pasarte recibes 429 rate_limit_exceeded con Retry-After, y details.scope dice cuál te frenó: account si fue el techo de la cuenta, credential si fue la parte de tu credencial, y entonces details.active_credentials dice entre cuántas se reparte.

Errores

Todo error llega con esta forma. error.code es estable: enrútate por él, no por el mensaje. details solo aparece cuando hay contexto.

{
  "error": {
    "code": "validation_failed",
    "message": "Los datos proporcionados no son válidos.",
    "details": { "destination_url": ["El campo destino es obligatorio."] }
  }
}
HTTP error.code Cuándo
400 invalid_idempotency_key La cabecera Idempotency-Key no es válida: entre 1 y 255 caracteres visibles, sin espacios.
401 unauthenticated Falta el token, es inválido o fue revocado.
403 api_access_denied Tu plan no incluye acceso a la API.
403 account_unavailable Tu cuenta no está activa.
403 missing_ability La API key no trae el scope que exige la ruta. details.required_abilities lo dice.
403 forbidden No tienes permiso para esa acción.
403 plan_limit_exceeded Alcanzaste un límite de tu plan. details.feature y details.current.
403 feature_not_available Pediste una función que tu plan no incluye. details.feature y details.field.
404 resource_not_found El recurso no existe, no es tuyo, o está oculto a la API.
405 method_not_allowed Ese método HTTP no existe en la ruta. El mensaje y la cabecera Allow dicen cuáles admite.
409 domain_has_links El dominio todavía conserva enlaces; muévelos antes de eliminarlo.
409 domain_disabled_by_operator El dominio lo desactivó el equipo: no se puede verificar, reactivar ni eliminar.
409 duplicate_destination_unavailable Ya tienes un enlace igual al que pides, y el equipo lo deshabilitó o lo está revisando: no se hace una copia. details.id y details.state dicen cuál y cómo está.
409 idempotency_request_in_progress Otra petición con la misma Idempotency-Key no ha terminado todavía. Repítela en un momento.
422 validation_failed Datos inválidos. details trae los errores campo por campo.
422 idempotency_key_reused Esa Idempotency-Key ya se usó con otra petición (otra ruta u otros datos).
429 rate_limit_exceeded Superaste un límite por minuto: el techo de tu cuenta o, con varias credenciales activas, la parte de la tuya. details.limit, details.retry_after y details.scope, que vale account o credential según cuál fue; con credential, details.active_credentials.
429 domain_verification_rate_limited Demasiadas comprobaciones a mano del TXT de un dominio en una hora. details.retry_after, en segundos, y cabecera Retry-After.
500 server_error Error interno.

OAuth 2.0 para apps de terceros

Una API key sirve para tu propio código. Si construyes una app que actúa en nombre de otros usuarios de ConvoChat, registra una app OAuth en /app/oauth-apps y usa el flujo authorization_code con PKCE. Las redirect URIs deben ser https (http solo en 127.0.0.1, [::1] o localhost).

GET https://convo.chat/oauth/authorize Pantalla de consentimiento.
POST https://convo.chat/oauth/token Canje de código y refresh.
GET https://convo.chat/api/oauth/v1/user Identidad del usuario que autorizó.

Con el access token llamas a los mismos 45 endpoints, con los mismos 19 scopes, cambiando la base a https://convo.chat/api/oauth/v1. Si no pides scopes, se conceden links:read y stats:read. Solo se acepta un bearer token real: una sesión de navegador no vale. El plan del usuario que autoriza es el que manda: si no incluye API, la llamada responde 403 api_access_denied.

  • PKCE es obligatorio para todas las apps: manda code_challenge (el SHA-256 del code_verifier, en base64url sin relleno) con code_challenge_method=S256; plain no se acepta. Sin eso, la persona vuelve a tu app con error=invalid_request. Manda también state y compruébalo al volver.
  • El token de acceso vale una hora (expires_in = 3600) y el de renovación, 60 días; cada renovación devuelve uno nuevo de 60 días y anula el anterior. Un token caducado o retirado responde 401 unauthenticated con WWW-Authenticate: Bearer error="invalid_token": renuévalo. Si la renovación responde invalid_grant, la persona tiene que volver a autorizar tu app.
  • Las apps de móvil, escritorio o navegador se registran sin secreto. En el mismo equipo usa http://127.0.0.1:{puerto} o http://[::1]:{puerto}, y el puerto puede cambiar en cada arranque; con localhost el puerto tiene que ser exactamente el registrado.
  • El sub del token es interno y opaco: el identificador de la persona es el public_id de /api/oauth/v1/user.
  • Un token sin el permiso de la ruta responde 403 missing_ability con details.required_abilities, igual que una API key.
  • Quien conectó tu app puede quitarle el acceso cuando quiera en Cuenta → Apps conectadas. Si añades una dirección de vuelta, la siguiente autorización vuelve a pedir consentimiento.

Avisos por webhook

Un aviso con el canal Webhook (en el panel: Cuenta → Notificaciones) hace un POST con un cuerpo JSON a tu dirección cada vez que ocurre uno de los eventos que elegiste. La dirección tiene que ser HTTPS en el puerto 443 y de un servidor público; no seguimos redirecciones. Responde con cualquier 2xx en menos de 8 segundos; si tienes que hacer algo largo, guárdalo y hazlo después.

Cabeceras

Cabecera Qué lleva
X-ConvoChat-Signature La firma: v1= seguido del HMAC-SHA256, en hexadecimal, de «marca de tiempo» + «.» + el cuerpo tal cual llega.
X-ConvoChat-Timestamp Cuándo se firmó, en segundos desde 1970 (UTC). Cada reintento lleva una marca nueva.
X-ConvoChat-Event El evento, el mismo que event en el cuerpo.
X-ConvoChat-Delivery La entrega: el mismo que delivery_id en el cuerpo. Se repite en los reintentos de esa entrega.

El cuerpo

Siempre el mismo sobre. id es el del evento: lo comparten todos los avisos que dispara ese evento. delivery_id es el de esta entrega y se repite si la reintentamos: deduplica por delivery_id. occurred_at va en UTC. data trae los datos del evento y sus claves dependen del tipo: puede ganar claves nuevas, así que ignora las que no conozcas.

{
    "id": "prueba:01K0KSKAN07AW2YNH65B4Y9B2M",
    "delivery_id": "01k0kskan07aw2ynh65b4y9b2m",
    "event": "test",
    "occurred_at": "2026-07-19T20:15:30.000000Z",
    "subject": {
        "type": "user",
        "id": 17
    },
    "data": {
        "title": "Notificación de prueba",
        "message": "Tu aviso de ConvoChat está configurado correctamente."
    }
}

Comprobar la firma

Con el secreto que pusiste en el aviso, calcula el HMAC-SHA256 de la marca de tiempo, un punto y el cuerpo sin tocar (los bytes que llegaron, no el JSON vuelto a escribir), y compáralo en tiempo constante con lo que va detrás de v1=. Rechaza los envíos cuya marca de tiempo tenga más de cinco minutos: así nadie puede repetirte uno viejo.

$marca  = $_SERVER['HTTP_X_CONVOCHAT_TIMESTAMP'];
$firma  = $_SERVER['HTTP_X_CONVOCHAT_SIGNATURE'];
$cuerpo = file_get_contents('php://input');

$esperada = 'v1=' . hash_hmac('sha256', $marca . '.' . $cuerpo, $secreto);

if (! hash_equals($esperada, $firma) || abs(time() - (int) $marca) > 300) {
    http_response_code(401);
    exit;
}

Para probar tu código: con el secreto un-secreto-de-ejemplo-de-al-menos-32-caracteres, la marca 1784492130 y este cuerpo, tal cual, en una sola línea:

{"id":"prueba:01K0KSKAN07AW2YNH65B4Y9B2M","delivery_id":"01k0kskan07aw2ynh65b4y9b2m","event":"test","occurred_at":"2026-07-19T20:15:30.000000Z","subject":{"type":"user","id":17},"data":{"title":"Notificación de prueba","message":"Tu aviso de ConvoChat está configurado correctamente."}}

la cabecera de la firma tiene que ser:

X-ConvoChat-Signature: v1=9fe1ecd01a52485861deb2e71d8bdf2b19e605a9da83260951acb6eb635c77fa

Reintentos y pausas

  • Si no responde, tarda más de 8 segundos o contesta con un 5xx, 408 o 429, lo intentamos 4 veces en total, esperando 15 s, 60 s, 300 s entre uno y otro.
  • Cualquier otro 4xx no se reintenta: esperar no lo arregla.
  • Un 3xx tampoco: no seguimos redirecciones, así que tu dirección volvería a redirigir. Pega en el aviso la dirección final, la que queda después de redirigir.
  • Tras 5 entregas seguidas que no llegan, pausamos el aviso y te lo decimos por correo y en el panel. En la pestaña «Entregas» del aviso ves cada envío y por qué falló.

Eventos

event Cuándo llega
new_click Hito de clics
form_submission Nueva respuesta de formulario o captura
form_limit_reached Formulario al tope de respuestas
booking_created Nueva cita reservada
booking_cancelled Cita cancelada por quien reservó
payment Pago recibido
affiliate_commission Comisión de afiliado
ai_generation_done Generación IA terminada
dunning_payment_failed Pago de suscripción fallido
dunning_recovered Pago de suscripción recuperado
dunning_grace_expired Periodo de gracia vencido
subscription_canceled Suscripción cancelada
subscription_expired Suscripción expirada
social_post_failed Publicación en redes que no salió
social_handoff Conversación pasada a una persona
email_campaign_failed Campaña de correo que no terminó de enviarse
test Notificación de prueba (el botón «Enviar prueba»)

Cambios

Lo que cambia en la v1 y puede afectar a una integración que ya funciona se anota aquí, con su fecha.

  • link_id y block_id de GET /data-submissions, y default_link_id de GET /domains, pasan de número a texto: ahora son el id público de la página, del bloque o del enlace, el mismo que da el resto de la API. Antes eran ids internos que no se podían cruzar con nada. Al mandarlos —el filtro ?link_id, default_link_id en POST y PATCH /domains y content.link_id de un QR— vale el id público, y el número de antes se sigue aceptando.
  • POST /links ya no te devuelve como reutilizado (200) un enlace que no está en servicio o que no es igual al que pides —otras utm, otro alias, reglas de destino—: crea otro (201). Si el igual lo retiró el equipo de ConvoChat, responde 409 duplicate_destination_unavailable. Lee «Creación y reutilización».
  • Con OAuth, un token sin el permiso de la ruta responde 403 missing_ability con details.required_abilities, igual que una API key; antes era 403 forbidden.
  • Con OAuth, el token de acceso vale una hora y el de renovación 60 días (antes, un año los dos), y PKCE con S256 es obligatorio para todas las apps, también las que tienen secreto.
  • El límite por minuto se reparte entre las credenciales que lo usan a la vez (lee «Límites»). Con una sola credencial no cambia nada.
  • PATCH /qr-codes/{id} cambia solo lo que mandas: antes, no mandar design lo devolvía a sus valores por defecto y no mandar project_id sacaba el QR de su proyecto.
  • POST y PATCH /links aplican project_id, method, splash_page_id, overlay_id y pixel_ids, que antes se descartaban sin avisar, y GET los devuelve.

Nuevo ese día: la cabecera Idempotency-Key en todos los POST —si ya la mandabas, ahora se respeta, y una mal formada responde 400 invalid_idempotency_key—, GET /domains?include=shared, y content y design en los códigos QR.

Empieza a construir

Crea tu cuenta, activa un plan con acceso a la API y haz tu primera petición en minutos.

Crear cuenta

Registrarte es gratis. La API no entra en el plan Free: la activas desde /app/billing cuando quieras.