Ir al contenido

Formularios de Comunicación: recibir solicitudes desde tu sitio o tu sistema

Los formularios de Comunicación reciben solicitudes —contacto, cotización, reclamo, ejercicio de derechos sobre datos personales— y cada una entra a la bandeja de tu equipo como un caso con número, plazo y responsable. La persona recibe un acuse por correo con ese número. Esta guía explica las tres formas de hacer llegar esas solicitudes: incrustando el formulario en tu sitio, con tu propio formulario HTML, o desde tu servidor.


Un mismo formulario recibe solicitudes por tres puertas, y las tres crean el mismo caso: igual tipo, campos, plazo y destino. Sólo cambia la puerta que queda registrada.

PuertaQuién envíaCredencialCuándo conviene
AlojadaUna persona, en la página del formulario o en el formulario incrustado en tu sitioNinguna, con captchaQuieres el formulario listo, sin programar. Ver Incrustar el formulario
PúblicaEl navegador de la persona, desde tu propio formulario HTMLNinguna, con captchaQuieres que el formulario tenga el diseño de tu sitio. Ver Tu propio formulario HTML
ServidorTu sistema, de servidor a servidorUna API key de tu empresaTu sistema ya tiene los datos. Ver Desde tu servidor

La puerta alojada y la pública son anónimas: quien llena el formulario no tiene cuenta. Por eso tienen defensas que no se pueden apagar: captcha, campo trampa, tope de envíos por visitante y por IP, y la cuota diaria de tu empresa. La de servidor no las tiene, porque la API key ya identifica a tu empresa.


Si tu empresa no tiene el servicio, no puedes crear formularios y la puerta de servidor responde 403 diciéndolo.

Los formularios se crean y se publican en el panel, en Comunicación → Tipos de solicitud y formularios. Sólo se puede enviar a un formulario publicado: uno en borrador o despublicado responde 409 FORMULARIO_NO_PUBLICADO, y uno archivado responde 404.

De cada formulario necesitas, según la puerta:

  • El identificador del formulario (idPublico): 22 letras y números. Está en la pestaña Publicar, en la sección «Enviar desde tu servidor». Lo usan la puerta pública y la de servidor.
  • La clave de cada campo: en la pestaña Campos, cada campo muestra su «Identificador interno» (por ejemplo correo o mensaje). Son las claves de campos y de archivos, en la puerta pública y en la de servidor.

Vale para la puerta pública y la de servidor:

Tipo de campo (en el panel)Valor en campos
Texto cortoTexto, hasta 200 caracteres
Texto largoTexto, hasta 5.000 caracteres
Correo electrónicoTexto con un correo válido, hasta 254 caracteres
TeléfonoTexto, hasta 20 caracteres
RUTTexto, con o sin puntos y guion. Se valida el dígito verificador
Lista de opcionesEl valor de la opción, no su etiqueta. Si admite varias, un arreglo de valores sin repetir
Casillatrue o false. Una casilla obligatoria o de declaración tiene que ir en true
ArchivoNo va en campos: va en archivos, con su archivoId. Ver Adjuntar archivos

Un campo obligatorio que no mandas, o un valor que no cumple su tipo, responde 422 CAMPO_INVALIDO con el campo y el motivo. Nunca se rellena un valor faltante ni se crea la solicitud a medias.

Un campo que el formulario no define se rechaza

Sección titulada «Un campo que el formulario no define se rechaza»

Si mandas una clave que el formulario no tiene, el envío responde 422 con todas esas claves, y no se crea nada:

{
"statusCode": 422,
"error": "CAMPO_NO_DEFINIDO",
"message": "Los campos «empresa_x» no existen en este formulario",
"campos": ["empresa_x"]
}

Es a propósito: un dato que tu integración cree estar enviando y que la plataforma descartara en silencio sería un dato perdido. Si alguien de tu equipo elimina un campo del formulario, te enteras en el primer envío, no semanas después.

