# Volunteer signup form with shifts, for events and festivals

> A volunteer signup form where people pick shifts, with a phone number for the day, and a script that shows which shifts are short and builds the rota.

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

Let volunteers tick every shift they can do on one form, with a phone number, a T-shirt size and an emergency contact, and post it to Ikiform. Ikiform stores each signup and emails the coordinators, but it doesn't fill or close shifts, so run a short script that counts volunteers per shift against what you need and writes a rota. Fill short shifts by asking the spare volunteers directly.

## What's different about volunteers

An RSVP asks one question: are you coming? A volunteer signup asks when, and the answer only matters against what each shift needs. Setup at 7 a.m. needs six people and gets two; the sunny afternoon needs ten and gets nineteen. The job isn't counting heads, it's coverage.

- **Availability, not a single choice.** People can do several shifts. Ask for every shift they could do and assign from there, instead of making them guess what's needed.
- **Contact on the day.** Email is for planning. On the day you need a mobile number, and an emergency contact for each volunteer.
- **Things to order.** T-shirts, lanyards and lunches, by size and count.
- **Age.** Many events need an adult, or a guardian's agreement, for each volunteer. Ask your organisation what applies; a checkbox records the answer but isn't a legal check.

Volunteer platforms such as SignUpGenius run slot-based signups that close a slot when it's full. If hard per-slot limits are what you need, use one. Ikiform suits organisers who want the form on their own site and are happy to assign shifts by hand from a clean list.

Diagram: Turning volunteer availability into a rota with the gaps showing

1. Volunteers tick every shift they could do on the signup form, which posts to Ikiform.
2. Ikiform stores each signup and emails the coordinators.
3. A script reads the signups through the API and counts volunteers per shift against what each shift needs.
4. It writes rota.csv with names and phone numbers for each shift.
5. Shifts that are short show up straight away, such as setup with 2 of 6, so you can ask volunteers who are spare elsewhere.

Ikiform holds the availability. The script turns it into coverage, and a coordinator makes the final calls.

## The form

Group shifts by day so people can scan them. The checkboxes are named `shifts[]`, so even a single tick is stored as a list. It works as plain HTML on any site or builder that accepts an 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>
```

Paste this definition on the form's **Fields** tab. The server then refuses a signup with no shifts, a phone number that isn't one, or an unticked age box, whatever page it came from:

Form: Volunteer signup

- **Name** (`name`, text, required)
- **Email** (`email`, email, required)
- **Mobile number for the day** (`phone`, phone, required)
- **Shifts you can do** (`shifts`, multi_select, required)
- **Where you'd like to help** (`role`, select)
- **T-shirt size** (`tshirt`, select, required)
- **Emergency contact name and number** (`emergency_contact`, text, required)
- **I'm 18 or over, or a parent or guardian is filling this in with me** (`adult`, checkbox, required)

`shifts` is a `multi_select` with `min_items: 1`. `phone` is a `phone` field, which checks the number's shape. `adult` must be ticked.

| Field | Why it's here |
| --- | --- |
| `phone` | Texting shift changes and finding people on the day |
| `shifts` | Availability you assign from |
| `role` | Preferences, so the welcome desk gets the people who asked for it |
| `tshirt` | The order you place two weeks out |
| `emergency_contact` | For the first-aid team, not for marketing |
| `adult` | Your organisation's age rule, recorded on a submission that keeps its own `created_at` |

> Phone numbers and emergency contacts are personal data about people who aren't signing up themselves. Keep the export in a private place, share the rota only with shift leads, and delete the submissions after the event.

## Build the rota and see the gaps

Run this with Node and a secret API key whenever you want the picture. It takes each volunteer's newest signup, prints each shift as filled, short or spare, and writes `rota.csv` with names and numbers for each shift:

`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`);
```

- Lists come newest first, so a volunteer who signs up again with changed availability is counted by their latest answer.
- A person appears under every shift they ticked. That's availability. Cut it down to assignments in the CSV, and tell each person their final shifts.
- Commas and quotes in names are escaped, so the CSV opens cleanly in any spreadsheet.

