Live monitoring

Live form monitoring

See what visitors are typing — before they hit submit. Live Draft Saving captures partial responses so you can follow up on abandoned forms and help visitors complete them.

What is Live Draft Saving

When Live Draft Saving (LDS) is enabled on a form, ContactFire saves the visitor's answers to the server as they type. If the visitor closes the tab or navigates away without submitting, you still receive their partial data.

See it live

Watch drafts appear in real-time in your Live inbox tab.

Follow up

Get notified after 10 minutes of visitor inactivity on a draft.

Resume drafts

Visitors can resume where they left off on any device.

Enable live draft saving

LDS is controlled per-form in Form Studio:

  1. Open your form in Form Studio
  2. Click Settings
  3. Toggle Live Draft Saving on
  4. Click Save

LDS is off by default. Enable it only for forms where the drop-off data is valuable, since it does store partial visitor data on the server.

How it works

1
Visitor starts the form
The embed script checks localStorage for an existing sessionId for this form.
2
First field interaction triggers a save
The script calls POST /api/public/forms/{formId}/partial with the current data and receives a sessionId.
3
sessionId stored in localStorage
The browser stores the sessionId under a key specific to this form ID.
4
Subsequent saves update the same draft
Each save call passes the stored sessionId, updating the same record instead of creating a new one.
5
Visitor submits
The final POST /api/public/forms/{formId}/submit call includes the sessionId, upgrading the partial record to a full submission.
6
Visitor abandons
The partial remains in the database with submissionStatus = partial. After 10 minutes of inactivity, an idle notification is sent.

Live tab in your inbox

Your dashboard inbox has a Live tab that shows all partial submissions for forms with LDS enabled. Each entry shows:

  • Fields filled in so far
  • Last activity timestamp
  • Whether the draft was eventually submitted or abandoned

Submitted drafts move from the Live tab to your main inbox automatically.

Draft resume flow

When a visitor returns to the same form on the same browser, the embed script:

  1. Reads the stored sessionId from localStorage
  2. Calls GET /api/public/forms/{formId}/partial?sessionId=...
  3. Pre-fills all previously saved fields
  4. Picks up from where the visitor left off

The sessionId is per-form and per-browser. A visitor on a different device starts a fresh session.

sessionId vs submissionPublicId: The partial save response returns both. sessionId is the resume token — pass it on every subsequent save and the final submit. submissionPublicId is a public identifier for the draft record, not a continuation token.

Partial submission API

Use the API directly if you are building a custom frontend:

// Save a partial (first save — no sessionId yet)
const res = await fetch(
  'https://my.contactfire.com/api/public/forms/YOUR_FORM_ID/partial',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-embed-token': 'cf_embed_your_token',
    },
    body: JSON.stringify({
      data: { name: 'Alice', email: '[email protected]' },
    }),
  }
);

const { sessionId } = await res.json();
localStorage.setItem('cf_session_YOUR_FORM_ID', sessionId);
// Update an existing draft (subsequent saves — pass sessionId)
const sessionId = localStorage.getItem('cf_session_YOUR_FORM_ID');

await fetch(
  'https://my.contactfire.com/api/public/forms/YOUR_FORM_ID/partial',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'x-embed-token': 'cf_embed_your_token',
    },
    body: JSON.stringify({
      sessionId,
      data: { name: 'Alice', email: '[email protected]', message: 'Hello' },
    }),
  }
);
// Resume a draft — pre-fill the form on page load
const sessionId = localStorage.getItem('cf_session_YOUR_FORM_ID');
if (sessionId) {
  const res = await fetch(
    `https://my.contactfire.com/api/public/forms/YOUR_FORM_ID/partial?sessionId=${sessionId}`,
    {
      headers: { 'x-embed-token': 'cf_embed_your_token' },
    }
  );
  if (res.ok) {
    const { data } = await res.json();
    // data = { name: 'Alice', email: '[email protected]', ... }
    // pre-fill your form fields from `data`
  }
}

Idle notifications

When a draft has not been updated for 10 minutes, ContactFire sends an email notification so you can follow up. The notification includes the full partial data saved so far.

Idle notification contains
  • Form name
  • All fields saved before the visitor went idle
  • Timestamp of last activity
  • Link to the full draft in your dashboard

Common mistakes

These mistakes show up in almost every custom LDS integration. Check each one before shipping.

Using POST /api/form/submit as the LDS finalization route
Use POST /api/public/forms/{formId}/submit with the same sessionId to upgrade a partial draft to a full submission.
Treating submissionPublicId as the resume token
Store sessionId in localStorage. That is the only token needed to continue or restore a draft.
Keeping stale local draft data after a failed restore
If GET /partial returns no data or fails, clear the stored sessionId. Never pre-fill from local state after a failed restore.
Clearing the stored sessionId before the final submit succeeds
Only clear the sessionId after the submit response confirms success. Clear on error leaves the draft resumable.
Making autosave depend on analytics scripts (PostHog etc)
Keep the autosave and submit paths fully independent of analytics. LDS must work when third-party scripts fail to load.
Previous: Notifications Next: Changelog