# Formulario de inscripción de voluntarios con turnos, para eventos y festivales

> Un formulario de voluntarios donde se eligen turnos, con un teléfono para el día, y un script que muestra qué turnos faltan cubrir y arma el cuadrante.

Publicado el 2026-10-25 · https://ikiform.com/es/guides/event-registration/volunteer-signup

Deja que los voluntarios marquen en un solo formulario todos los turnos que pueden hacer, con un número de teléfono, una talla de camiseta y un contacto de emergencia, y envíalo a Ikiform. Ikiform guarda cada inscripción y avisa por email a los coordinadores, pero no llena ni cierra turnos, así que ejecuta un script corto que cuente los voluntarios por turno frente a los que necesitas y escriba un cuadrante. Cubre los turnos que faltan pidiéndoselo directamente a los voluntarios que sobran.

## Qué tienen de distinto los voluntarios

Un RSVP hace una pregunta: ¿vienes? Una inscripción de voluntarios pregunta cuándo, y la respuesta solo importa frente a lo que necesita cada turno. El montaje a las 7:00 necesita seis personas y tiene dos; la tarde soleada necesita diez y tiene diecinueve. El trabajo no es contar cabezas, es cubrir turnos.

- **Disponibilidad, no una única elección.** La gente puede hacer varios turnos. Pide todos los turnos que podrían hacer y asigna a partir de ahí, en lugar de obligarles a adivinar qué hace falta.
- **Contacto el día del evento.** El email sirve para organizar. El día del evento necesitas un número de móvil y un contacto de emergencia de cada voluntario.
- **Cosas que pedir.** Camisetas, acreditaciones y comidas, por talla y cantidad.
- **Edad.** Muchos eventos exigen que cada voluntario sea mayor de edad o tenga la autorización de un tutor. Pregunta a tu organización qué se aplica; una checkbox registra la respuesta, pero no es una comprobación legal.

Las plataformas de voluntariado como SignUpGenius gestionan inscripciones por huecos que cierran un hueco cuando se llena. Si lo que necesitas son límites estrictos por hueco, usa una. Ikiform encaja con organizadores que quieren el formulario en su propio sitio y no tienen problema en asignar turnos a mano a partir de una lista limpia.

Diagrama: Convertir la disponibilidad de los voluntarios en un cuadrante con los huecos a la vista

1. Los voluntarios marcan en el formulario todos los turnos que podrían hacer, y este se envía a Ikiform.
2. Ikiform guarda cada inscripción y avisa por email a los coordinadores.
3. Un script lee las inscripciones mediante la API y cuenta los voluntarios por turno frente a lo que necesita cada turno.
4. Escribe rota.csv con nombres y teléfonos para cada turno.
5. Los turnos que faltan cubrir aparecen enseguida, como el montaje con 2 de 6, para que puedas pedírselo a voluntarios que sobran en otros turnos.

Ikiform guarda la disponibilidad. El script la convierte en cobertura, y una persona coordinadora toma las decisiones finales.

## El formulario

Agrupa los turnos por día para que se lean de un vistazo. Las checkboxes se llaman `shifts[]`, así que incluso una sola marca se guarda como lista. Funciona como HTML simple en cualquier sitio o creador de sitios que acepte un embed:

`volunteer.html`:

```html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label>Name <input name="name" autocomplete="name" required></label>
  <label>Email <input name="email" type="email" autocomplete="email" required></label>
  <label>Mobile number for the day <input name="phone" type="tel" autocomplete="tel" placeholder="+44 7700 900123" required></label>

  <fieldset>
    <legend>Shifts you can do</legend>
    <p>Saturday</p>
    <label><input type="checkbox" name="shifts[]" value="sat_setup"> 7:00 to 10:00, setup</label>
    <label><input type="checkbox" name="shifts[]" value="sat_day"> 10:00 to 14:00</label>
    <label><input type="checkbox" name="shifts[]" value="sat_late"> 14:00 to 18:00</label>
    <p>Sunday</p>
    <label><input type="checkbox" name="shifts[]" value="sun_day"> 10:00 to 14:00</label>
    <label><input type="checkbox" name="shifts[]" value="sun_pack"> 14:00 to 17:00, pack-down</label>
  </fieldset>

  <label>Where you'd like to help
    <select name="role">
      <option value="anywhere">Anywhere</option>
      <option value="welcome">Welcome desk</option>
      <option value="food">Food stall</option>
      <option value="stewarding">Stewarding</option>
    </select>
  </label>
  <label>T-shirt size
    <select name="tshirt" required>
      <option value="s">S</option><option value="m" selected>M</option><option value="l">L</option>
      <option value="xl">XL</option><option value="xxl">XXL</option>
    </select>
  </label>
  <label>Emergency contact name and number <input name="emergency_contact" required maxlength="160"></label>
  <label><input type="checkbox" name="adult" required> I'm 18 or over, or a parent or guardian is filling this in with me</label>

  <input type="hidden" name="_redirect" value="https://fair.example/volunteers/thanks">
  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden>
  <button type="submit">Sign me up</button>
</form>
```