En la práctica, esto quiere decir que tu código manda sólo las claves que el formulario define: si tu formulario HTML tiene inputs propios (un buscador, un campo de tu analítica), no los mandes. Lo más simple es una lista blanca que traduzca el name de cada input a la clave del campo.


Es lo más rápido: el formulario lo dibuja la plataforma, con los colores y el logo que configuraste en la pestaña Presentación, y el captcha y la sesión ya vienen resueltos.

En la pestaña Publicar del formulario, la sección «Compartir el formulario» tiene dos cosas:

  • El enlace: una página propia del formulario, para compartir por correo, redes o un código QR.
  • El código para incrustar: un <iframe> y un <script> que pegas en tu sitio donde quieras el formulario. El script ajusta el alto del iframe al contenido, para que no aparezcan barras de desplazamiento.

Una solicitud que llega así queda con la puerta alojada.

Si quieres que el formulario sea parte del diseño de tu sitio, puedes hacer tu propio <form> y mandarlo desde el navegador a la puerta pública. No necesitas backend: la puerta responde a cualquier origen (CORS abierto, sin cookies ni cabeceras propias), así que un sitio estático puede hablarle directo.

El envío tiene tres pasos:

1. GET /public/comunicacion/formularios/{idPublico}
2. GET /public/comunicacion/formularios/{idPublico}/desafio
3. POST /public/comunicacion/formularios/{idPublico}/envios
  1. Definición: los campos, el texto informativo y la sesion del visitante, firmada y válida por 2 horas.
  2. Desafío: un captcha que el navegador resuelve solo, sin que la persona haga nada.
  3. Envío: responde 201 con el número de la solicitud.

Pide la definición al cargar la página o cuando la persona empieza a escribir, no al enviar: así también puedes mostrar el texto informativo que configuraste. Si el formulario está despublicado, la definición responde { "estado": "despublicado", "mensaje": "…" } y conviene mostrar ese mensaje en vez del formulario.

El captcha es de ALTCHA: una prueba de trabajo que el navegador calcula, sin imágenes que resolver. El desafío trae algorithm, challenge, salt, signature y maxnumber; hay que encontrar el número n tal que el SHA-256 de salt + n sea igual a challenge, y mandar la solución en base64. Cada solución sirve para un solo envío y vence a los 5 minutos: pide un desafío nuevo para cada envío.

async function resolverDesafio(base) {
const d = await (await fetch(`${base}/desafio`)).json();
for (let n = 0; n <= d.maxnumber; n++) {
const bytes = new TextEncoder().encode(d.salt + n);
const hash = new Uint8Array(await crypto.subtle.digest('SHA-256', bytes));
const hex = Array.from(hash, (b) => b.toString(16).padStart(2, '0')).join('');
if (hex === d.challenge) {
return btoa(JSON.stringify({
algorithm: d.algorithm, challenge: d.challenge, number: n, salt: d.salt, signature: d.signature,
}));
}
}
throw new Error('No se pudo resolver el desafío');
}

crypto.subtle sólo existe en páginas servidas por https (o en localhost).

const base = 'https://api.redcumbre.cl/public/comunicacion/formularios/{idPublico}';
const definicion = await (await fetch(base)).json(); // al cargar la página
async function enviar(campos, trampa) {
const res = await fetch(`${base}/envios`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
sesion: definicion.sesion,
altcha: await resolverDesafio(base),
trampa, // el valor del campo trampa: una persona lo deja vacío
campos, // { nombre: '…', correo: '…', mensaje: '…' }
}),
});
return { status: res.status, cuerpo: await res.json() };
}

La respuesta trae el número y lo que hay que mostrar al terminar, según lo que configuraste en el panel: un texto o una dirección a la que llevar a la persona.

{ "numero": 42, "exito": { "texto": "Recibimos tu solicitud N° 42. Te enviamos un correo con el detalle." } }
{ "numero": 42, "exito": { "redireccion": "https://www.tu-empresa.cl/gracias" } }

