OpenForm

signing

Last updated on

Signers, signature capture, witnesses, and attestations

The signing flow in OpenForm separates the concepts of who can sign (Signers) from who they sign for (Parties). This enables scenarios where one person signs for multiple parties, or an authorized representative signs on behalf of an organization.

Signing Flow Overview

// 1. Fill form with party data
const draft = form.fill({
  fields: { /* ... */ },
  parties: {
    landlord: { id: 'landlord-0', legalName: 'ABC Property LLC' },
    tenant: { id: 'tenant-0', fullName: 'Jane Smith' }
  }
})

// 2. Register signers with their adopted signatures
const withSigners = draft
  .addSigner('jane', {
    person: { fullName: 'Jane Smith' },
    adopted: {
      signature: { image: 'data:image/png;base64,...', method: 'drawn' },
      initials: { method: 'typed' }
    }
  })
  .addSigner('bob', {
    person: { fullName: 'Bob Manager' },
    adopted: {
      signature: { image: 'data:image/png;base64,...', method: 'drawn' }
    }
  })

// 3. Link signers to parties (who signs for whom)
const ready = withSigners
  .addSignatory('tenant', 'tenant-0', { signerId: 'jane' })
  .addSignatory('landlord', 'landlord-0', { signerId: 'bob', capacity: 'Managing Member' })

// 4. Prepare for signing (freezes data)
const signable = ready.prepareForSigning()

// 5. Capture signatures at template locations
const signed = signable
  .captureSignature('tenant', 'tenant-0', 'jane', 'tenant-final-sig')
  .captureSignature('landlord', 'landlord-0', 'bob', 'landlord-final-sig')

// 6. Add witnesses and attestations
const witnessed = signed
  .addWitness({ id: 'witness-0', party: { fullName: 'Sam Witness' } })
  .addAttestation({
    witnessId: 'witness-0',
    signature: { image: 'data:...', method: 'drawn', timestamp: new Date().toISOString() },
    attestsTo: [
      { roleId: 'tenant', partyId: 'tenant-0', signerId: 'jane' }
    ]
  })

// 7. Finalize
const executed = witnessed.finalize()

Signer

A Signer represents a person who can sign documents, along with their adopted signature and initials.

interface Signer {
  person: Person              // Who is signing (always a Person)
  adopted?: {
    signature?: AdoptedSignature  // Adopted signature image
    initials?: AdoptedSignature   // Adopted initials image
  }
}
person: Person
The signer identity. Always a Person, never an Organization.
adopted?: { signature?, initials? }
Container for adopted signature and initials
adopted.signature?: AdoptedSignature
The adopted signature image
adopted.initials?: AdoptedSignature
The adopted initials image

AdoptedSignature

An AdoptedSignature is a reusable signature or initials image that a signer has approved.

interface AdoptedSignature {
  image?: string   // Base64-encoded image or data URI
  method: 'drawn' | 'typed' | 'uploaded' | 'certificate'
}
image?: string
Base64-encoded signature image or data URI
method: 'drawn' | 'typed' | 'uploaded' | 'certificate'
Method used to create this signature

PartySignatory

A PartySignatory links a signer to a party, specifying who signs for whom.

interface PartySignatory {
  signerId: string    // Reference to a Signer
  capacity?: string   // Role or title (CEO, Managing Member, etc.)
}
signerId: string
Reference to a signer ID in the signers registry
capacity?: string
Capacity in which they sign (CEO, Managing Member, Attorney-in-fact, etc.)

Example: One Signer, Multiple Parties

// Jane signs for herself as tenant AND as authorized representative for guarantor
const draft = form.fill({
  parties: {
    tenant: { id: 'tenant-0', fullName: 'Jane Smith' },
    guarantor: { id: 'guarantor-0', legalName: 'Smith Family Trust' }
  }
})
  .addSigner('jane', { person: { fullName: 'Jane Smith' }, adopted: { /* ... */ } })
  .addSignatory('tenant', 'tenant-0', { signerId: 'jane' })
  .addSignatory('guarantor', 'guarantor-0', { signerId: 'jane', capacity: 'Trustee' })

SignatureCapture

A SignatureCapture records when and where a signature was placed in the document.

