Send a form to your email

Send a form to your email6 min read

Send one form to several email addresses

Email one form to your whole team, or route each message to the right person by topic, without letting the form itself pick who gets the mail.

Preet SutharFounder, Ikiform

Add each person as a recipient of the form in the dashboard: workspace members by ticking them, and anyone else by a confirmation link, up to 10 addresses. Everyone gets their own copy of every submission. To send sales questions to one person and support to another, route by a field in a webhook instead.

What's different here

Old PHP scripts and some form services let the form name its own recipients, with a hidden to or cc field. That's convenient, and it's also how contact forms become spam relays: a bot changes the hidden field and your server mails whoever it likes, from your domain. Ikiform has no recipient field. Who gets email is set in the dashboard, and anyone outside your workspace has to confirm first.

That leaves two different jobs, and they're solved in different places:

You wantWhere it's doneLimit
Everyone gets every submissionRecipients in the form's Notifications settings10 addresses per form
Each submission goes to the right personA webhook that reads a field and emails one team3 webhook endpoints per workspace

Send every submission to the whole team

One submission, one email per recipientyour formone postIkiformresolves recipientsticked membersverified workspaceemailsoutside addressesconfirmed by linkbounced or pendingskippedpostOne submission, one email per recipientyour formone postIkiformresolvesrecipientsticked membersverified workspaceemailsoutsideaddressesconfirmed by linkbounced orpendingskippedpost
  1. The form posts once to Ikiform.
  2. Ikiform builds the recipient list: the members ticked for this form, or the workspace default, plus outside addresses that confirmed.
  3. Duplicates are dropped and the list stops at 10.
  4. Each recipient gets a separate email, so nobody sees the others' addresses.
  5. Addresses still waiting for confirmation, and addresses paused after a bounce, are skipped.
Separate copies mean one person replying doesn't copy in the rest. Decide who answers what before you add five people.
  1. Open the form in the dashboard, then Settings and Notifications. Make sure Email new submissions is on.
  2. Tick each workspace member who should get email. Only members with a verified address are listed.
  3. For anyone outside the workspace, enter their address under Add someone outside the workspace and press Send link. You can add 5 per form.
  4. Ask them to confirm. Until they do, their row says Waiting for confirmation and they get nothing.

Outside recipients see the submission's fields and an unsubscribe link; they don't get a dashboard link, because they have no account. Members get a View submission button. If someone leaves, remove their row; if they unsubscribe, their row goes too.

Need more than 10 people, or a team that changes often? Add one shared address, such as a Google Group or a mail alias, as the only recipient and manage who's in it there.

Members and outside addresses

The two kinds of recipient behave differently, which matters when you decide who to add where:

ItemWorkspace memberOutside address
Added byTicking them on the formSend link, then their confirmation
Sees in the emailFields and a View submission buttonFields and an unsubscribe link
Can open the dashboardYesNo
Stops getting email whenYou untick them, or they leave the workspaceYou remove the row, or they unsubscribe
Counts towardThe 10-address limitThe 10-address limit and the 5 outside addresses per form

Give the people who answer messages a seat in the workspace, so they can see past submissions and spam. Use outside addresses for people who only need to know, such as a manager or a shared inbox.

Route each submission by topic

Recipients can't depend on what the visitor picked. For that, add a field that names the team, and let a small webhook handler choose the address. Here's the form:

contact.html
<form action="https://ikiform.com/f/frm_xxxxxxxxxxxxxxxx" method="post">
  <label for="department">Who should read this?</label>
  <select id="department" name="department" required>
    <option value="sales">Sales</option>
    <option value="support">Support</option>
    <option value="billing">Billing</option>
  </select>

  <label for="name">Name</label>
  <input id="name" name="name" autocomplete="name" required />

  <label for="email">Email</label>
  <input id="email" name="email" type="email" autocomplete="email" required />

  <label for="message">Message</label>
  <textarea id="message" name="message" rows="5" required></textarea>

  <input name="_gotcha" type="text" tabindex="-1" autocomplete="off" hidden />
  <button>Send</button>
</form>

Define department as a select field in the dashboard, so a post can only carry sales, support or billing. A bot that sends anything else gets 422 validation_failed and never reaches your handler.

Then the handler. It verifies the signature, picks the team from department, and sends through your own email provider, here Resend. Because you send the email, you choose its subject too:

app/api/route-message/route.ts
import { Resend } from "resend";
import { verifyWebhook, WebhookVerificationError } from "@ikiform/sdk/webhooks";

const resend = new Resend(process.env.RESEND_API_KEY);

const TEAMS: Record<string, string[]> = {
  sales: ["sales@example.com"],
  support: ["support@example.com", "oncall@example.com"],
  billing: ["accounts@example.com"],
};

export async function POST(request: Request) {
  const payload = await request.text();
  try {
    const event = await verifyWebhook({
      payload,
      headers: request.headers,
      secret: process.env.IKIFORM_WEBHOOK_SECRET!,
    });
    if (event.type !== "submission.created") return new Response(null, { status: 204 });

    const fields = event.data.fields;
    const department = String(fields.department ?? "support");
    const { error } = await resend.emails.send(
      {
        from: "Website <forms@example.com>",
        to: TEAMS[department] ?? TEAMS.support,
        replyTo: typeof fields.email === "string" ? fields.email : undefined,
        subject: `[${department}] ${String(fields.name ?? "New message")}`,
        text: Object.entries(fields)
          .map(([key, value]) => `${key}: ${String(value)}`)
          .join("\n"),
      },
      { idempotencyKey: event.id },
    );
    if (error) return new Response(error.message, { status: 502 });
    return new Response(null, { status: 204 });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return new Response(error.reason, { status: 400 });
    throw error;
  }
}
  • The addresses live in your code, not in the form. A visitor chooses a team name, never an address.
  • replyTo keeps Reply pointing at the visitor, as Ikiform's own email does.
  • idempotencyKey: event.id makes a retried delivery send nothing new. Ikiform retries a delivery that doesn't get a 2xx, with the same id.
  • A 502 when the provider fails tells Ikiform to retry later, so a short outage doesn't lose a message.
  • Unknown or missing departments fall back to support rather than dropping the message.

Add the URL under Webhooks in the dashboard and copy its whsec_ secret into IKIFORM_WEBHOOK_SECRET. The webhooks docs cover retries and the payload. Prefer no code? A Zapier or n8n flow with a filter per department does the same.

Keep Ikiform's own email on for one person as a backstop, or switch off Email new submissions so nobody gets the message twice.

Check it works

  1. Check every recipient row in the form's Notifications settings. None should say Waiting for confirmation.
  2. Fill in the form on your page and submit it. You land on your thank-you page, or Ikiform's hosted one.
  3. Each recipient gets their own email. Ask one of them to check the To line: it shows only their address.
  4. If you route by webhook, submit once per department and check the delivery log under Webhooks. Each shows succeeded.
  5. Post with the honeypot filled in, _gotcha=bot. It still gets a success answer, but the submission is stored as spam and sends no email or webhook.

Gotchas

  • No CC or BCC. Each recipient gets a separate message, so there's no shared thread. Agree who replies, or route by topic.
  • The cap counts submissions, not recipients. Past 50 submissions an hour on one form, every recipient gets the digest instead.
  • Membership changes. When someone leaves the workspace, they stop getting email at once. Check the form's recipients after a team change, so the form isn't left emailing nobody.
  • A bounced address is paused. The row shows Emails bounced with a date, and email to it resumes after that. Fix the address or remove it.
  • Nothing goes to the visitor. 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.

Questions

Stuck on a step, or want a guide for your situation? Email hello@ikiform.com.