La respuesta no trae nada más: con el identificador del formulario no se puede leer ninguna solicitud.

En la pestaña Presentación, «Dominios autorizados» limita desde qué sitios se acepta un envío por la puerta pública: por ejemplo tu-empresa.cl, que cubre también sus subdominios. Si declaras dominios, un envío desde otro sitio —o sin la cabecera Origin, que el navegador manda sola— responde 403 ORIGEN_NO_DECLARADO. Sin dominios declarados, se acepta desde cualquiera.

Este filtro reduce el ruido de quien copia tu formulario en otro sitio, pero no es un control de acceso: un programa que no es un navegador puede declarar el origen que quiera. Lo que contiene el abuso son el captcha, los topes y la cuota.

LímiteValorAl pasarlo
Envíos por visitante, por formulario5 por hora (el visitante es la sesión)429
Envíos por IP30 por hora429
Desafíos por IP60 por minuto429
Archivos por sesión10429
Cuota diaria de tu empresa1.000 envíos al día, salvo que REDCUMBRE haya fijado otra para tu empresa429 CUOTA_DIARIA_ALCANZADA

El 429 de la cuota diaria trae el código en codigo y el texto en mensaje, no en error y message. Un formulario de ejercicio de derechos no se rechaza por la cuota: la solicitud entra retenida, y tu equipo decide en la bandeja si es legítima: el ejercicio de un derecho no se pierde por volumen. La puerta de servidor no cuenta para la cuota.

Igual que en la puerta de servidor, primero subes y después envías. La subida es POST /public/comunicacion/formularios/{idPublico}/archivos en multipart/form-data, con el archivo en el campo archivo y la sesión en el campo sesion. Devuelve { archivoId }, que va en archivos del envío. El archivo queda ligado a esa sesión: con otra, el envío responde 422 ARCHIVO_NO_DISPONIBLE. La subida no lleva captcha; el desafío viaja en el envío.

CódigoQué pasóQué hacer
401 SESION_FORMULARIO_INVALIDALa sesión falta, venció (2 horas) o es de otro formularioPide la definición otra vez y reintenta, sin borrar lo que escribió la persona
403 sin códigoEl captcha falta, es inválido o ya se usóPide un desafío nuevo
403 ORIGEN_NO_DECLARADOEl sitio no está entre los dominios autorizadosRevisa la pestaña Presentación
404El formulario no existe o está archivado. La definición también responde 404 si está en borradorRevisa el identificador
409 FORMULARIO_NO_PUBLICADOEl formulario se despublicóMuestra el mensaje de la definición
422 CAMPO_INVALIDOUn valor no cumple: vienen campo y motivoMuestra el error junto al campo
422 CAMPO_NO_DEFINIDOMandaste una clave que el formulario ya no tieneAjusta la lista de campos que mandas
429Un tope o la cuota diariaPide a la persona que lo intente más tarde

Los 422 y el 403 de origen llevan el código en error: decide por el código, no por el texto.


Una API key con el rol COMUNICACION_SOLICITUDES_CREAR

Sección titulada «Una API key con el rol COMUNICACION_SOLICITUDES_CREAR»

La llave la crea un administrador de tu empresa en el panel, en General → API Keys. En el desplegable de rol, el que corresponde es Comunicación — crear solicitudes.

El rol es angosto a propósito: permite crear solicitudes y subir sus archivos, y nada más. Con esa llave no se leen casos, no se listan formularios y no se configura nada: cualquier otra operación responde 403. Así, si la llave se filtra desde tu servidor, quien la tenga puede crear solicitudes pero no puede leer ninguna de las que ya recibiste.

Una llave revocada responde 401 desde la llamada siguiente.

