formbasedocs
Ir a la appApp

Solicitudes

Crear una solicitud

Dos llamadas a la API: pregunta al formulario qué se le puede indicar, y luego asígnalo a una persona con los valores que ya conoces.


Empieza en la hoja de compartir

La pestaña Solicitudes en su pestaña curl, mostrando el id del formulario y una llamada requests.create ya lista
La pestaña curl: el id del formulario y una llamada ya rellena con las claves de campo de este formulario.

Abre tu formulario publicado, haz clic en Compartir y elige la pestaña Solicitudes. La tarjeta que aparece ahí te da todo lo que necesitas para hacer la primera llamada:

  • El id del formulario, con un botón de copiar.

  • Un fragmento de curl y un prompt de MCP, ambos construidos con las claves de campo reales de tu formulario — así el ejemplo ya se dirige a los campos que este formulario tiene de verdad.

  • Una pestaña Manual que crea una solicitud a mano, y Pruébalo tú mismo, que convierte lo que rellenaste ahí en una solicitud en modo de prueba y te da su enlace.

  • Un enlace directo a la página de Solicitudes, filtrado a este formulario.

Paso 1 — Descubre los campos

fields.list devuelve todos los campos de la versión publicada actual del formulario, con la clave para dirigirte a cada uno, la forma de valor que espera y a qué grupo pertenece.

fields.list
bash
curl -X POST https://api.formbase.so/api/v1 \
  -H "Authorization: Bearer $FORMBASE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"method":"fields.list","params":{"formId":"j57..."}}'
Respuesta
json
{
  "ok": true,
  "data": {
    "published": true,
    "items": [
      { "key": "company_name", "type": "text", "title": "Company", "required": true, "prefillable": true },
      {
        "key": "company_size", "type": "select", "title": "Company size", "required": false, "prefillable": true,
        "options": [
          { "key": "1_50", "label": "1–50" },
          { "key": "51_200", "label": "51–200" }
        ]
      },
      { "key": "case_id", "type": "hidden", "title": "Case", "required": false, "prefillable": false, "context": true },
      { "key": "total", "type": "number", "title": "Total", "required": false, "prefillable": false, "calculated": true },
      { "key": "signature", "type": "signature", "title": "Sign here", "required": true, "prefillable": false }
    ],
    "hasMore": false
  }
}
  • context: true marca un campo oculto. Su valor va en context, nunca en prefill; la clave de un campo oculto en prefill se rechaza con UNKNOWN_FIELD_KEY.

  • calculated: true marca un campo calculado. El formulario calcula su valor, así que nadie puede enviarlo; lo lees de vuelta bajo su clave en answers.

  • prefillable: false marca un campo para el que nadie puede suministrar un valor: subida de archivo, firma, pago, reserva de cita y bloques de Documentos. Las preguntas las rellena el propio destinatario. Los campos ocultos y los campos calculados también muestran prefillable: false: los campos ocultos reciben su valor mediante context, y los campos calculados no reciben nada.

  • options lista las opciones de una pregunta de elección. Envía la clave de la opción, no su etiqueta; la etiqueta solo está ahí para que puedas relacionar la opción que conoces con su clave. Una matriz lista sus rows y columns de la misma forma.

  • Los grupos repetibles vuelven como una única entrada con type: “group”, repeating: true y una lista de members.

Paso 2 — Crea la solicitud

requests.create
bash
curl -X POST https://api.formbase.so/api/v1 \
  -H "Authorization: Bearer $FORMBASE_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"method":"requests.create","params":{
        "formId":"j57...",
        "recipient":{"email":"ada@acme.com","name":"Ada"},
        "context":{"case_id":"CASE-9"},
        "prefill":{"company_name":"Acme","company_size":"51_200"},
        "readonly":["company_name"],
        "delivery":"email",
        "externalId":"run-42",
        "callbackUrl":"https://automation.example/webhook/resume-abc",
        "idempotencyKey":"run-42"}}'
Respuesta
json
{
  "ok": true,
  "data": {
    "id": "kd7...",
    "status": "pending",
    "url": "https://form.formbase.so/r/rq_...",
    "deliveryStatus": "queued",
    "expiresAt": 1794787200000,
    "createdAt": 1789379200000,
    "externalId": "run-42",
    "deduplicated": false
  }
}

