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
capTableContractIdThe active CapTable contract ID.
actAsSigning parties — must include the issuer.
readAsExtra parties for visibility during resolution.
capTableContractDetails.templateIdOverride 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.