Ventana de terminal
curl -X POST "https://api.redcumbre.cl/{tenantSlug}/comunicacion/solicitudes" \
-H "Authorization: Bearer {api_key}" \
-H "Content-Type: application/json" \
-d '{
"formulario": "4fT9kQ2mXbR7vLpZ1cN8sW",
"claveIdempotencia": "contacto-pedido-4471",
"referencia": "pedido-4471",
"persona": { "idExterno": "cliente-0932" },
"campos": {
"nombre": "María Soto",
"correo": "maria.soto@ejemplo.cl",
"rut": "12.345.678-5",
"motivo": "despacho",
"mensaje": "El pedido llegó incompleto.",
"acepta_contacto": true
}
}'

Respuesta:

{ "numero": 42, "sandbox": false }

numero es el número de la solicitud dentro de tu empresa: el mismo que ve tu equipo en la bandeja. La respuesta no trae nada más, ni el estado, ni la persona, ni los campos: la puerta de servidor sólo crea.

CampoQué es
formularioEl identificador del formulario (22 caracteres)
claveIdempotenciaUna clave de tu sistema que hace seguro el reintento. Ver Reintentar sin duplicar
camposLos valores, por clave de campo
archivosEl archivoId de cada campo de tipo archivo. Ver Adjuntar archivos
referenciaOpcional. Un texto de tu sistema que queda con la solicitud y vuelve en cada aviso
persona.idExternoOpcional. El identificador de la persona en tu sistema

Los tipos y los largos exactos están en Swagger.

claveIdempotencia es obligatoria, de 8 a 100 caracteres. Si repites la llamada con la misma clave, la respuesta es 200 con el mismo número: no se crea otra solicitud y la persona no recibe otro acuse. El primer envío responde 201.

RespuestaQué significa
201 { numero }Solicitud creada
200 { numero }Esa clave ya tenía solicitud: es la misma, no se creó otra

La clave vale por empresa, y la llave de prueba y la real tienen cada una su espacio: una clave que usaste probando no choca con la misma clave en real.

Esta clave es propia de los formularios y va en el cuerpo, como claveIdempotencia. No es la idempotencyKey de los documentos tributarios descrita en Reglas transversales.

Si el formulario tiene campos de tipo archivo, el envío va en dos pasos: primero subes cada archivo y después creas la solicitud con los identificadores que te devolvió la subida.

Ventana de terminal
curl -X POST "https://api.redcumbre.cl/{tenantSlug}/comunicacion/solicitudes/archivos" \
-H "Authorization: Bearer {api_key}" \
-F "archivo=@boleta.pdf"
{ "archivoId": "cmb2x7k1p0003qz8f4h2d9n1e", "sandbox": false }
{
"formulario": "4fT9kQ2mXbR7vLpZ1cN8sW",
"claveIdempotencia": "reclamo-pedido-4471",
"campos": { "correo": "maria.soto@ejemplo.cl", "mensaje": "Adjunto la boleta." },
"archivos": { "comprobante": "cmb2x7k1p0003qz8f4h2d9n1e" }
}

Reglas de los archivos:

  • Tipos: PDF, JPEG y PNG, reconocidos por su contenido, no por la extensión ni por el tipo que declares. Un ejecutable renombrado a .pdf se rechaza al subirlo.
  • Tamaño: hasta 10 MB por archivo. Uno más grande se corta en la subida con 413.
  • Cuántos: hasta 10 subidas por llave cada 24 horas.
  • Quién lo usa: el archivo queda ligado a la llave que lo subió. Crear la solicitud con otra llave responde 422 ARCHIVO_NO_DISPONIBLE.
  • Cuánto dura: un archivo que no se usa en una solicitud dentro de 24 horas se borra.
RechazoCuándo
422 ARCHIVO_TIPO_NO_PERMITIDOAl subir: el contenido no es PDF, JPEG ni PNG
413Al subir: el archivo pasa de 10 MB
422 ARCHIVO_NO_DISPONIBLEAl crear la solicitud: el archivo no existe, venció o lo subió otra llave

