Submit Form via API

Learn how to submit form data programmatically using the Transactional API.

Submit Form via API

This guide shows you how to submit form data directly via the API, bypassing the form widget. This is useful for custom form implementations, mobile apps, or server-side form processing.

Prerequisites

  • A Transactional account with a form created
  • Your form's UUID (found in the form settings)
  • Basic understanding of REST APIs

Finding Your Form UUID

Navigate to your form in the dashboard and copy the UUID from the form settings or URL:

https://app.usetransactional.com/forms/{your-form-uuid}

Basic Form Submission

Submit form data using a POST request:

const response = await fetch(
  'https://api.usetransactional.com/v1/f/{form-uuid}/submit',
  {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      sessionId: crypto.randomUUID(),
      data: {
        name: 'John Doe',
        email: 'john@example.com',
        message: 'Hello from the API!',
      },
    }),
  }
);
 
const result = await response.json();
console.log('Submission ID:', result.submissionId);

Using the SDK

With the TypeScript SDK:

import { Transactional } from 'transactional-sdk';
 
const client = new Transactional({
  apiKey: process.env.TRANSACTIONAL_API_KEY,
});
 
// Submit to a form
const submission = await client.forms.submit('form-uuid', {
  name: 'John Doe',
  email: 'john@example.com',
  message: 'Hello from the SDK!',
});
 
console.log('Submission ID:', submission.id);

Handling File Uploads

For forms with file upload fields, you need to upload files first:

// Step 1: Upload the file
const formData = new FormData();
formData.append('file', fileInput.files[0]);
 
const uploadResponse = await fetch(
  'https://api.usetransactional.com/v1/f/{form-uuid}/upload',
  {
    method: 'POST',
    body: formData,
  }
);
 
const { fileUrl } = await uploadResponse.json();
 
// Step 2: Submit the form with the file URL
const submission = await fetch(
  'https://api.usetransactional.com/v1/f/{form-uuid}/submit',
  {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      sessionId: crypto.randomUUID(),
      data: {
        name: 'John Doe',
        resume: fileUrl,
      },
    }),
  }
);

Server-Side Submission (Node.js)

When submitting from a server, you can include the client's IP for analytics:

import { Transactional } from 'transactional-sdk';
 
const client = new Transactional({
  apiKey: process.env.TRANSACTIONAL_API_KEY,
});
 
async function handleFormSubmission(req, res) {
  const { name, email, message } = req.body;
 
  try {
    const submission = await client.forms.submit('form-uuid', {
      name,
      email,
      message,
    }, {
      metadata: {
        clientIp: req.headers['x-forwarded-for'] || req.ip,
        userAgent: req.headers['user-agent'],
        referrer: req.headers['referer'],
      },
    });
 
    res.json({ success: true, submissionId: submission.id });
  } catch (error) {
    res.status(400).json({ error: error.message });
  }
}

Validation

The API validates submissions against your form's field requirements:

try {
  await client.forms.submit('form-uuid', {
    // Missing required 'email' field
    name: 'John Doe',
  });
} catch (error) {
  // error.code === 'VALIDATION_ERROR'
  // error.details.email === 'This field is required'
}

Tracking Partial Progress

For multi-step forms, save progress as users complete each step:

const sessionId = crypto.randomUUID();
 
// Save progress after step 1
await fetch('https://api.usetransactional.com/v1/f/{form-uuid}/partial', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    sessionId,
    data: { name: 'John Doe', email: 'john@example.com' },
    currentFieldIndex: 2,
  }),
});
 
// User continues later...
 
// Complete submission
await fetch('https://api.usetransactional.com/v1/f/{form-uuid}/submit', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    sessionId, // Same session to link partial progress
    data: {
      name: 'John Doe',
      email: 'john@example.com',
      message: 'Final message',
    },
  }),
});

Error Handling

Handle common submission errors:

async function submitForm(data) {
  try {
    const response = await fetch(
      'https://api.usetransactional.com/v1/f/{form-uuid}/submit',
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ sessionId: crypto.randomUUID(), data }),
      }
    );
 
    if (!response.ok) {
      const error = await response.json();
 
      switch (error.error.code) {
        case 'FORM_NOT_FOUND':
          throw new Error('Form does not exist or is not published');
        case 'FORM_CLOSED':
          throw new Error('This form is no longer accepting submissions');
        case 'VALIDATION_ERROR':
          throw new Error(`Validation failed: ${JSON.stringify(error.error.details)}`);
        case 'RATE_LIMITED':
          throw new Error('Too many submissions. Please try again later.');
        default:
          throw new Error('Submission failed');
      }
    }
 
    return await response.json();
  } catch (error) {
    console.error('Form submission error:', error);
    throw error;
  }
}

Webhooks for Submission Notifications

Set up a webhook to receive notifications when forms are submitted:

// Configure webhook in dashboard or via API
await client.forms.webhooks.create('form-uuid', {
  url: 'https://your-app.com/webhooks/form-submitted',
  events: ['form.submitted'],
});
 
// Your webhook handler
app.post('/webhooks/form-submitted', (req, res) => {
  const { event, data } = req.body;
 
  if (event === 'form.submitted') {
    const { submissionId, formId, data: formData } = data;
    console.log(`New submission ${submissionId}:`, formData);
 
    // Process the submission...
    processSubmission(formData);
  }
 
  res.sendStatus(200);
});

Next Steps