Event registration7 min read
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.
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.
- Volunteers tick every shift they could do on the signup form, which posts to Ikiform.
- Ikiform stores each signup and emails the coordinators.
- A script reads the signups through the API and counts volunteers per shift against what each shift needs.
- It writes rota.csv with names and phone numbers for each shift.
- Shifts that are short show up straight away, such as setup with 2 of 6, so you can ask volunteers who are spare elsewhere.
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:
<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:
Preview
Runs Ikiform's validator in your browser. Nothing is sent.
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 |
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:
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
- Two weeks out, run the rota and order T-shirts from the
tshirtcounts. - For each short shift, message the volunteers who are spare and also ticked that shift. They already said they could.
- Send each volunteer their final shifts from your email or messaging tool. The notification email's Reply goes straight to them.
- 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. 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 or Discord. Coordinators then see a short shift fill up as it happens.
Check it before you recruit
- Under the form's Allowed domains, add your site's domain, plus
localhostwhile you develop. Leave it empty to accept posts from anywhere. - Sign up with one shift ticked. In the dashboard,
shiftsis a list with one value. - Try to sign up with no shifts, or with the age box unticked. The browser stops you, and a crafted post gets
422. - 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
emailfield. - Run the rota script. Your test shows under its shift, and every other shift shows as short.
- Open
rota.csvin 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
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.
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.
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.
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.
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.
Stuck on a step, or want a guide for your situation? Email hello@ikiform.com.