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

# Enviar formulario

> Envía un botón que abre un Native Flow de WhatsApp para captura de datos estructurados

**Auth:** `TokenAccount` o `TokenInstance` • **Rate-limit:** `Global` (100/min) • **Idempotente:** no

## Descripción

Envía un mensaje con un botón que abre un **Native Flow** de WhatsApp, un formulario nativo que recolecta datos estructurados (nombre, teléfono, email, CPF/CNPJ, dirección) sin salir de la conversación. Soporta dos flujos preconfigurados: `contact_details` (Datos del cliente) y `registration_offer` (Oferta de registro), además de un escape hatch vía `buttonParamsJSON` para flujos completamente personalizados. Ideal para captura de leads, registros y ofertas con confirmación rápida.

<Warning>
  **Compatibilidad de cliente:** los Native Flows **aún no son compatibles con WhatsApp Web/Desktop**. El botón del formulario solo se renderizará para destinatarios en las apps oficiales de **Android** e **iOS**, en otros clientes el mensaje aparecerá sin el botón interactivo.
</Warning>

## Ejemplos

### Oferta de registro (registration\_offer)

Envía una oferta con título y descripción. El botón abre el Flow default de registro con campos visibles configurables.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://ryzeapi.cloud/api/message/form/$Instance_Name" \
    -H "token: $Token_Instance" \
    -H "Content-Type: application/json" \
    -d '{
      "number":           "5511999999999",
      "message":          "Lock in your spot in the course with 50% off!",
      "formType":         "registration_offer",
      "buttonLabel":      "I want it",
      "offerName":        "Go Course - May Cohort",
      "offerDescription": "Lifetime access + certificate + 30-day support"
    }'
  ```

  ```javascript JavaScript theme={null}
  await fetch(`https://ryzeapi.cloud/api/message/form/${process.env.Instance_Name}`, {
    method: "POST",
    headers: {
      "token":        process.env.Token_Instance,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      number:           "5511999999999",
      message:          "Lock in your spot in the course with 50% off!",
      formType:         "registration_offer",
      buttonLabel:      "I want it",
      offerName:        "Go Course - May Cohort",
      offerDescription: "Lifetime access + certificate + 30-day support"
    })
  });
  ```

  ```python Python theme={null}
  import os, requests

  requests.post(
      f"https://ryzeapi.cloud/api/message/form/{os.environ['Instance_Name']}",
      headers={
          "token":        os.environ["Token_Instance"],
          "Content-Type": "application/json"
      },
      json={
          "number":           "5511999999999",
          "message":          "Lock in your spot in the course with 50% off!",
          "formType":         "registration_offer",
          "buttonLabel":      "I want it",
          "offerName":        "Go Course - May Cohort",
          "offerDescription": "Lifetime access + certificate + 30-day support"
      }
  )
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "os"
      "strings"
  )

  func main() {
      body := strings.NewReader(`{
          "number":           "5511999999999",
          "message":          "Lock in your spot in the course with 50% off!",
          "formType":         "registration_offer",
          "buttonLabel":      "I want it",
          "offerName":        "Go Course - May Cohort",
          "offerDescription": "Lifetime access + certificate + 30-day support"
      }`)
      req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/form/"+os.Getenv("Instance_Name"), body)
      req.Header.Set("token", os.Getenv("Token_Instance"))
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
  }
  ```
</CodeGroup>

### Captura de datos de contacto (contact\_details)

Usa el flujo oficial `contact_details` de WhatsApp. Oculta los campos que no quieres pedir vía las flags `*Visible`. Aquí pedimos solo nombre, teléfono y email.

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://ryzeapi.cloud/api/message/form/$Instance_Name" \
    -H "token: $Token_Instance" \
    -H "Content-Type: application/json" \
    -d '{
      "number":                 "5511999999999",
      "message":                "To finish your support request, please complete your details:",
      "formType":               "contact_details",
      "buttonLabel":            "Fill in details",
      "fullNameVisible":        true,
      "phoneNumberVisible":     true,
      "emailVisible":           true,
      "cpfOrCnpjVisible":       false,
      "deliveryAddressVisible": false
    }'
  ```

  ```javascript JavaScript theme={null}
  await fetch(`https://ryzeapi.cloud/api/message/form/${process.env.Instance_Name}`, {
    method: "POST",
    headers: {
      "token":        process.env.Token_Instance,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      number:                 "5511999999999",
      message:                "To finish your support request, please complete your details:",
      formType:               "contact_details",
      buttonLabel:            "Fill in details",
      fullNameVisible:        true,
      phoneNumberVisible:     true,
      emailVisible:           true,
      cpfOrCnpjVisible:       false,
      deliveryAddressVisible: false
    })
  });
  ```

  ```python Python theme={null}
  import os, requests

  requests.post(
      f"https://ryzeapi.cloud/api/message/form/{os.environ['Instance_Name']}",
      headers={
          "token":        os.environ["Token_Instance"],
          "Content-Type": "application/json"
      },
      json={
          "number":                 "5511999999999",
          "message":                "To finish your support request, please complete your details:",
          "formType":               "contact_details",
          "buttonLabel":            "Fill in details",
          "fullNameVisible":        True,
          "phoneNumberVisible":     True,
          "emailVisible":           True,
          "cpfOrCnpjVisible":       False,
          "deliveryAddressVisible": False
      }
  )
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "os"
      "strings"
  )

  func main() {
      body := strings.NewReader(`{
          "number":                 "5511999999999",
          "message":                "To finish your support request, please complete your details:",
          "formType":               "contact_details",
          "buttonLabel":            "Fill in details",
          "fullNameVisible":        true,
          "phoneNumberVisible":     true,
          "emailVisible":           true,
          "cpfOrCnpjVisible":       false,
          "deliveryAddressVisible": false
      }`)
      req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/form/"+os.Getenv("Instance_Name"), body)
      req.Header.Set("token", os.Getenv("Token_Instance"))
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
  }
  ```
</CodeGroup>

### Flujo personalizado vía `buttonParamsJSON`

Escape hatch para tus propios flujos (creados en WhatsApp Business Manager). Cuando se proporciona `buttonParamsJSON`, el servidor **ignora** todos los demás campos relacionados con el flujo (`formType`, `flowId`, flags de visibilidad, `offerName`, etc.) y usa el JSON literal como `params` del botón Native Flow.

<CardGroup cols={2}>
  <Card title="Crear Flows" icon="wand-magic-sparkles" href="https://business.facebook.com/latest/whatsapp_manager/flows">
    Abre el WhatsApp Business Manager para crear y gestionar tus Flows personalizados (obtén el `flow_id` aquí).
  </Card>

  <Card title="Flows Playground" icon="flask" href="https://developers.facebook.com/documentation/business-messaging/whatsapp/flows/playground">
    Prueba y prototipa schemas de Flow en el playground oficial de Meta antes de enviarlos a producción.
  </Card>
</CardGroup>

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST "https://ryzeapi.cloud/api/message/form/$Instance_Name" \
    -H "token: $Token_Instance" \
    -H "Content-Type: application/json" \
    -d '{
      "number":           "5511999999999",
      "message":          "Take the quick survey and earn 10% off",
      "buttonLabel":      "Take survey",
      "buttonParamsJSON": "{\"flow_message_version\":\"3\",\"flow_token\":\"my-token-123\",\"flow_id\":\"123456789012345\",\"flow_cta\":\"Take survey\",\"flow_action\":\"navigate\",\"flow_action_payload\":{\"screen\":\"WELCOME\"}}"
    }'
  ```

  ```javascript JavaScript theme={null}
  const flowParams = {
    flow_message_version: "3",
    flow_token:           "my-token-123",
    flow_id:              "123456789012345",
    flow_cta:             "Take survey",
    flow_action:          "navigate",
    flow_action_payload:  { screen: "WELCOME" }
  };

  await fetch(`https://ryzeapi.cloud/api/message/form/${process.env.Instance_Name}`, {
    method: "POST",
    headers: {
      "token":        process.env.Token_Instance,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      number:           "5511999999999",
      message:          "Take the quick survey and earn 10% off",
      buttonLabel:      "Take survey",
      buttonParamsJSON: JSON.stringify(flowParams)
    })
  });
  ```

  ```python Python theme={null}
  import os, json, requests

  flow_params = {
      "flow_message_version": "3",
      "flow_token":           "my-token-123",
      "flow_id":              "123456789012345",
      "flow_cta":             "Take survey",
      "flow_action":          "navigate",
      "flow_action_payload":  {"screen": "WELCOME"}
  }

  requests.post(
      f"https://ryzeapi.cloud/api/message/form/{os.environ['Instance_Name']}",
      headers={
          "token":        os.environ["Token_Instance"],
          "Content-Type": "application/json"
      },
      json={
          "number":           "5511999999999",
          "message":          "Take the quick survey and earn 10% off",
          "buttonLabel":      "Take survey",
          "buttonParamsJSON": json.dumps(flow_params)
      }
  )
  ```

  ```go Go theme={null}
  package main

  import (
      "net/http"
      "os"
      "strings"
  )

  func main() {
      body := strings.NewReader(`{
          "number":           "5511999999999",
          "message":          "Take the quick survey and earn 10% off",
          "buttonLabel":      "Take survey",
          "buttonParamsJSON": "{\"flow_message_version\":\"3\",\"flow_token\":\"my-token-123\",\"flow_id\":\"123456789012345\",\"flow_cta\":\"Take survey\",\"flow_action\":\"navigate\",\"flow_action_payload\":{\"screen\":\"WELCOME\"}}"
      }`)
      req, _ := http.NewRequest("POST", "https://ryzeapi.cloud/api/message/form/"+os.Getenv("Instance_Name"), body)
      req.Header.Set("token", os.Getenv("Token_Instance"))
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
  }
  ```
</CodeGroup>

## Respuesta exitosa

El `messageType` retornado es `interactive` (un formulario es una variación de mensaje interactivo Native Flow), y `content` refleja el `message` que enviaste. Guarda el `messageId` (y el `flowToken`, generado automáticamente cuando no envías uno) para correlacionar con la respuesta del flujo en el webhook.

```json 200 OK theme={null}
{
  "success": true,
  "message": "Form message sent successfully",
  "status":  "sent",
  "data": {
    "messageId":   "3EB08FCF27E532F1B0F5",
    "direction":   "sent",
    "messageType": "interactive",
    "content":     "Lock in your spot in the course with 50% off!",
    "source":      "api",
    "timestamp":   "2026-04-30T14:30:00Z",
    "chat": {
      "jid":     "5511999999999@s.whatsapp.net",
      "isGroup": false
    },
    "sender": {
      "jid":      "5511777777777@s.whatsapp.net",
      "instance": "my-instance"
    }
  }
}
```

<Note>
  Cuando el usuario llena y envía el formulario, WhatsApp envía un mensaje `interactive_response` cargando el `flow_token` (el UUID que proporcionaste o el autogenerado) y el JSON con las respuestas. Captúralo vía webhook/websocket para correlacionar con el envío original.
</Note>

## Parámetros de ruta

<ParamField path="instance" type="string" required>
  Nombre de la instancia (p. ej., `$Instance_Name`).
</ParamField>

## Cabeceras

<ParamField header="token" type="string" required>
  `TokenAccount` o `TokenInstance`.
</ParamField>

<ParamField header="Content-Type" type="string" required>
  `application/json`
</ParamField>

## Cuerpo de la solicitud

<ParamField body="number" type="string" required>
  Destino: teléfono (`5511999999999`) o JID (`@s.whatsapp.net`, `@lid`, `@g.us`).
</ParamField>

<ParamField body="message" type="string" required>
  Texto mostrado en la burbuja del mensaje, sobre el botón que abre el Flow.
</ParamField>

<ParamField body="formType" type="string" default="registration_offer">
  Tipo de formulario preconfigurado: `"contact_details"` (Datos del cliente) o `"registration_offer"` (Oferta de registro). Ignorado si se proporciona `buttonParamsJSON`.
</ParamField>

<ParamField body="buttonLabel" type="string" default="Add information">
  Texto mostrado en el botón que abre el Flow (`flow_cta`).
</ParamField>

<ParamField body="flowToken" type="string">
  Token para correlacionar la respuesta del formulario con el envío. Cuando se omite, el servidor genera un **UUID** automáticamente. Puedes usar este token para vincularlo con un lead/oportunidad en tu CRM.
</ParamField>

<ParamField body="flowId" type="string">
  ID del Flow en WhatsApp Business. Defaults por `formType`:

  * `contact_details` → `1889354358373616`
  * `registration_offer` → `892701196712475`

  Sobrescríbelo solo si pretendes usar un Flow personalizado por nombre (sin usar `buttonParamsJSON`).
</ParamField>

<ParamField body="flowMessageVersion" type="string" default="4">
  Valor `flow_message_version` enviado a WhatsApp.
</ParamField>

<ParamField body="messageVersion" type="int" default="3">
  `message_version` del payload Native Flow.
</ParamField>

<ParamField body="buttonParamsJSON" type="string">
  **Escape hatch** para flujos completamente personalizados. Cuando se proporciona, el servidor envía este JSON literal como `params` del botón Native Flow e **ignora** `formType`, `flowId`, `flowToken`, `flowMessageVersion`, `messageVersion`, todas las flags `*Visible`, `offerName` y `offerDescription`. Útil para integrar con flujos que has creado en WhatsApp Business Manager con schemas personalizados.
</ParamField>

<ParamField body="fullNameVisible" type="boolean" default="true">
  Muestra el campo "Full name" en el formulario. Ignorado si se proporciona `buttonParamsJSON`.
</ParamField>

<ParamField body="phoneNumberVisible" type="boolean" default="true">
  Muestra el campo "Phone number".
</ParamField>

<ParamField body="emailVisible" type="boolean" default="true">
  Muestra el campo "Email".
</ParamField>

<ParamField body="cpfOrCnpjVisible" type="boolean" default="true">
  Muestra el campo "CPF/CNPJ".
</ParamField>

<ParamField body="deliveryAddressVisible" type="boolean" default="true">
  Muestra el campo "Delivery address".
</ParamField>

<ParamField body="offerName" type="string">
  Título de la oferta mostrado dentro del Flow. Usado cuando `formType=registration_offer`.
</ParamField>

<ParamField body="offerDescription" type="string">
  Descripción de la oferta mostrada dentro del Flow. Usada cuando `formType=registration_offer`.
</ParamField>

<ParamField body="delay" type="int" default="0">
  Tiempo en **segundos** a esperar antes de enviar. Durante el intervalo, el servidor muestra el indicador "escribiendo..." al destinatario.
</ParamField>

<ParamField body="replyTo" type="string">
  ID del mensaje a responder. El mensaje original debe pertenecer a la misma instancia y estar guardado en la base de datos.
</ParamField>

<ParamField body="replyPrivate" type="boolean" default="false">
  Cuando es `true` **y** `replyTo` apunta a un mensaje originado en un grupo, la respuesta se redirige al **chat privado** del autor original.
</ParamField>

<ParamField body="source" type="string" default="api">
  Identificador de origen para trazabilidad (p. ej., `crm`, `landing-sales`, `n8n`).
</ParamField>

## Notas

<Note>
  * Los Flows `contact_details` y `registration_offer` son plantillas preaprobadas de WhatsApp listas para usar. Si necesitas un formulario con campos específicos (preguntas personalizadas, lógica de pantalla), usa **`buttonParamsJSON`** con tu propio Flow.
  * El **`flowToken`** es tu identificador para vincular la respuesta del formulario con el registro originador (lead, pedido, etc.). Si no envías uno, guarda el UUID autogenerado para poder correlacionar después.
  * Cuando se envía `buttonParamsJSON`, todos los demás campos relacionados con el Flow se ignoran, tomas el control total del payload, incluyendo `flow_id`, `flow_action`, `flow_action_payload` y `flow_message_version`.
  * Native Flow solo funciona en chats 1-a-1 (`@s.whatsapp.net`) y grupos (`@g.us`); las newsletters (`@newsletter`) no son compatibles con WhatsApp.
  * La respuesta del formulario llega como un evento `interactive_response`, no es un mensaje de texto regular, así que maneja el webhook en consecuencia.
</Note>

## Errores

| HTTP | Status interno                    | Mensaje                                             |
| ---- | --------------------------------- | --------------------------------------------------- |
| 400  | ,                                 | `Instance name is required`                         |
| 400  | ,                                 | `Invalid request: <detail>`                         |
| 400  | ,                                 | `Number is required`                                |
| 400  | ,                                 | `Message is required`                               |
| 400  | `invalid_number`                  | `Invalid phone number format: <detail>`             |
| 400  | `reply_message_not_found`         | `Original message not found (ID: ...)`              |
| 400  | `reply_message_instance_mismatch` | `Original message does not belong to this instance` |
| 400  | `private_reply_failed`            | (motivo del fallo de redirección privada)           |
| 404  | ,                                 | `Instance not found`                                |
| 500  | `send_failed`                     | `Failed to send message: <reason>`                  |
| 503  | `disconnected`                    | `Instance is not connected to WhatsApp`             |

Envoltorio de error:

```json theme={null}
{
  "success": false,
  "error": { "message": "Message is required" }
}
```