deliveryStatus es “queued” cuando formbase envía la invitación por email, y “not_requested” cuando entregas el enlace tú mismo.

Precompletado, campos bloqueados y contexto

Hay tres cosas distintas que se pueden adjuntar a una solicitud, y confundirlas es el error inicial más habitual.

Va enEl destinatario…Vuelve en el callback
PrecompletadoprefillLo ve y puede cambiarloSí, como una respuesta
Campo bloqueadoprefill + readonlyLo ve, no puede cambiarloSí, como una respuesta
ContextocontextNo puede cambiarlo; lo ve solo donde lo mencionesSí, en el bloque de la solicitud y como una respuesta
MetadatosmetadataNunca lo ve, y el formulario tampocoSí, en el bloque de la solicitud

Precompletado

Respuestas iniciales para las preguntas visibles, para que el destinatario revise y corrija en lugar de escribir desde cero. Todo lo que ya sepas sobre esa persona pertenece aquí — el nombre de la empresa desde tu CRM, el importe de la factura, las respuestas del año pasado.

Campos bloqueados

Incluye una clave precompletada en readonly y el destinatario verá el valor pero no podrá cambiarlo. Úsalo para los datos que está confirmando en lugar de proporcionando — el número de contrato, el precio acordado. El bloqueo es por solicitud: el formulario en sí no se toca, y el mismo campo queda libremente editable en la siguiente solicitud.

Toda clave bloqueada también debe estar precompletada, y un campo bloqueado obligatorio debe precompletarse con algo no vacío — de lo contrario el destinatario se enfrentaría a un formulario que nunca podría enviar, y formbase rechaza la llamada en lugar de crear esa trampa.

Contexto

Valores de confianza para los campos ocultos del formulario — un número de caso, un id de ejecución de flujo, un importe. El contexto alimenta variables, lógica condicional, campos calculados y copys de correo, vuelve sin cambios en el callback, y el destinatario no puede alterarlo. Esa última parte es la diferencia frente a sembrar un campo oculto mediante una URL en un enlace público, donde cualquiera puede editar la cadena de consulta; los enlaces de solicitud ignoran por completo los parámetros de consulta de la URL. Los valores de contexto deben ser una cadena, un número o un booleano.

El contexto no es de formato libre: cada clave debe ser un campo oculto en la versión publicada del formulario, y cualquier otra clave se rechaza con UNKNOWN_FIELD_KEY. La contabilidad interna que no tiene un campo oculto, como un id de ejecución, pertenece a metadata.

Los campos ocultos no se muestran en el formulario, pero un valor de contexto no es secreto para el destinatario. Lo ve donde sea que el formulario o la invitación lo muestren: una mención en el contenido del formulario o en el texto del correo, o una pregunta visible que usa ese campo oculto como su valor predeterminado. En ese último caso, el destinatario ve el valor de contexto precompletado en esa pregunta y puede editar la respuesta. El valor de contexto en sí permanece sin cambios. Un prefill para la clave propia de esa pregunta tiene prioridad sobre el valor predeterminado.

Metadatos

Tu propia contabilidad interna — un id de ejecución, un id de registro del CRM. Nunca llega al formulario, así que no se puede insertar en el texto ni leer por la lógica; solo viaja junto a la solicitud y vuelve en cada callback y lectura de estado.

Formas de valor

Envía los valores en la forma que pida el type de fields.list. Una forma incorrecta vuelve como un error de validación que nombra la clave, el tipo esperado y — para preguntas de elección — los valores que se habrían aceptado.

TipoEnvía
text, email, phone, url, textareaUna cadena de texto
number, rating, scaleUn número
switchtrue o false
date"2026-03-04"
time"09:30" o "09:30:00"
radio, selectLa clave de la opción, no su etiqueta
checkbox, ranking, picture-choiceUn array de claves de opción
matrixUn objeto de clave de fila a clave de columna: { "row_key": "column_key" }
group (repetible)Un array de instancias, como máximo 100: [{ "member_key": value }, …]
file, signature, payment, schedule-appointmentNada — los suministra el destinatario
cualquier campo con calculated: trueNada — el formulario lo calcula
documentsNada en el precompletado — usa la opción documents de abajo

Documentos

