OpenForm

party

Last updated on

Define signing roles and signature requirements

Define party roles for signatories with signature configuration. Parties represent roles like "buyer", "seller", or "guarantor" that can be filled by persons or organizations at runtime.

Party data format is determined by max:

  • max = 1 (default): single party object with required id
  • max > 1: array of party objects, each with required id

Examples

Creating parties

// Single signer with signature requirement
const buyer = party()
  .label('Buyer')
  .partyType('person')
  .signature({ required: true })
  .build()

// Organization signer with witness requirement
const company = party()
  .label('Company')
  .partyType('organization')
  .description('The selling organization')
  .signature({ required: true, witnesses: 1 })
  .build()

// Multiple allowed (e.g., co-signers)
const coOwners = party()
  .label('Co-owners')
  .min(1)
  .max(4)  // max > 1 enables array format
  .signature({ required: true })
  .build()

// Optional party (min: 0 allows empty)
const coSigner = party()
  .label('Co-signer')
  .optional()  // sets min: 0
  .signature({ required: true })
  .build()

// Conditional requirement
const guarantor = party()
  .label('Guarantor')
  .required('needsGuarantor')
  .signature({ required: true })
  .build()

// Notarized signature
const affiant = party()
  .label('Affiant')
  .signature({ required: true, notarized: true })
  .build()

Adding parties to forms

// Object pattern
const form = open.form({
  name: 'purchase-agreement',
  parties: {
    buyer: {
      label: 'Buyer',
      partyType: 'person',
      signature: { required: true }
    },
    seller: {
      label: 'Seller',
      partyType: 'any',
      signature: { required: true, witnesses: 2 }
    }
  }
})

// Builder pattern
const form = open.form()
  .name('purchase-agreement')
  .party('buyer', party()
    .label('Buyer')
    .partyType('person')
    .signature({ required: true })
    .build())
  .party('seller', party()
    .label('Seller')
    .signature({ required: true, witnesses: 2 })
    .build())
  .build()

API

Object Pattern

Create party definitions directly as objects:

const partyDef: FormParty = {
  label: 'Buyer',
  description: 'The purchasing party',
  partyType: 'person',
  min: 1,
  max: 1,  // default, single party
  required: true,
  signature: {
    required: true,
    witnesses: 1,
    notarized: false
  }
}

FormParty Properties

label: string
Human-readable role name
description?: string
Description of this role
partyType?: 'person' | 'organization' | 'any'
Constraint on party type
min?: number
Minimum parties required. Defaults to 1
max?: number
Maximum parties allowed. Defaults to 1. When max > 1, party data must be an array
required?: boolean | string
Whether this role is required. Can be an expression.
signature?: FormSignature
Signature requirements

FormSignature Properties

required?: boolean
Whether signature is required for this role
witnesses?: number
Number of witnesses required for this signature
notarized?: boolean
Whether at least one witness must be a notary

Builder Pattern

Chain methods to build a party incrementally:

All methods return PartyBuilder and are chainable.

party()
label: (value: string) => PartyBuilder
Set role display name (required)
description: (value: string) => PartyBuilder
Set role description
partyType: (value: 'person' | 'organization' | 'any') => PartyBuilder
Constrain party type
min: (value: number) => PartyBuilder
Set minimum parties required (default: 1)
max: (value: number) => PartyBuilder
Set maximum parties allowed (default: 1). When > 1, party data is an array
optional: () => PartyBuilder
Set min to 0, making this party optional
required: (value?: boolean | string) => PartyBuilder
Mark as required (default: true). Accepts expression.
signature: (value: FormSignature) => PartyBuilder
Set signature requirements
from: (value: FormParty) => PartyBuilder
Initialize from existing party
build: () => FormParty
Build and validate

Static Methods

parse: (input: unknown) => FormParty
Parse unknown input (throws on error)
safeParse: (input: unknown) => Result<FormParty>
Parse unknown input (returns result object)

Party Types

TypeDescription
personIndividual person (natural person)
organizationCompany, business, or legal entity
anyEither person or organization (default)

Signature Requirements

Use the signature property to specify requirements for this party's signature:

// Basic signature requirement
party()
  .label('Signer')
  .signature({ required: true })
  .build()

// With witnesses
party()
  .label('Borrower')
  .signature({ required: true, witnesses: 2 })
  .build()

// Notarized signature
party()
  .label('Affiant')
  .signature({ required: true, notarized: true })
  .build()

// Notarized with witness
party()
  .label('Declarant')
  .signature({ required: true, witnesses: 1, notarized: true })
  .build()

Multiple Parties

Allow multiple parties to fill a single role by setting max > 1:

const form = open.form()
  .name('joint-account')
  .party('accountHolders', party()
    .label('Account Holders')
    .min(1)   // At least 1 required
    .max(4)   // Up to 4 allowed (enables array format)
    .signature({ required: true })  // All must sign
    .build())
  .build()

When max > 1, the party data at runtime must be an array:

const filled = form.fill({
  fields: { /* ... */ },
  parties: {
    accountHolders: [
      { id: 'accountHolders-0', fullName: 'Jane Smith' },
      { id: 'accountHolders-1', fullName: 'John Smith' }
    ]
  }
})

Conditional Parties

Use logic expressions for conditional party requirements:

const form = open.form()
  .name('loan-application')
  .expr('needsGuarantor', 'loanAmount > 100000')
  .party('borrower', party()
    .label('Borrower')
    .signature({ required: true })
    .build())
  .party('guarantor', party()
    .label('Guarantor')
    .required('needsGuarantor')
    .signature({ required: true })
    .build())
  .build()

Runtime Party Types

At runtime, parties are represented using the Party and RuntimeParty types.

Party

The base Party type is a union of Person or Organization:

type Party = Person | Organization

Party type is inferred from shape:

  • Person: Has fullName property
  • Organization: Has name property (without fullName)

RuntimeParty

RuntimeParty extends Party with a required id field for tracking in the execution context:

type RuntimeParty = (Person | Organization) & {
  id: string  // Unique identifier for this party
}

The ID convention is {roleId}-{index}:

// Fill a form with party data (single parties)
const filled = form.fill({
  fields: { /* ... */ },
  parties: {
    buyer: {
      id: 'buyer-0',  // Required ID
      fullName: 'John Smith',
      firstName: 'John',
      lastName: 'Smith'
    },
    seller: {
      id: 'seller-0',  // Required ID
      legalName: 'Acme Corporation, Inc.',
      entityType: 'corporation'
    }
  }
})

You can also use the primitive builders with the id field:

const filled = form.fill({
  fields: { /* ... */ },
  parties: {
    buyer: {
      id: 'buyer-0',
      ...open.person().fullName('John Smith').build()
    },
    seller: {
      id: 'seller-0',
      ...open.organization()
        .name('Acme Corp')
        .legalName('Acme Corporation, Inc.')
        .build()
    }
  }
})

Related

On this page