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

An empty list for a max > 1 role counts as an absent role. A role whose required is false, or whose required expression evaluates to false, may be absent or empty; min then applies only to a list that names parties.

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')
  .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 = p.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 = p.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
payment?: FormPayment
Payment requirement

FormSignature Properties

required?: boolean
Whether signature is required for this role
witnesses?: number
Number of caller-declared witness attestations required for this signature
notarized?: boolean
Whether an attesting witness must be declared with notary status

FormPayment Properties

required?: boolean
Whether payment is required for this role
amount: Money | MoneyExpression
Amount owed: a fixed Money value, or a MoneyExpression resolved from filled data at request time

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
required: (value?: boolean | string) => PartyBuilder
Mark as required (default: true). Accepts expression.
signature: (value: FormSignature) => PartyBuilder
Set signature requirements
payment: (value: FormPayment) => PartyBuilder
Set the payment requirement
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 = p.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', name: 'Jane Smith' },
      { id: 'accountHolders-1', name: 'John Smith' }
    ]
  }
})

Conditional Parties

Use defs for conditional party requirements:

const form = p.form()
  .name('loan-application')
  .field('loanAmount', field.number().label('Loan amount').build())
  .def('needsGuarantor', 'fields.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 name property (a natural person)
  • Organization: Has name property plus legalName, entityType, etc.

RuntimeParty

RuntimeParty extends Party with the id a filled form gives each party. Signatories, captures, and attestations reference a party by it:

type RuntimeParty = (Person | Organization) & {
  id: string  // `<roleId>-<index>`, assigned by core
}

The id is always {roleId}-{index}, the party's position in its role. fill, update, setParty, addParty, and runtimeFormFromJSON assign it, so you can leave it out. An id you do supply must match that position, or the call throws. removeParty gives each later party the id of its new position and moves its signatories with it.

A party record holds only its Person or Organization members. A member the schema does not declare, such as address or phone, is refused. Declare those as fields beside the party instead; the Party component prints them by path.

// Fill a form with party data (single parties)
const filled = form.fill({
  fields: { /* ... */ },
  parties: {
    buyer: {
      id: 'buyer-0',  // Optional: core assigns it
      name: 'John Smith',
      firstName: 'John',
      lastName: 'Smith'
    },
    seller: {
      id: 'seller-0',  // Optional: core assigns it
      name: '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',
      ...p.person().name('John Smith').build()
    },
    seller: {
      id: 'seller-0',
      ...p.organization()
        .name('Acme Corp')
        .legalName('Acme Corporation, Inc.')
        .build()
    }
  }
})

On this page