Un bloque de documentos le entrega archivos al respondente. Sus documentos creados por ti son los mismos para todos y siempre permanecen; una solicitud añade archivos para su único destinatario debajo de ellos — el propio contrato de alquiler del cliente, una copia del DNI para revisar. Los bytes nunca viajan por la propia llamada a la API: primero se sube, y luego se referencia.

  1. 1

    Reserva la subida

    Llama a documents.create con formId, name (1–200 caracteres), contentType (PDF o imagen), el tamaño exacto en bytes y, opcionalmente, un sha256 del archivo (64 caracteres hexadecimales). Recibes de vuelta un id y una uploadUrl válida durante una hora.

  2. 2

    Sube los bytes

    Haz un PUT del archivo a uploadUrl con el mismo Content-Type. Todavía no se verifica nada.

  3. 3

    Referéncialo en la solicitud

    Pasa documents: [{ documentId, name? }] en requests.create. formbase comprueba el objeto subido (tamaño, firma del archivo, sha256 si enviaste uno) antes de crear la solicitud, y el destinatario ve el archivo en el bloque.

requests.create → documents
json
"documents": [
  { "documentId": "kn7...", "name": "Your lease contract" },
  { "documentId": "kn8..." }
]

name sustituye el nombre visible guardado con la subida. Si el formulario tiene más de un bloque de documentos, indica el destino con field, la clave de campo del bloque (fields.list la muestra, junto con los documentos de autor que ya reciben todos los encuestados). Los archivos aparecen debajo de esos documentos de autor: una solicitud añade archivos, nunca sustituye ninguno. Una misma subida se puede referenciar desde tantas solicitudes como quieras — una lista de precios subida una vez sirve a quinientas solicitudes.

Límites: solo PDF e imágenes, 25 MB por documento, 100 MB por solicitud (DOCUMENTS_TOO_LARGE), y como máximo 20 documentos por bloque contando los documentos de autor (DOCUMENTS_TOO_MANY). Los archivos cuentan contra el almacenamiento de tu espacio de trabajo y se liberan en cuanto las solicitudes que los referencian salen de la ventana de retención del formulario. La respuesta registra la lista que vio el destinatario bajo la clave de campo del bloque, así que el callback te dice exactamente qué archivos recibió esa persona.

El resto de las opciones

OpciónQué hace
languageEl idioma en el que se abre el formulario y en el que está redactada la invitación; uno de los idiomas publicados del formulario. Si se omite, usa el idioma predeterminado del formulario. El destinatario puede seguir cambiando de idioma, igual que en un enlace público.
delivery"email" envía la invitación por ti y requiere un email de destinatario y un plan Pro o Business, o una de las 10 invitaciones gratis de una cuenta Free; "none" (el valor predeterminado) significa que tú entregas el enlace.
remindersSustituye el calendario de recordatorios del formulario solo para esta solicitud con hasta cinco retrasos de inactividad como ["2d", "12h", "30m"], o pasa una lista vacía para desactivar los recordatorios en ella. Un calendario personalizado requiere un email de destinatario y un plan Pro o Business; sin un email de destinatario, el calendario propio del formulario simplemente no se ejecuta.
expiresAtCuándo deja de funcionar el enlace, como marca de tiempo Unix en milisegundos. Por defecto 30 días; 365 días es el máximo.
externalIdTu propio id para esta solicitud. Puedes filtrar por él más tarde.
idempotencyKeyHace que una ejecución reintentada reutilice la solicitud en lugar de crear una segunda.
callbackUrlAdónde envía formbase el callback por POST cuando la solicitud termina. Solo HTTPS.
domainIdGenera el enlace en uno de tus dominios personalizados, en lugar del dominio en el que el formulario ya está publicado.
testUna prueba en seco: no se envía nada, el callback dice test, y la respuesta no cuenta en ningún sitio. Ver más abajo.

Modo de prueba

