All support articles

How to submit a form with JavaScript (AJAX/JSON)

Use JavaScript submissions when you want to stay on the same page (SPAs), render your own success state, or submit JSON instead of a browser redirect.

Endpoint URL

Your endpoint URL is shown in the dashboard (and in the Integration tab). It looks like:

https://www.forms.fyi/api/f/<form_id>

Vanilla JavaScript (fetch)

This example reads a normal HTML form, converts it to JSON, and posts it to Forms.fyi.

const form = document.querySelector('#my-form');

form.addEventListener('submit', async (e) => {
  e.preventDefault();
  const formData = new FormData(form);

  // Convert to JSON (skip honeypot field)
  const data = {};
  for (const [key, value] of formData.entries()) {
    if (key !== 'special-instructions') data[key] = value;
  }

  const res = await fetch('https://www.forms.fyi/api/f/<form_id>', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  });

  const result = await res.json().catch(() => ({}));

  if (res.ok) {
    // TODO: show success UI
    form.reset();
  } else {
    // TODO: show error UI
    console.error(result.error || 'Submission failed');
  }
});
Redirects don’t run on AJAX

When you submit JSON, Forms.fyi returns JSON (success/error). Your browser will not navigate to a thank-you page automatically, even if you configured a redirect URL in settings.

If you want a thank-you page, redirect in your own client code after a successful response.

React-friendly example

Use a local status state and submit with fetch.

import { useState } from 'react';

export function ContactForm() {
  const [status, setStatus] = useState('idle'); // idle | submitting | success | error

  async function onSubmit(e) {
    e.preventDefault();
    setStatus('submitting');

    const formData = new FormData(e.currentTarget);
    const data = Object.fromEntries(formData.entries());
    delete data['special-instructions']; // honeypot

    try {
      const res = await fetch('https://www.forms.fyi/api/f/<form_id>', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(data),
      });
      if (res.ok) {
        setStatus('success');
        e.currentTarget.reset();
      } else {
        setStatus('error');
      }
    } catch {
      setStatus('error');
    }
  }

  if (status === 'success') return <p>Thanks! We got your message.</p>;

  return (
    <form onSubmit={onSubmit}>
      <input type="text" name="special-instructions" style={{ display: 'none' }} tabIndex={-1} autoComplete="off" />
      <input name="email" type="email" required />
      <textarea name="message" required />
      <button disabled={status === 'submitting'}>Send</button>
      {status === 'error' ? <p>Failed to send. Try again.</p> : null}
    </form>
  );
}

Common errors

  • 403 Origin not allowed usually means your site domain doesn’t match the form’s allowed origin.
  • 429 Too many submissions can be rate limiting or a monthly limit.
  • Validation errors happen when the form has required fields or schema rules enabled (Pro).