Pega esta definición en la pestaña **Fields** del formulario. Así el servidor rechaza una inscripción sin turnos, con un teléfono que no lo es o con la casilla de edad sin marcar, venga de la página que venga:

Formulario: Volunteer signup

- **Nombre** (`name`, text, obligatorio)
- **Email** (`email`, email, obligatorio)
- **Número de móvil para el día** (`phone`, phone, obligatorio)
- **Turnos que puedes hacer** (`shifts`, multi_select, obligatorio)
- **Dónde te gustaría ayudar** (`role`, select)
- **Talla de camiseta** (`tshirt`, select, obligatorio)
- **Nombre y teléfono del contacto de emergencia** (`emergency_contact`, text, obligatorio)
- **Tengo 18 años o más, o un padre, madre o tutor está rellenando esto conmigo** (`adult`, checkbox, obligatorio)

`shifts` es un `multi_select` con `min_items: 1`. `phone` es un campo `phone`, que comprueba el formato del número. `adult` debe estar marcado.

| Campo | Por qué está |
| --- | --- |
| `phone` | Avisar por SMS de cambios de turno y localizar a la gente el día del evento |
| `shifts` | La disponibilidad a partir de la cual asignas |
| `role` | Preferencias, para que el mostrador de bienvenida tenga a quienes lo pidieron |
| `tshirt` | El pedido que haces dos semanas antes |
| `emergency_contact` | Para el equipo de primeros auxilios, no para marketing |
| `adult` | La norma de edad de tu organización, registrada en un envío que conserva su propio `created_at` |

> Los teléfonos y los contactos de emergencia son datos personales de personas que no se están inscribiendo ellas mismas. Guarda la exportación en un lugar privado, comparte el cuadrante solo con los responsables de turno y elimina los envíos después del evento.

## Crea el cuadrante y mira los huecos

Ejecuta esto con Node y una clave de API secreta cuando quieras ver la situación. Toma la inscripción más reciente de cada voluntario, muestra cada turno como cubierto, corto o con gente de sobra, y escribe `rota.csv` con nombres y números para cada turno:

`rota.ts`:

```ts
import { writeFileSync } from "node:fs";
import { createClient } from "@ikiform/sdk";

const NEEDED: Record<string, number> = { sat_setup: 6, sat_day: 10, sat_late: 10, sun_day: 8, sun_pack: 6 };
const ikiform = createClient({ apiKey: process.env.IKIFORM_SECRET_KEY! });
const people = new Map<string, Record<string, unknown>>();

for await (const submission of ikiform.submissions.iterate({ formId: "frm_xxxxxxxxxxxxxxxx", status: "received", limit: 100 })) {
  const email = String(submission.fields.email ?? "").trim().toLowerCase();
  if (email && !people.has(email)) people.set(email, submission.fields);
}

const cell = (value: unknown) => `"${String(value ?? "").replaceAll('"', '""')}"`;
const rows = [["shift", "name", "phone", "role", "tshirt"].join(",")];

for (const [shift, needed] of Object.entries(NEEDED)) {
  const team = [...people.values()].filter((fields) => (fields.shifts as string[] | undefined)?.includes(shift));
  const state = team.length < needed ? `SHORT ${needed - team.length}` : team.length > needed ? "spare" : "ok";
  console.log(`${shift.padEnd(10)} ${team.length}/${needed} ${state}`);
  for (const fields of team) {
    rows.push([shift, fields.name, fields.phone, fields.role, fields.tshirt].map(cell).join(","));
  }
}