Tu equipo descarga los archivos desde el caso, en el panel. No hay una URL pública del archivo.

CódigoQué pasó¿Reintentar?
401Falta la llave, no es válida o fue revocadaNo. Revisa el header Authorization
403La llave no tiene el rol COMUNICACION_SOLICITUDES_CREAR, o tu empresa no tiene el servicio ComunicaciónNo
404El formulario no existe en tu empresa o está archivadoNo. Revisa el identificador
409 FORMULARIO_NO_PUBLICADOEl formulario está en borrador o despublicadoNo, hasta que alguien lo publique
422 CAMPO_NO_DEFINIDOMandaste claves que el formulario no tiene: vienen en camposNo. Corrige el cuerpo
422 CAMPO_INVALIDOFalta un obligatorio o un valor no cumple su tipo: vienen campo y motivoNo. Corrige el cuerpo
413 / 422 ARCHIVO_*Ver Adjuntar archivosNo
5xxError del lado de RedcumbreSí, con la misma claveIdempotencia

Los 422 de los formularios llevan el código en error: decide por ese código, no por el texto de message. La forma general de los errores está en Reglas transversales.


Saber qué pasa con la solicitud: los avisos

Sección titulada «Saber qué pasa con la solicitud: los avisos»

Ninguna de las tres puertas lee solicitudes. Lo que pasa después con cada una te llega por webhook, con tres eventos del módulo Comunicación:

EventoCuándo
solicitud.creadaSe creó una solicitud, entrara por la puerta que entrara
solicitud.resueltaTu equipo la cerró. Trae el motivo, por ejemplo respondida o descartada
solicitud.reabiertaUna solicitud cerrada se volvió a abrir

Te suscribes a ellos como a cualquier otro evento, en el panel, en General → Webhooks. La firma, los reintentos y la verificación HMAC son los de todos los webhooks: ver Webhooks.

{
"event": "solicitud.resuelta",
"data": {
"numero": 42,
"tipo": "contacto",
"estado": "resuelto",
"motivo": "respondida",
"referencia": "pedido-4471",
"puerta": "servidor",
"retenidaPorCuota": false,
"ocurridoAt": "2026-10-20T14:05:12.000Z"
},
"timestamp": "2026-10-20T14:05:13.000Z",
"sandbox": false
}
  • referencia es la que mandaste al crear la solicitud. Úsala para amarrar el aviso con el registro de tu sistema. Si no mandaste referencia, el campo no viene.
  • estado es abierto en solicitud.creada y solicitud.reabierta, y resuelto en solicitud.resuelta.
  • puerta dice por dónde entró: servidor, publica o alojada. Una solicitud que tu equipo creó dentro de la bandeja —por ejemplo, convirtiendo una conversación— trae conversion, cambio_tipo o respuesta_tardia.
  • El aviso no trae datos de la persona: ni nombre, ni correo, ni ningún valor de los campos. Es a propósito: un webhook pasa por más sistemas que la bandeja, y los datos personales se quedan en ella.

El modo de prueba existe sólo en la puerta de servidor, porque es una propiedad de la credencial. Con una llave de prueba que tenga el mismo rol, la solicitud se crea marcada de prueba:

  • Ninguna persona recibe correo: el acuse queda en la bandeja de mensajes simulados, que ves con redcumbre bandeja.
  • Los avisos van firmados con el secreto de prueba y llegan a tu URL de prueba, nunca a la real.
  • No se cobra.
  • La solicitud no aparece en la bandeja del modo real.

Los eventos solicitud.* no se disparan con redcumbre eventos disparar: se provocan creando una solicitud con la llave de prueba, que es la prueba real del flujo.


Cada solicitud que se crea cuenta como un envío de formulario, por cualquiera de las tres puertas. No cuentan un reintento con la misma clave de idempotencia, un envío rechazado ni una solicitud de prueba. Los precios están en redcumbre.cl/precios.


👉 Ver los endpoints de servidor en Swagger