> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trybrein.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Enviar desde tu servidor

> Usa tu propio formulario HTML y tu backend, y envía cada Respuesta a Brein con la clave del formulario.

A veces el formulario debe verse como el resto de tu sitio. Construye el HTML, envíalo a tu backend y deja que ese backend mande las respuestas a Brein. El recorrido tiene tres pasos:

```
Tu formulario HTML  →  tu servidor (/api/brein)  →  Brein (POST …/responses)
```

El visitante no habla con Brein ni ve la clave del formulario. Tu servidor agrega la clave, asigna los campos a las claves de las preguntas y crea la Respuesta, que llega al CRM como un contacto.

## Crear la clave del formulario

Abre **Compartir → Desde tu servidor** y crea una clave. La clave completa se ve como `frm_sk_…` y se muestra **una sola vez**; Brein guarda solo su huella y un prefijo corto. Cópiala en tu gestor de secretos. Revoca una clave desde el mismo diálogo cuando un servidor deba dejar de enviar.

El mismo diálogo muestra el endpoint de tu formulario y las claves exactas de las preguntas, con su tipo y si son obligatorias. Usa esas claves como llaves de `answers`.

## Enviar una respuesta

Manda un `POST` al endpoint público con la clave en el encabezado `Authorization`:

```bash theme={"theme":"github-light"}
curl -X POST https://api.trybrein.com/api/v1/forms/frm_AbC123dEf4/responses \
  -H "Authorization: Bearer $BREIN_FORM_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"answers":{"correo":"ana@empresa.com"},"consent":{"privacy":true}}'
```

`answers` es un objeto cuyas llaves son las claves de las preguntas del formulario. `consent.privacy` debe ser `true`. `Idempotency-Key` es opcional: enviar la misma clave dos veces devuelve la misma Respuesta.

Un envío correcto responde `202`:

```json theme={"theme":"github-light"}
{ "id": "8f1c…", "status": "received" }
```

## Ejemplo: tu formulario y tu backend

Este ejemplo tiene tres partes: un formulario HTML en tu sitio, un manejador de ruta de Next.js (`app/api/brein/route.ts`) en tu servidor y la clave en tu entorno. Cambia `frm_AbC123dEf4` por el id de tu formulario y los nombres de campo por las claves de **Compartir → Desde tu servidor**.

Pon la clave en el entorno de tu servidor, nunca en la página:

```bash theme={"theme":"github-light"}
BREIN_FORM_KEY=frm_sk_…
```

El formulario HTML envía a tu propia ruta. El diseño es tuyo; sólo debes respetar los nombres de campo y la casilla de consentimiento:

```html theme={"theme":"github-light"}
<form action="/api/brein" method="post">
  <input type="text" name="nombre" placeholder="Tu nombre" />
  <input type="email" name="correo" placeholder="Tu correo de trabajo" required />
  <label>
    <input type="checkbox" name="privacy" required />
    Acepto el <a href="/privacidad">aviso de privacidad</a>
  </label>
  <button type="submit">Enviar</button>
</form>
```

El manejador lee ese POST, asigna cada campo a la clave de su pregunta, agrega el consentimiento y lo envía a Brein con la clave del entorno. Si funciona, redirige a una página de agradecimiento; si falla, devuelve el estado y cuerpo de error de Brein:

```ts theme={"theme":"github-light"}
// app/api/brein/route.ts
import { NextResponse } from 'next/server'

const BREIN_RESPONSES_URL = 'https://api.trybrein.com/api/v1/forms/frm_AbC123dEf4/responses'

export async function POST(request: Request) {
  const data = await request.formData()

  const response = await fetch(BREIN_RESPONSES_URL, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.BREIN_FORM_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      answers: {
        nombre: String(data.get('nombre') ?? ''),
        correo: String(data.get('correo') ?? ''),
      },
      consent: { privacy: data.get('privacy') === 'on' },
    }),
  })

  if (!response.ok) {
    return NextResponse.json(await response.json(), { status: response.status })
  }
  return NextResponse.redirect(new URL('/gracias', request.url), 303)
}
```

Una pregunta de selección múltiple envía una lista: léela con `data.getAll('intereses')` y pasa ese arreglo como respuesta. Cualquier otro servidor funciona igual; sólo cambian el envío y la respuesta.

## Códigos de estado

| Estado | Significado |
| - | - |
| `202` | Aceptado. La Respuesta queda en cola y se procesará. |
| `401` | Falta la clave, o no es válida o ya fue revocada. |
| `403` | La clave es de otro formulario, el formulario está pausado, o un envío desde el navegador llegó de un sitio que no está permitido. |
| `404` | El formulario no existe o sigue siendo un borrador. |
| `422` | Hay una respuesta que corregir. El cuerpo usa la [forma de error](/es/api/errors) con `errors`, una entrada por pregunta. |
| `429` | Demasiadas solicitudes. Espera y reintenta. |

## Deja la clave en el servidor

La clave de un formulario es una credencial. Nunca la pongas en un navegador, una app móvil, un repositorio público ni en código que un visitante pueda leer. El HTML anterior envía a tu propia ruta para que la clave nunca salga de tu servidor. Si prefieres no construir un formulario, usa el [embed](/es/forms/embed) o el [enlace público](/es/forms/share-by-link): esa puerta revisa Turnstile y los dominios permitidos por ti.

## Relacionado

* [Compartir por enlace](/es/forms/share-by-link)
* [Insertar en tu web](/es/forms/embed)
* [Errores](/es/api/errors)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.