> For the complete documentation index, see [llms.txt](https://docs.dapta.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.dapta.ai/dapta-docs-es/dforms/developers/public-api.md).

# API pública y límites de tasa

Los endpoints sin autenticar que hay detrás de cada formulario público de Dapta: obtener un formulario publicado, subir un archivo, registrar una respuesta, un evento de embudo o una reserva. Más los

Cada formulario publicado de Dapta se renderiza desde una API pequeña, abierta y sin autenticar. Los mismos endpoints que llaman los navegadores de quienes responden están disponibles para ti, lo que significa que puedes obtener la estructura de un formulario publicado o enviar una respuesta desde tu propio código.

En el producto alojado, la API vive en **`https://api.dforms.ai`**. En un despliegue autoalojado es tu propio origen de API.

> **💡 Nota:** Estos endpoints no piden llave, y son todo lo que usa un formulario público. Para trabajar con el resto de tu workspace desde código (crear, editar y publicar formularios, leer respuestas y analíticas), crea una llave de API: mira [Llaves de API y MCP](/dapta-docs-es/dforms/developers/api-keys.md). Para enviar los datos a medida que llegan, usa [Webhooks](/dapta-docs-es/dforms/connect/webhooks.md) o la sincronización con [HubSpot](/dapta-docs-es/dforms/connect/hubspot.md).

***

## Los endpoints públicos

Todos viven bajo `/v1/public`. `{accountCode}` es el código de cuenta de tu workspace y `{slug}` es el slug del formulario, los mismos dos valores que puedes leer del enlace del formulario, `app.dforms.ai/{accountCode}/f/{slug}`. Por ejemplo, el enlace `https://app.dforms.ai/acme/f/lead-qualification-quiz` da `acme` y `lead-qualification-quiz`.

| Método y ruta                                            | Qué hace                                                                                                                                                                                                                                                                                                                                   | Éxito                                                              |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------ |
| `GET /v1/public/forms/{accountCode}/{slug}`              | La configuración publicada de un formulario: su nombre y sus pasos, exactamente como los renderiza la página pública.                                                                                                                                                                                                                      | `200`, o `404` cuando no hay formulario publicado en esa dirección |
| `POST /v1/public/forms/{accountCode}/{slug}/uploads`     | Autoriza un archivo para una pregunta **Subir archivo**. Cuerpo: `sessionId`, `stepKey`, `name`, `size` y `mime`. La respuesta dice dónde subir el archivo y qué enviar como respuesta.                                                                                                                                                    | `200`, o `404` cuando el despliegue no guarda archivos             |
| `POST /v1/public/forms/{accountCode}/{slug}/submissions` | Registra una respuesta. Cuerpo: `sessionId`, `data` (un objeto de respuestas indexado por clave de campo), un `partial: true` opcional para un guardado parcial, y los campos opcionales que envía el formulario público: `locale`, `visit` (la página donde se respondió) y, en un formulario con protección contra spam, `captchaToken`. | `201`                                                              |
| `POST /v1/public/forms/{accountCode}/{slug}/events`      | Registra un evento de embudo. Cuerpo: `sessionId`, `type` (uno de `view`, `start`, `step_view`, `step_complete`, `partial_submit`, `submit`) y opcionalmente `stepIndex` y `stepKey`. Esto es lo que llena el gráfico de abandono de **Analíticas**.                                                                                       | `202`                                                              |
| `POST /v1/public/forms/{accountCode}/{slug}/booking`     | Registra que alguien reservó una reunión, desde un paso de Agendador o desde el calendario insertado de un resultado, para que la reserva quede atada a la misma sesión.                                                                                                                                                                   | `202`                                                              |
| `GET /v1/public/profiles/{accountCode}/{handle}`         | La [página pública](/dapta-docs-es/dforms/share/public-page.md) de un miembro, o `404` cuando no la ha activado.                                                                                                                                                                                                                           | `200`                                                              |
| `GET /health`                                            | Comprobación de vida más una prueba de base de datos. Útil en un despliegue autoalojado.                                                                                                                                                                                                                                                   | `200`                                                              |

Algunos comportamientos que conviene conocer antes de enviar nada:

* **Una respuesta por sesión.** Todas las escrituras se indexan por tu `sessionId`. Un guardado parcial se convierte en el sitio en la respuesta completa cuando la misma sesión termina, así que nunca te quedan dos filas de una persona.
* **El puntaje siempre se recalcula en el servidor** a partir de la configuración publicada. Un cliente no puede afirmar su propio puntaje ni su resultado.
* **Se revisa el cuerpo, no cada regla del formulario.** La API revisa la forma del cuerpo, el largo máximo definido en una pregunta de texto largo, y cada archivo subido. No vuelve a revisar las demás reglas que el formulario aplica en el navegador, como las preguntas obligatorias.
* **Los slugs viejos siguen funcionando.** Cuando se cambia el enlace de un formulario, cada endpoint sigue respondiendo en el slug viejo, y el `GET` devuelve el `slug` actual.
* **Un formulario con protección contra spam** necesita un `captchaToken` válido en su respuesta completa, que solo puede producir el formulario público. Mira [Protección contra spam (captcha)](/dapta-docs-es/dforms/connect/spam-protection.md).

### Errores

Todo cuerpo de error tiene la misma forma: `{"error":"CODE","message":"..."}`. El `message` está en inglés; apóyate en el código de `error`.

| Estado | `error`               | Qué significa                                                                                                                                                    |
| ------ | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `BAD_REQUEST`         | El cuerpo está mal formado. El mensaje nombra el primer problema.                                                                                                |
| `400`  | `ANSWER_TOO_LONG`     | Una respuesta es más larga de lo que permite su pregunta de texto largo.                                                                                         |
| `403`  | `CAPTCHA_REQUIRED`    | Una respuesta completa en un formulario con protección contra spam llegó sin verificación. No se guarda nada.                                                    |
| `403`  | `CAPTCHA_FAILED`      | La verificación de la protección contra spam no pasó. No se guarda nada.                                                                                         |
| `404`  | `NOT_FOUND`           | No hay un formulario publicado en esa dirección.                                                                                                                 |
| `429`  | `RATE_LIMITED`        | Demasiadas peticiones. Mira [Límites de tasa](#limites-de-tasa).                                                                                                 |
| `503`  | `CAPTCHA_UNAVAILABLE` | La verificación de la protección contra spam no se pudo completar. Las respuestas quedaron guardadas como respuesta parcial, que no se entrega; vuelve a enviar. |

***

## Obtener un formulario publicado

Este es el endpoint que más gente quiere. Un `GET` normal, sin cabeceras:

```bash
curl https://api.dforms.ai/v1/public/forms/acme/lead-qualification-quiz
```

La respuesta real de ese formulario, recortada a dos de las cuatro opciones de la primera pregunta:

```json
{
  "slug": "lead-qualification-quiz",
  "name": "Lead qualification quiz",
  "config": {
    "steps": [
      {
        "key": "multiple_choice_1",
        "type": "multiple_choice",
        "question": "How big is your team?",
        "helper": "Pick the option that fits best.",
        "required": true,
        "selectionMode": "single",
        "flowGroup": "qualification",
        "options": [
          { "label": "Just me", "value": "option_1", "points": 0 },
          { "label": "2 to 10 people", "value": "option_2", "points": 0 }
        ]
      },
      {
        "key": "email_2",
        "type": "email",
        "question": "What's your work email?",
        "required": true,
        "flowGroup": "lead_capture"
      },
      {
        "key": "name_3",
        "type": "name",
        "question": "And your name?",
        "helper": "Both fields, please. Thanks!",
        "required": true,
        "flowGroup": "lead_capture"
      }
    ],
    "version": 1
  }
}
```

Fíjate en lo que vuelve: solo la versión **publicada**. Las ediciones de borrador sin publicar nunca se sirven aquí, y por eso la respuesta es segura de cachear y segura de leer desde un navegador. La `key` de cada paso es la clave de campo que volverás a ver en el payload de un webhook y en una exportación a CSV.

Según el formulario y el despliegue, la respuesta puede traer algo más:

* `uploadMaxMb`, el archivo más grande que acepta el despliegue, cuando la subida de archivos está activa.
* `captcha`, la parte pública de la verificación de la protección contra spam, cuando el formulario la tiene activa.
* `screenGroup` en los pasos que comparten una pantalla de Diapositivas. Mira [Varias preguntas en una pantalla](/dapta-docs-es/dforms/builder/screens.md).

Los ajustes de integración nunca viajan aquí: las URLs de webhook, los secretos de firma, los mapeos del CRM y el interruptor de la protección contra spam se quitan de la configuración publicada.

***

## El documento OpenAPI

Cada despliegue se describe a sí mismo. Dos direcciones, las dos abiertas:

* **`https://api.dforms.ai/openapi.json`**: el documento OpenAPI 3.1, legible por máquina. Apunta ahí tu generador de clientes o tu cliente HTTP.
* **`https://api.dforms.ai/docs`**: el mismo documento renderizado como página, sin dependencias externas.

<figure><img src="https://365551146-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FMUYDYzjmxpzlYaw0gcaZ%2Fuploads%2Fgit-blob-72263921c12daf91e0a2c252ff56000b93bba9ab%2Fforms-public-api-01-openapi-docs.png?alt=media" alt="La página /docs mostrando el documento OpenAPI 3.1 con un enlace a /openapi.json"><figcaption><p>La página <code>/docs</code> imprime el documento OpenAPI y enlaza al JSON crudo.</p></figcaption></figure>

El documento cubre tanto los endpoints públicos de arriba como los del panel, que están marcados como que necesitan sesión. En un despliegue autoalojado, las mismas dos direcciones están en tu propio origen de API.

***

## Límites de tasa

La superficie pública es la única parte de dForms que puede alcanzar un cliente anónimo, así que se limita por dirección IP del cliente con un token bucket. El servicio alojado usa límites más altos que los valores por defecto de abajo, que son los que recibe un despliegue autoalojado:

| Valor por defecto | Cantidad                                                      |
| ----------------- | ------------------------------------------------------------- |
| Ráfaga            | **60 peticiones**                                             |
| Recarga           | **1 petición por segundo**                                    |
| Alcance           | Por dirección IP del cliente, en todos los endpoints públicos |

Con estos valores puedes lanzar 60 peticiones seguidas, tras lo cual obtienes aproximadamente una por segundo, y el margen se recupera hasta 60 mientras estás sin actividad. Eso está muy por encima de lo que necesita una persona real rellenando un formulario, y lo bastante bajo como para que hacer scraping y spam de envíos salga caro.

Cuando te pasas, la API contesta **`429 Too Many Requests`** con una cabecera `Retry-After` en segundos enteros:

```
HTTP/1.1 429 Too Many Requests
Retry-After: 1
Content-Type: application/json

{"error":"RATE_LIMITED","message":"Too many requests. Please slow down."}
```

Espera los segundos de `Retry-After` y reintenta. No reintentes en un bucle apretado: cada petición rechazada no recarga nada.

> **⚠️ Nota:** En un despliegue autoalojado estos números son tuyos: `RATE_LIMIT_CAPACITY`, `RATE_LIMIT_REFILL_PER_SEC` y `RATE_LIMIT_ENABLED`. Si tu API está detrás de un proxy, ajusta también `TRUST_PROXY_HOPS`, o cada petición parecerá venir del proxy y tus visitantes compartirán un solo cubo. Mira [Flags de funcionalidad y entorno](/dapta-docs-es/dforms/developers/environment-flags.md).

***

## Llamar a la API desde un navegador

Las peticiones desde una página de otro dominio están sujetas a las reglas de origen habituales del navegador, y la API alojada permite los orígenes desde los que sirve formularios. Para llamarla desde tu propio sitio, llámala desde tu servidor. En un despliegue autoalojado puedes listar dominios extra en `CORS_ORIGINS`.

El [fragmento para insertar](/dapta-docs-es/dforms/share/embed.md) no se ve afectado: el formulario corre en un marco servido por dForms, y llega a la API desde ahí.

***

## Qué sigue

* [Webhooks](/dapta-docs-es/dforms/connect/webhooks.md): que te empujen cada respuesta en lugar de andar consultando.
* [Referencia del payload y las cabeceras](/dapta-docs-es/dforms/connect/webhooks/payload-reference.md): qué contiene una entrega.
* [Autoalojamiento](/dapta-docs-es/dforms/developers/self-hosting.md): ejecuta la API tú mismo.
* [Flags de funcionalidad y entorno](/dapta-docs-es/dforms/developers/environment-flags.md): los ajustes que cambian los límites de arriba.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.dapta.ai/dapta-docs-es/dforms/developers/public-api.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
