Skip to content

TypeScript SDK ​

The FlowForm TypeScript SDK provides a typed client for the FlowForm API. It handles authentication, request formatting, and response parsing.

Installation ​

bash
npm install @flowformhq/sdk
bash
pnpm add @flowformhq/sdk
bash
yarn add @flowformhq/sdk

Quick Start ​

typescript
import { FlowForm } from '@flowformhq/sdk'

const client = new FlowForm({
  baseUrl: 'http://localhost:8000',
  token: 'YOUR_SANCTUM_TOKEN', // optional for public endpoints
})

// List active forms
const forms = await client.forms.list()

// Get a form schema
const schema = await client.forms.schema('550e8400-e29b-41d4-a716-446655440000')

// Create and complete a submission
const submission = await client.submissions.create({
  formUuid: '550e8400-e29b-41d4-a716-446655440000',
})

await client.submissions.upsertValues(submission.uuid, [
  { fieldId: 1, value: 'Jane Doe' },
  { fieldId: 2, value: 'jane@example.com' },
])

await client.submissions.update(submission.uuid, { status: 'completed' })

Configuration ​

typescript
const client = new FlowForm({
  // Required: your FlowForm API base URL
  baseUrl: 'https://api.example.com',

  // Optional: Sanctum bearer token for authenticated endpoints
  token: 'YOUR_TOKEN',

  // Optional: custom fetch implementation (defaults to global fetch)
  fetch: customFetch,

  // Optional: default headers added to every request
  headers: {
    'X-Custom-Header': 'value',
  },
})

Forms ​

List Forms ​

Retrieve a paginated list of active forms.

typescript
const result = await client.forms.list({ page: 1, perPage: 10 })

console.log(result.data)    // Form[]
console.log(result.meta)    // { current_page, last_page, per_page, total }

Get Form by UUID ​

typescript
const form = await client.forms.get('550e8400-e29b-41d4-a716-446655440000')

console.log(form.name)        // "Contact Form"
console.log(form.slug)        // "contact-form"
console.log(form.is_active)   // true

Get Form by Slug ​

typescript
const form = await client.forms.getBySlug('contact-form')

Get Form Schema ​

Returns the full form structure including steps, fields, options, and conditions.

typescript
const schema = await client.forms.schema(
  '550e8400-e29b-41d4-a716-446655440000'
)

for (const step of schema.steps) {
  console.log(`Step ${step.step_number}: ${step.title}`)

  for (const field of step.fields) {
    console.log(`  ${field.code} (${field.field_type.name})`)

    if (field.options.length > 0) {
      for (const option of field.options) {
        console.log(`    - ${option.label}: ${option.value}`)
      }
    }
  }
}

Submissions ​

All submission methods require a token to be set in the client configuration.

Create Submission ​

typescript
const submission = await client.submissions.create({
  formUuid: '550e8400-e29b-41d4-a716-446655440000',
})

console.log(submission.uuid)          // "7c9e6679-..."
console.log(submission.status)        // "draft"
console.log(submission.current_step)  // 1

Get Submission ​

typescript
const submission = await client.submissions.get(
  '7c9e6679-7425-40de-944b-e07fc1f90ae7'
)

for (const value of submission.values) {
  console.log(`${value.field_code}: ${value.value}`)
}

Update Submission ​

typescript
await client.submissions.update('7c9e6679-7425-40de-944b-e07fc1f90ae7', {
  status: 'completed',
  meta: { source: 'web', completed_at: new Date().toISOString() },
})

Upsert Values ​

Submit or update field values. Values are upserted -- sending a value for a field that already has one will replace it.

typescript
await client.submissions.upsertValues(
  '7c9e6679-7425-40de-944b-e07fc1f90ae7',
  [
    { fieldId: 1, value: 'Jane Doe' },
    { fieldId: 2, value: 'jane@example.com' },
  ]
)

To link a value to an entity record:

typescript
await client.submissions.upsertValues(
  '7c9e6679-7425-40de-944b-e07fc1f90ae7',
  [
    { fieldId: 10, value: 'John Doe', entityRecordId: 5 },
    { fieldId: 11, value: '1990-05-15', entityRecordId: 5 },
  ]
)

Advance Step ​

typescript
const result = await client.submissions.advance(
  '7c9e6679-7425-40de-944b-e07fc1f90ae7'
)

console.log(result.current_step)        // 2
console.log(result.is_last_step)        // true
console.log(result.progress_percentage) // 100

Retreat Step ​

typescript
const result = await client.submissions.retreat(
  '7c9e6679-7425-40de-944b-e07fc1f90ae7'
)

console.log(result.current_step)        // 1
console.log(result.progress_percentage) // 50

Get Conditions ​

Evaluate field conditions based on the current submission values.

typescript
const conditions = await client.submissions.conditions(
  '7c9e6679-7425-40de-944b-e07fc1f90ae7'
)

// conditions is a Record<number, { visible: boolean; required: boolean }>
for (const [fieldId, state] of Object.entries(conditions)) {
  if (!state.visible) {
    console.log(`Field ${fieldId} is hidden`)
  }
  if (state.required) {
    console.log(`Field ${fieldId} is required`)
  }
}

Error Handling ​

The SDK throws typed errors for different failure scenarios.

typescript
import { FlowForm, FlowFormError, ValidationError } from '@flowformhq/sdk'

try {
  await client.submissions.create({ formUuid: 'invalid-uuid' })
} catch (error) {
  if (error instanceof ValidationError) {
    // 422 response -- field-level validation errors
    console.log(error.errors)
    // { form_uuid: ["The form uuid must be a valid UUID."] }
  } else if (error instanceof FlowFormError) {
    // Other API errors (401, 404, 429, 500)
    console.log(error.status)   // HTTP status code
    console.log(error.message)  // Error message from the API
  }
}

Error Types ​

Error ClassHTTP StatusDescription
ValidationError422Invalid request data
FlowFormError401Missing or invalid authentication
FlowFormError404Resource not found
FlowFormError429Rate limit exceeded
FlowFormError500Server error

TypeScript Types ​

The SDK exports types for all API responses.

typescript
import type {
  Form,
  FormSchema,
  Step,
  Field,
  FieldType,
  FieldOption,
  Condition,
  Submission,
  SubmissionValue,
  FieldConditionState,
  PaginatedResponse,
} from '@flowformhq/sdk'

Full Example: Multi-Step Form ​

typescript
import { FlowForm } from '@flowformhq/sdk'

const client = new FlowForm({
  baseUrl: 'http://localhost:8000',
  token: 'YOUR_TOKEN',
})

async function submitJobApplication() {
  // 1. Load the form schema
  const schema = await client.forms.schema('550e8400-e29b-41d4-a716-446655440000')

  // 2. Create a draft submission
  const submission = await client.submissions.create({
    formUuid: schema.uuid,
  })

  // 3. Fill in step 1 fields
  await client.submissions.upsertValues(submission.uuid, [
    { fieldId: 1, value: 'Jane Doe' },
    { fieldId: 2, value: 'jane@example.com' },
    { fieldId: 3, value: 'employed' },
  ])

  // 4. Check which fields should be visible
  const conditions = await client.submissions.conditions(submission.uuid)

  // Field 4 (company_name) is visible because employment_status = "employed"
  if (conditions[4].visible) {
    await client.submissions.upsertValues(submission.uuid, [
      { fieldId: 4, value: 'Acme Corp' },
    ])
  }

  // 5. Move to step 2
  const step2 = await client.submissions.advance(submission.uuid)
  console.log(`Now on step ${step2.current_step}`)

  // 6. Fill in step 2 fields
  await client.submissions.upsertValues(submission.uuid, [
    { fieldId: 6, value: 'I am passionate about building great software...' },
  ])

  // 7. Complete the submission
  await client.submissions.update(submission.uuid, {
    status: 'completed',
    meta: { submitted_from: 'careers-page' },
  })

  console.log('Application submitted!')
}

Licensed under CC BY 4.0.