writeFileSync("rota.csv", `${rows.join("\n")}\n`);
```

- Las listas vienen de la más reciente a la más antigua, así que un voluntario que se vuelve a inscribir con otra disponibilidad cuenta según su última respuesta.
- Una persona aparece en todos los turnos que marcó. Eso es disponibilidad. Redúcelo a asignaciones en el CSV y comunica a cada persona sus turnos finales.
- Las comas y comillas de los nombres se escapan, así que el CSV se abre sin problemas en cualquier hoja de cálculo.

Para echar un vistazo rápido sin código, exporta el CSV desde **Submissions** y filtra la columna `shifts` por el valor de un turno, como `sat_setup`. Los valores de selección múltiple aparecen juntos en una celda en la exportación.

## De la disponibilidad a las asignaciones

1. Dos semanas antes, ejecuta el cuadrante y pide las camisetas según los recuentos de `tshirt`.
2. Para cada turno corto, escribe a los voluntarios que sobran y que también marcaron ese turno. Ya dijeron que podían.
3. Envía a cada voluntario sus turnos finales desde tu herramienta de email o de mensajería. El **Reply** del email de notificación va directamente a ellos.
4. El día anterior, imprime el cuadrante para los responsables de turno, con los teléfonos.

Ikiform no envía emails a la persona que rellena el formulario. Los emails de notificación solo van a ti y a tu equipo, así que no hay respuesta automática, email de confirmación ni doble opt-in. Envíalos desde tu herramienta de email, activada por un [webhook](https://ikiform.com/es/docs/webhooks.md). Los voluntarios olvidan a qué se apuntaron, así que envía el cuadrante final personalmente una semana antes, y otra vez la tarde anterior.

Para enterarte de cada inscripción donde tu equipo ya conversa, envía el webhook a [Slack](https://ikiform.com/blog/form-submissions-to-slack.md) o [Discord](https://ikiform.com/blog/form-submissions-to-discord.md). Así los coordinadores ven cómo se llena un turno corto en tiempo real.

## Compruébalo antes de reclutar

1. En **Allowed domains** del formulario, añade el dominio de tu sitio, y `localhost` mientras desarrollas. Déjalo vacío para aceptar envíos desde cualquier lugar.
2. Inscríbete marcando un turno. En el panel, `shifts` es una lista con un valor.
3. Intenta inscribirte sin turnos, o con la casilla de edad sin marcar. El navegador te lo impide, y un envío manipulado recibe `422`.
4. Revisa la bandeja de entrada de cada destinatario de las notificaciones. Cada uno recibe un email con los campos, y **Reply** va a quien envió el formulario cuando este tiene un campo `email`.
5. Ejecuta el script del cuadrante. Tu prueba aparece en su turno, y todos los demás turnos aparecen como cortos.
6. Abre `rota.csv` en una hoja de cálculo y revisa la columna de teléfonos.

## Errores comunes

| Problema | Qué hacer |
| --- | --- |
| Un único turno se guarda como texto, no como lista | Las checkboxes se llaman `shifts` sin `[]` y no hay ningún campo definido. Cámbiales el nombre a `shifts[]`, o define `shifts` como `multi_select`. |
| `422` en `phone` | El número no parece un número de teléfono. Pide el prefijo del país en el texto de ayuda, como hace la definición. |
| Un voluntario aparece dos veces en el cuadrante | Se inscribió con dos emails distintos. Únelos a mano; el script agrupa por email. |
| `422 validation_failed` | El envío no coincide con la definición de campos del formulario. `error.fields` indica cada campo y el motivo. Haz que el `name` del input coincida con el id del campo. |
| `429 rate_limited` | Un visitante envió más de lo que permite el límite de frecuencia del formulario. Standard permite 5 por minuto por visitante. Espera lo que indique `Retry-After`, o elige Relaxed o Custom para formularios que envían desde una IP compartida. |

## Preguntas

### ¿Puede Ikiform cerrar un turno cuando se llena?

No. Ikiform no tiene límites por opción. Pide la disponibilidad, cuenta los voluntarios por turno con un script o una hoja, y asigna tú a las personas. Usa una herramienta de inscripción por huecos si los límites estrictos importan más.

### ¿Cómo recojo varios turnos de un mismo voluntario?

Usa checkboxes llamadas shifts[] y define shifts como un campo multi_select. Cada turno marcado se guarda en una lista, y min_items: 1 obliga a marcar al menos uno.

### ¿Pueden los voluntarios cambiar sus turnos?

Pídeles que se vuelvan a inscribir con el mismo email. El script del cuadrante usa la inscripción más reciente de cada voluntario, así que la nueva disponibilidad sustituye a la anterior.

### ¿Puede Ikiform enviar un email a la persona que envió el formulario?

No. Ikiform te envía emails a ti y a tu equipo, no a quien envía el formulario, así que no hay respuesta automática ni email de confirmación. Envía un webhook a tu herramienta de email y manda la confirmación desde ahí.

### ¿Ikiform es gratis?

No, no hay plan gratuito. Probar es gratis: puedes enviar 50 envíos de prueba al mes desde localhost antes de pagar. Para pasar a producción, Pro empieza en 6 $/mes por 500 envíos, o puedes pagar una sola vez 49 $ por 10.000 envíos o 149 $ por 50.000 envíos, con créditos que nunca caducan.

## Más guías

- [Inscripción a eventos](https://ikiform.com/es/guides/event-registration.md): Recibe RSVP en la página de tu evento: el formulario, límite de acompañantes, un recuento fiable, cierre al llenarse y lista de entrada, sin app de entradas.
- [Sin Eventbrite](https://ikiform.com/es/guides/event-registration/without-eventbrite.md): Gestiona la inscripción a un evento comunitario gratuito en tu sitio: elección de sesiones, email de confirmación desde tu herramienta, aforos por sala y lista de entrada.
- [RSVP de boda](https://ikiform.com/es/guides/event-registration/wedding-rsvp.md): Un formulario de RSVP para la web de boda que hiciste tú: una respuesta por invitación, un menú por invitado, una fecha límite y la lista de quién no ha respondido.
