API de Galmia
Con esta API puedes enviar a Galmia los datos de un formulario desde tu propio sistema: tu sitio web, tu CRM, tu app o cualquier proceso que ya tengas andando. Lo que entra por acá llega igual que si la persona hubiera llenado el formulario en la web de Galmia: queda el registro, se crea el lead, el negocio recibe su aviso y la recepcionista IA entra a trabajar.
Está escrito para que lo lea quien programa la integración. No hace falta conocer Galmia por dentro.
Qué necesitas antes de empezar
- Una clave de API. La genera el dueño de la cuenta desde el panel, en Configuración → API y claves.
- El identificador del formulario al que vas a enviar los datos. También está en el panel, y la API te lo puede listar.
1. Dirección y formato
| Qué | Valor |
|---|---|
| Base | https://api.agendario.cl/api/public/v1/ |
| Formato | JSON en el cuerpo y en la respuesta (Content-Type: application/json) |
| Autenticación | Header Authorization: Bearer <tu-clave> |
El dominio de la API conserva el nombre anterior de la plataforma; es el correcto y no va a cambiar sin aviso previo.
Todas las respuestas correctas devuelven un objeto JSON. Los errores vienen siempre con la misma forma, descrita en la sección 7.
2. Autenticación
Cada llamada lleva tu clave en el header Authorization:
La clave se ve así: glm_live_ seguido de una cadena larga. En los ejemplos de
esta guía va en la variable de entorno GALMIA_API_KEY, que es también la forma
en que conviene tenerla en tu servidor: nunca escrita dentro del código.
export GALMIA_API_KEY="la-clave-que-te-dio-el-panel"
curl https://api.agendario.cl/api/public/v1/forms/ \
-H "Authorization: Bearer $GALMIA_API_KEY"
Tres cosas que conviene saber de las claves:
- Se muestran una sola vez. Al generarla, el panel te la enseña y después guarda únicamente su huella. Si la pierdes, no se recupera: se genera otra y se revoca la anterior.
- Van en tu servidor, nunca en el navegador. Una clave en el HTML o en el JavaScript de tu sitio queda a la vista de cualquier visitante. Si lo que necesitas es capturar datos desde una página web, usa el formulario incrustable de Galmia, que está hecho para eso y no necesita clave.
- Se pueden revocar en cualquier momento desde el mismo panel. La revocación es inmediata.
Permisos
Cada clave lleva los permisos que le dieron al crearla. Un endpoint al que le
falta su permiso responde 403, aunque la clave sea válida.
| Permiso | Para qué sirve |
|---|---|
forms:read | Listar formularios y consultar sus campos |
forms:write | Enviar formularios |
Conviene darle a cada integración solo lo que necesita: si una clave hecha para enviar formularios se filtra, lo máximo que alguien puede hacer con ella es enviar formularios.
3. Listar tus formularios
GET /api/public/v1/forms/
Requiere forms:read.
curl https://api.agendario.cl/api/public/v1/forms/ \
-H "Authorization: Bearer $GALMIA_API_KEY"
{
"data": [
{
"id": 42,
"slug": "contacto-sitio-web",
"name": "Contacto sitio web",
"layout": "two-steps",
"is_active": true
}
]
}
Los dos sirven para referenciar el formulario y los endpoints aceptan
cualquiera de los dos. Para guardar la referencia en tu código preferimos el
id: el slug se puede cambiar a mano desde el panel, y el id no cambia
nunca. (Renombrar el formulario ya no mueve su slug, así que esto dejó de ser
una trampa, pero el id sigue siendo la referencia más segura.)
4. Ver los campos de un formulario
GET /api/public/v1/forms/{id-o-slug}/
Requiere forms:read. Te dice exactamente qué claves esperar en el envío, así
no tienes que adivinarlas:
curl https://api.agendario.cl/api/public/v1/forms/42/ \
-H "Authorization: Bearer $GALMIA_API_KEY"
{
"data": {
"id": 42,
"slug": "contacto-sitio-web",
"name": "Contacto sitio web",
"layout": "two-steps",
"is_active": true,
"fields": [
{
"name": "servicio",
"type": "select",
"label": "¿Qué servicio te interesa?",
"required": true,
"options": ["Limpieza facial", "Depilación láser"]
},
{ "name": "name", "type": "text", "label": "Nombre", "required": true },
{ "name": "whatsapp", "type": "phone", "label": "WhatsApp", "required": true }
]
}
}
El name de cada campo es la clave que va en el envío. Los tipos posibles son
text, email, tel, phone, select, textarea, checkbox, date y
number.
Dos detalles del esquema:
nameywhatsappaparecen siempre, incluso si el negocio no los declaró como preguntas: son obligatorios en todo envío.- Un campo
phoneacepta además el código de país en una clave aparte,<campo>_countryCode. Si no lo mandas se asume+56.
5. Enviar un formulario
POST /api/public/v1/forms/{id-o-slug}/submissions/
Requiere forms:write.
curl -X POST https://api.agendario.cl/api/public/v1/forms/42/submissions/ \
-H "Authorization: Bearer $GALMIA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: contacto-9f3c1a" \
-d '{
"fields": {
"name": "Ana Soto",
"whatsapp": "912345678",
"whatsapp_countryCode": "+56",
"email": "ana@example.com",
"servicio": "Limpieza facial"
},
"source_url": "https://miclinica.cl/contacto",
"utm": { "utm_source": "google", "utm_medium": "cpc" }
}'
| Campo | Obligatorio | Qué es |
|---|---|---|
fields | sí | Objeto con las respuestas. Las claves son los name del esquema, y los valores texto, número o booleano |
fields.name | sí | Nombre de la persona |
fields.whatsapp | sí | Su número de WhatsApp |
source_url | no | Página donde se originó el contacto |
referrer | no | De dónde venía |
user_agent | no | Navegador de la persona, si lo tienes |
utm | no | Objeto con utm_source, utm_medium, utm_campaign, utm_content, utm_term |
timestamp | no | Momento del envío, como texto |
Respuesta 201:
{
"submission_id": "7c9f0b5e-1d2a-4f3b-8c7d-6e5f4a3b2c1d",
"lead_id": "a1b2c3d4-5678-90ab-cdef-1234567890ab",
"created_at": "2026-10-26T15:04:05.123456-03:00"
}
Guarda el submission_id: es la referencia del envío y lo que nos sirve para
encontrarlo si algo no cuadra. El lead_id es el contacto en el embudo del
negocio, por si quieres cruzarlo con tu propio sistema; en casos raros puede
venir null, y eso no significa que el envío se haya perdido.
Qué pasa después del envío
Lo mismo que si la persona hubiera llenado el formulario en la web:
- Queda el registro del envío con todas sus respuestas.
- Se crea el lead en el embudo del negocio (o se actualiza el que ya existía para ese contacto).
- El negocio recibe su aviso por correo.
- Si la cuenta lo tiene configurado, sale el mensaje de WhatsApp al contacto y la recepcionista IA queda a cargo de la conversación.
- Si la cuenta tiene la Conversions API de Meta configurada, se envía el evento de conversión.
Es decir: un envío por API no es "solo guardar un dato". Dispara la misma operación que el formulario. Tenlo presente si estás importando datos históricos — para eso no uses esta API.
6. Idempotencia
Si tu proceso puede reintentar un envío (un timeout, una cola que repite),
manda el header Idempotency-Key con un valor propio de ese envío:
Idempotency-Key: contacto-9f3c1a
La clave la eliges tú y puede tener el largo que quieras. Vale para ese formulario: la misma clave contra otro formulario es otro envío.
Cuando llega una clave de idempotencia que ya se usó en las últimas 24
horas, la API no crea nada nuevo y responde 200 con el envío original:
{ "submission_id": "7c9f0b5e-...", "duplicate": true }
Dos puntos sobre los que conviene ser claro: la ventana son 24 horas, no para siempre; y la protección es por clave de API, así que dos integraciones distintas pueden usar el mismo número de pedido sin pisarse.
7. Errores
Todos los errores tienen la misma forma:
{
"error": {
"code": "form_not_found",
"message": "No encontramos un formulario activo con la referencia \"43\" en tu cuenta.",
"details": {}
}
}
Ramifica siempre por code, nunca por message: el texto puede cambiar, el
código no.
| HTTP | code | Qué pasó y qué hacer |
|---|---|---|
| 401 | invalid_api_key | Falta la clave, o está mal escrita, revocada o vencida. Revisa el header o genera otra |
| 403 | scope_missing | La clave es válida pero no tiene el permiso del endpoint. El mensaje dice cuál falta |
| 404 | form_not_found | El formulario no existe, está desactivado o no es de tu cuenta |
| 400 | validation_error | Falta fields, o falta el nombre o el WhatsApp dentro de fields |
| 429 | rate_limited | Te pasaste de tu cuota de llamadas. Espera los segundos del header Retry-After |
| 429 | too_many_rejected | Te rechazamos demasiadas peticiones seguidas. No es tu cuota: la clave está mal o le falta el permiso. Arréglalo antes de reintentar |
| 500 | internal_error | Un problema nuestro. Si se repite, escríbenos con la hora exacta |
Un detalle a propósito: un formulario de otra cuenta responde 404, igual que
uno que no existe. No hay forma de averiguar desde afuera qué formularios
tienen las demás cuentas.
8. Límites de uso
Los límites son por clave, no por dirección IP, así que no te afecta que tus llamadas salgan todas del mismo servidor.
| Operación | Límite |
|---|---|
| Enviar formularios | 60 por hora, por clave |
| Lecturas (listar, ver campos) | 600 por hora, por clave |
| Peticiones rechazadas (401 y 403) | 60 por hora, por dirección IP |
La última fila cuenta solo lo que rechazamos, y va por dirección IP en vez de por clave —cuando la clave no sirve, no hay clave que contar—. Una integración que funciona nunca la toca. Si la ves, el problema no es tu volumen: revisa la clave y sus permisos antes de seguir reintentando, porque mientras acumules rechazos esa dirección queda bloqueada aunque después mandes la clave buena.
Cada cuenta puede tener hasta 10 claves activas a la vez. Si necesitas una más, revoca alguna que ya no uses.
El límite de envíos es estrecho a propósito: cada envío manda correos y mensajes de WhatsApp reales, que el negocio paga. Si tu volumen legítimo es mayor, escríbenos y lo subimos para tu clave.
Al pasarte, la API responde 429 con el header Retry-After en segundos.
Reintenta después de ese tiempo, de preferencia con una espera que vaya
creciendo.
9. Versionado y cambios
La versión va en la ruta: /api/public/v1/.
Mientras siga siendo v1:
- Podemos agregar campos a las respuestas y parámetros opcionales al cuerpo. Tu integración no debería romperse por un campo nuevo que no conoce, así que no valides que la respuesta tenga exactamente las claves de hoy.
- No vamos a quitar ni renombrar campos existentes, ni a cambiar el
significado de un
codede error.
Si alguna vez hay un cambio que rompe el contrato, sale como v2 y te avisamos
con al menos 60 días de anticipación, con la v1 andando en ese tiempo.
10. Checklist de integración
- La clave vive en una variable de entorno del servidor, no en el código ni en el repositorio.
- La clave tiene solo los permisos que esta integración necesita.
- Guardas el
idnumérico del formulario, no el slug. - Mandas
Idempotency-Keysi tu proceso puede reintentar. - Manejas el
429esperando lo que diceRetry-After. - Ramificas por
error.codey no por el texto del mensaje. - Probaste un envío real y lo viste llegar al panel del negocio.
11. Contacto
Escríbenos a soporte@galmia.ai. Para que podamos ayudarte rápido, incluye:
- El
submission_idsi lo tienes, o el prefijo de tu clave (los caracteres que el panel sigue mostrando, por ejemploglm_live_a1b2c3). - La hora exacta del intento.
- La petición que enviaste y la respuesta que recibiste.
Nunca nos mandes tu clave completa, ni por correo ni por WhatsApp. Con el prefijo nos alcanza para identificarla.