For a quick look without code, export the CSV from **Submissions** and filter the `shifts` column for a shift's value, such as `sat_setup`. Multi-select values in the export are joined in one cell.

## From availability to assignments

1. Two weeks out, run the rota and order T-shirts from the `tshirt` counts.
2. For each short shift, message the volunteers who are spare and also ticked that shift. They already said they could.
3. Send each volunteer their final shifts from your email or messaging tool. The notification email's **Reply** goes straight to them.
4. The day before, print the rota for shift leads, with phone numbers.

Ikiform doesn't email the person who fills in the form. Notification emails go to you and your team only, so there is no autoresponder, confirmation email or double opt-in. Send those from your email tool, triggered by a [webhook](https://ikiform.com/docs/webhooks.md). Volunteers forget what they signed up for, so send the final rota personally a week before, and again the evening before.

To hear about each signup where your team already talks, send the webhook to [Slack](https://ikiform.com/blog/form-submissions-to-slack.md) or [Discord](https://ikiform.com/blog/form-submissions-to-discord.md). Coordinators then see a short shift fill up as it happens.

## Check it before you recruit

1. Under the form's **Allowed domains**, add your site's domain, plus `localhost` while you develop. Leave it empty to accept posts from anywhere.
2. Sign up with one shift ticked. In the dashboard, `shifts` is a list with one value.
3. Try to sign up with no shifts, or with the age box unticked. The browser stops you, and a crafted post gets `422`.
4. Check the inbox of each notification recipient. Each gets an email with the fields, and **Reply** goes to the submitter when the form has an `email` field.
5. Run the rota script. Your test shows under its shift, and every other shift shows as short.
6. Open `rota.csv` in a spreadsheet and check the phone number column.

## Gotchas

| Problem | What to do |
| --- | --- |
| A single shift is stored as text, not a list | The checkboxes are named `shifts` without `[]`, and no field is defined. Rename them to `shifts[]`, or define `shifts` as a `multi_select`. |
| `422` on `phone` | The number doesn't look like a phone number. Ask for the country code in the help text, as the definition does. |
| A volunteer appears twice in the rota | They signed up with two different emails. Merge them by hand; the script keys on email. |
| `422 validation_failed` | The post doesn't match the form's field definition. `error.fields` names each field and the reason. Match the input `name` to the field id. |
| `429 rate_limited` | One visitor sent more than the form's rate limit allows. Standard allows 5 a minute per visitor. Wait for `Retry-After`, or choose Relaxed or Custom for forms that post from one shared IP. |

## Questions

### Can Ikiform close a shift when it's full?

No. Ikiform has no per-option limits. Ask for availability, count volunteers per shift with a script or a sheet, and assign people yourself. Use a slot-based signup tool if hard limits matter more.

### How do I collect several shifts from one volunteer?

Use checkboxes named shifts[] and define shifts as a multi_select field. Every ticked shift is stored in one list, and min_items: 1 makes at least one required.

### Can volunteers change their shifts?

Ask them to sign up again with the same email. The rota script uses each volunteer's newest signup, so the new availability replaces the old.

### Can Ikiform email the person who submitted the form?

No. Ikiform emails you and your team, not the submitter, so there is no autoresponder or confirmation email. Send a webhook to your email tool and send the confirmation from there.

### Is Ikiform free?

No, there is no free plan. Testing is free: you can send 50 test submissions a month from localhost before you pay. To go live, Pro starts at $6/mo for 500 submissions, or you can pay once for 10,000 submissions for $49 or 50,000 submissions for $149, with credits that never expire.

## More guides

- [Event registration](https://ikiform.com/guides/event-registration.md): Take RSVPs on your own event page: the form, guest limits, an honest headcount, closing when full and a check-in list, without a ticketing app.
- [Without Eventbrite](https://ikiform.com/guides/event-registration/without-eventbrite.md): Run registration for a free community event on your own site: session choices, a confirmation email from your own tool, room counts and a check-in list.
- [Wedding RSVP](https://ikiform.com/guides/event-registration/wedding-rsvp.md): An RSVP form for a wedding website you built yourself: one reply per invitation, a meal per named guest, a deadline and a list of who hasn't replied.
