Skip to content

Reference

Batch updates (CapTableBatch)

Queue multiple OCF creates, edits, and deletes and execute them atomically via a single UpdateCapTable exercise.

capTable.update starts a CapTableBatch — a fluent builder that queues creates, edits, and deletes and submits them all in one atomic UpdateCapTable transaction.

Starting a batch

const batch = ocp.OpenCapTable.capTable.update({
  capTableContractId: 'CAP_TABLE_CONTRACT_ID',
  actAs: ['issuer::party'],
  readAs: ['observer::party'], // optional
  capTableContractDetails: { templateId: 'optional-if-package-diverges' },
});
FieldRequiredNotes
capTableContractId✓The active CapTable contract ID.
actAs✓Signing parties — must include the issuer.
readAs—Extra parties for visibility during resolution.
capTableContractDetails.templateId—Override when your deployment uses a different package hash.

Queuing operations

Chain create, edit, and delete before executing.

MethodPurpose
create(type, data)Queue an OCF create. Accepts any entity type except issuer (issuers are created via issuer.buildCreate).
edit(type, data)Queue an edit. data.id must match the live OCF id on-chain.
delete(type, id)Queue a delete by OCF id. issuer deletes are blocked.
clear()Reset the queue to reuse the builder. Returns this.
size / isEmptyInspect queue length.

Executing

MethodPurpose
execute()Build and submit in one call. Returns { updateId, updatedCapTableCid }.
build()Build the command without submitting. Returns { command, disclosedContracts } for manual submission.

Always rotate capTableContractId to updatedCapTableCid after each execute (or call ocp.context.setCapTableContractId(updatedCapTableCid)).


Example

const { updateId, updatedCapTableCid } = await ocp.OpenCapTable.capTable
  .update({
    capTableContractId: 'CAP_TABLE_CONTRACT_ID',
    actAs: ['issuer::party'],
  })
  .create('stakeholder', {
    id: 'sh_alice',
    object_type: 'STAKEHOLDER',
    name: { legal_name: 'Alice Example' },
    stakeholder_type: 'INDIVIDUAL',
    current_relationship: 'EMPLOYEE',
  })
  .create('stockClass', {
    id: 'sc_common',
    object_type: 'STOCK_CLASS',
    name: 'Common Stock',
    class_type: 'COMMON',
    default_id_prefix: 'CS-',
    initial_shares_authorized: '10000000',
    votes_per_share: '1',
    par_value: { amount: '0.01', currency: 'USD' },
    price_per_share: { amount: '0.10', currency: 'USD' },
    seniority: '1',
  })
  .execute();

Errors

ScenarioError
No operations queuedOcpValidationError('batch')
undefined in payloadOcpValidationError('batch.payload')
Invalid entity typeOcpValidationError('type')
Issuer create/delete attemptedOcpValidationError('type')
Ledger submission failureOcpContractError with CHOICE_FAILED

Note: Canton rejects undefined in payloads — use null for optional fields.


Low-level variant: buildUpdateCapTableCommand

For cases where you need to compose the UpdateCapTable command with other Canton commands in the same transaction, use the package-level buildUpdateCapTableCommand export directly:

import { buildUpdateCapTableCommand } from '@open-captable-protocol/canton';

const { command, disclosedContracts } = buildUpdateCapTableCommand(
  { capTableContractId: 'CAP_TABLE_CONTRACT_ID' },
  {
    creates: [{ type: 'stakeholder', data: stakeholderData }],
    edits: [],
    deletes: [],
  },
);
// compose with other commands and submit manually

Throws match CapTableBatch. Source: CapTableBatch.ts.