interface SignatureCapture {
  role: string           // Party role ID (e.g., "landlord", "tenant")
  partyId: string        // The party ID
  signerId: string       // The signer ID
  locationId: string     // Template location identifier
  type: 'signature' | 'initials'
  timestamp: string      // ISO 8601 date-time
  image?: string         // Override image (if different from adopted)
  method?: 'drawn' | 'typed' | 'uploaded' | 'certificate'
}
role: string
The role ID of the party (e.g., "landlord", "tenant")
partyId: string
The party ID (user-supplied)
signerId: string
The signer ID (references signers registry)
locationId: string
Unique identifier for this location in the template
type: 'signature' | 'initials'
Whether this is a full signature or initials
timestamp: string
ISO 8601 date-time when the capture occurred
image?: string
Override image (if different from adopted signature)
method?: 'drawn' | 'typed' | 'uploaded' | 'certificate'
Override method

Capturing Signatures

const signable = draft.prepareForSigning()

// Capture using adopted signature
const signed = signable.captureSignature('tenant', 'tenant-0', 'jane', 'page-1-sig')

// Capture with override image (one-time signature)
const signed = signable.captureSignature('tenant', 'tenant-0', 'jane', 'page-1-sig', {
  image: 'data:image/png;base64,...',
  method: 'drawn'
})

// Capture initials
const initialed = signed.captureInitials('tenant', 'tenant-0', 'jane', 'page-1-init')

WitnessParty

A WitnessParty represents a witness who can attest to signatures.

interface WitnessParty {
  id: string          // Unique identifier
  party: Person       // Witness identity (always a Person)
  notary?: boolean    // Whether this witness is a notary public
}
id: string
Unique identifier for this witness
party: Person
The witness identity (always a Person, never an Organization)
notary?: boolean
Whether this witness is a notary public

Adding Witnesses

const signable = draft.prepareForSigning()

// Add a regular witness
const withWitness = signable.addWitness({
  id: 'witness-0',
  party: { fullName: 'Sam Witness' }
})

// Add a notary
const withNotary = withWitness.addWitness({
  id: 'notary-0',
  party: { fullName: 'Nancy Notary' },
  notary: true
})

Attestation

An Attestation records a witness's attestation to one or more party signatures.

interface Attestation {
  witnessId?: string           // Reference to declared witness
  witness?: WitnessParty       // Inline witness definition
  signature: Signature         // The witness's signature
  attestsTo: AttestationTarget[]  // Signatures being witnessed
}

interface AttestationTarget {
  roleId: string       // Role of the party being witnessed
  partyId: string      // Party ID
  signerId: string     // Signer ID
  locationId?: string  // Specific capture location (optional)
}

Attestation Properties

witnessId?: string
Reference to a pre-declared witness by ID
witness?: WitnessParty
Inline witness definition
signature: Signature
The witness signature
attestsTo: AttestationTarget[]
The party signatures this attestation witnesses

AttestationTarget Properties

roleId: string
The role ID of the party being witnessed
partyId: string
The party ID whose signature is being witnessed
signerId: string
The signer ID whose signature is being witnessed
locationId?: string
The specific capture location being witnessed

Adding Attestations

// Using a pre-declared witness
const attested = signable.addAttestation({
  witnessId: 'witness-0',
  signature: {
    image: 'data:image/png;base64,...',
    method: 'drawn',
    timestamp: new Date().toISOString()
  },
  attestsTo: [
    { roleId: 'tenant', partyId: 'tenant-0', signerId: 'jane' },
    { roleId: 'landlord', partyId: 'landlord-0', signerId: 'bob' }
  ]
})

// Using inline witness definition
const attested = signable.addAttestation({
  witness: { id: 'witness-1', party: { fullName: 'New Witness' } },
  signature: { /* ... */ },
  attestsTo: [{ roleId: 'tenant', partyId: 'tenant-0', signerId: 'jane' }]
})

Signature Status

Check signature progress using status methods on SignableForm:

// Get status for a specific role
const status = signable.getSignatureStatus('tenant')
// { required: true, captured: 1, total: 2, complete: false, witnesses: { required: 1, captured: 0 } }

// Get overall status
const overall = signable.getOverallSignatureStatus()
// { complete: false, roles: { tenant: {...}, landlord: {...} } }

Related

On this page