Pasa test: true para poner a prueba todo el circuito antes de una ejecución real. Una solicitud de prueba es real en todo lo que importa para la integración: el enlace se abre y se puede completar, el callback se dispara como siempre, y requests.get devuelve las respuestas. Lo que nunca hace es llegar a nadie ni a nada que después tengas que limpiar:

  • No se envía ninguna invitación ni ningún recordatorio, sea lo que sea lo que diga delivery. Enviar recordatorio se rechaza en ella, y no gasta nada de tu asignación mensual.

  • El callback lleva “test”: true, para que tu flujo de trabajo pueda bifurcar o ignorar el evento.

  • La respuesta se almacena pero no cuenta: ni contra tu cuota mensual (una prueba se completa incluso con la cuota agotada), y nunca aparece en los recuentos de respuestas del formulario, en la pestaña de respuestas, en las exportaciones, ni en tus integraciones. Nadie recibe notificación.

  • La solicitud queda oculta en la página de Solicitudes detrás de Mostrar solicitudes de prueba, se deja fuera del embudo de solicitudes en Analytics, y se deja fuera de requests.list salvo que pases includeTest: true.

  • El enlace se cierra en un plazo de 24 horas, incluso cuando expiresAt pide más; el expiresAt de la respuesta indica cuándo. En Free, un workspace puede crear 10 solicitudes de prueba al día. La siguiente falla con RATE_LIMITED y el motivo TEST_REQUEST_LIMIT_REACHED, y retryAfterMs indica cuándo puedes volver a intentarlo. Pro y Business no tienen límite diario.

Pruébalo tú mismo en la hoja de compartir es este modo con un solo clic: toma el borrador de la pestaña Manual, deja fuera el destinatario y la entrega, y te da el enlace para abrirlo tú mismo.

Lo que cuesta una solicitud

Todo plan tiene una asignación mensual compartida por ambos canales: un envío por enlace público gasta una unidad, y también lo hace cada solicitud que creas — la responda el destinatario, la ignore, o la cancele tú. La respuesta que recopila una solicitud ya está pagada y no cuenta en ningún sitio. Free incluye 1.000 unidades al mes, Pro y Business 50.000; el contador se reinicia el día 1 de cada mes, UTC. Al llegar al tope, requests.create falla con UPGRADE_REQUIRED y motivo MONTHLY_ALLOWANCE_REACHED; las solicitudes que ya habías creado siguen pudiéndose responder.

En Free, una solicitud creada con “delivery”: “email” también gasta una de las 10 invitaciones gratis de la cuenta. No se reinician nunca; cuando se agotan, el envío por correo falla con UPGRADE_REQUIRED y motivo FREE_INVITATIONS_USED.

Idempotencia

Pasa la misma idempotencyKey con el mismo cuerpo y recibes la solicitud original de vuelta, con deduplicated: true y el enlace original — sin segunda solicitud, sin segundo correo. Reutiliza la clave con un cuerpo distinto y formbase lo rechaza con IDEMPOTENCY_CONFLICT en lugar de adivinar cuál querías. Las claves están limitadas al espacio de trabajo y se respetan durante 30 días; pasado ese tiempo, la misma clave inicia una solicitud nueva.

En una herramienta de flujos de trabajo, el id de ejecución es la clave natural: una ejecución reintentada tras un fallo de red recupera la solicitud que ya había creado.

Límite de frecuencia

requests.create y documents.create comparten un presupuesto de 60 llamadas por minuto, contado por token de API (o por usuario, para una llamada hecha sin uno). Un backlog que estás vaciando debería ir a su propio ritmo; una ráfaga que supere el presupuesto se rechaza y se puede reintentar.

Dominios personalizados

Si el formulario ya está publicado en uno de tus dominios personalizados, los enlaces de solicitud se generan ahí automáticamente — https://forms.tuempresa.com/r/rq_…. Especifica domainId explícitamente cuando el formulario esté publicado en más de uno. El dominio debe pertenecer al mismo espacio de trabajo que el formulario.

Lo que ve el destinatario

Exactamente el formulario que tú creaste — mismo tema, mismo logo, mismo idioma — con sus valores en su sitio, los campos bloqueados en solo lectura, y sin captcha que resolver. Cuando lo envía, ve tu página de agradecimiento. Si vuelve al enlace después, ve la página de resultado en lugar de un formulario en blanco.

No hay ningún mensaje de tu automatización en la página. Todo lo que el destinatario necesite saber debe ir en el propio formulario, donde puedes personalizarlo mencionando un valor de contexto o un campo precompletado.

Un agente de IA ejecuta los mismos dos pasos que fields_list y request_create, con las mismas opciones — documents y domainId incluidos. Consulta Solicitudes en el servidor MCP.