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' },
});
| Field | Required | Notes |
|---|---|---|
| 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.
| Method | Purpose |
|---|---|
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 / isEmpty | Inspect queue length. |
Executing
| Method | Purpose |
|---|---|
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
| Scenario | Error |
|---|---|
| No operations queued | OcpValidationError('batch') |
undefined in payload | OcpValidationError('batch.payload') |
| Invalid entity type | OcpValidationError('type') |
| Issuer create/delete attempted | OcpValidationError('type') |
| Ledger submission failure | OcpContractError 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.