Hanzo

Captable

Package captable is your cap table: stakeholders, share classes, grants, SAFEs, rounds, and who owns what.

Package captable is your cap table: stakeholders, share classes, grants, SAFEs, rounds, and who owns what.

Base URLhttps://api.hanzo.ai
Operations31
AuthAuthorization: Bearer $HANZO_API_KEY

Specification

HIP-1111 · Captable — Who Owns What — Draft · read the specification →

/v1/captable is a company's cap table: stakeholders, share classes, share certificates and transfers, option grants and equity plans, SAFEs and convertible notes, priced rounds and their investments, and the summary that totals outstanding and fully-diluted ownership from them. It is implemented in hanzoai/cloud apps/captable. This HIP states the design worth holding to: the business logic is a ported bundle that carries no storage, the Go host is storage that carries no business logic, and each tenant's table is its own database file.

Motivation

The predecessor was a standalone application — Next.js, Prisma, Postgres — that duplicated the platform's identity, storage and deployment for one vertical. The fold retires that deployment entirely (apps/captable/captable.go:28-33): cloud's per-tenant store is authoritative from the first write, with no data to migrate and no second pod to keep honest.

Specification

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.

§1 Logic and storage are two artifacts

The cap-table logic — validation, conversion math, round mechanics — lives in a self-contained bundle, github.com/hanzoai/captable (v1.0.0 in go.mod:140), executed by the reusable goja host (apps/goja, over dop251/goja, MIT). The Go host injects exactly three primitives — __db, __newId, __now — and gives each request one SQLite transaction (apps/captable/captable.go:9-22). The bundle MUST NOT reach storage except through them, and the host MUST NOT reimplement a rule the bundle carries: one owner per concern, so the math cannot drift between two copies.

§2 The store

One SQLite file per tenant, selected by the validated org, under the host's data directory. The org selects the file AND scopes every row (apps/captable/captable.go:24-26): a cross-tenant read requires both a wrong file and a wrong predicate, which is the point of paying for two mechanisms. The per-tenant schema is seeded on first touch (apps/captable/schema.go:224).

§3 The addresses

Everything is under /v1/captable: company, stakeholders, classes, shares (and shares/transfer), plans, options, safes, convertibles, rounds (with close and investments), investments, and summary — collection and record routes composed on one group from the single prefix. Operations are typed through the registry; the wire tests (apps/captable/typed_test.go, bodyless_test.go) hold the surface to it.

§4 The in-process seam

apps/captable/facade.go lets a sibling subsystem — company formation, import and fundraising flows — write to a tenant's cap table without an HTTP hop, by dispatching the same bundle routes the handlers do. The caller MUST pass an already-validated org (apps/captable/facade.go:15-19); the facade grants no authority the HTTP path does not, it only removes the hop.

§5 Tenancy, money, events, telemetry, stage

Every route resolves the org from the validated principal (principal.Org / principal.Acting, HIP-0026), never a client header (apps/captable/captable.go:359-361); no principal, no answer. The capability is free (plugin/captable/main.go:21, cloud.Free). It publishes no events on the bus. It emits nothing to observability beyond the request span. Its stage is beta: a vertical application, not the agentic-OS core.

§6 Upstreams

dop251/goja (MIT) is embedded as the bundle interpreter, through the shared apps/goja host. hanzoai/captable is our own module, a port of the retired application's tRPC logic. Storage is the hanzoai/sqlite facade like every store in the binary. Nothing else is forked or mirrored.

Rationale

The alternative to the bundle-plus-host split is a rewrite of the cap-table math in Go. That is a second implementation of conversion and dilution rules to keep in agreement with the one that was already trusted, and cap-table math is exactly where a quiet divergence costs the most. Porting the logic whole and giving it persistence keeps one implementation; the goja host is the price, and it is shared with esign and dataroom rather than paid three times (apps/captable/captable.go:14-16).

Security Considerations

A cap table is the ownership record of a company: who holds what, at what price, under what terms. A wrong implementation leaks a competitor's round to a tenant, or worse, lets one tenant write another's ledger — a forged transfer is a forged ownership claim. Tenancy is therefore physical (a file per tenant) plus scoped (org on every row), and the org is never an input on any HTTP route. The facade is the one path that takes org as a parameter, which is why its contract requires the caller to have validated it first; a caller that passes an unvalidated org has recreated the client-supplied-header hole in-process.

Four surfaces

SurfaceReaches this capability asCoverage
RESTcaptable at its own prefix31 operations
CLIhanzo captable …31 of 31
SDKCaptableApi in every published client31 methods
MCPtool captable on https://api.hanzo.ai/v1/mcp31 operations, 2 under the document's own id — ask describe for the rest

Quickstart

export HANZO_API_KEY=sk-...   # console.hanzo.ai → API keys

Then the first call — a read that needs nothing but the key. GET /v1/captable/plans, operation get_captable_plans:

hanzo captable plans get

Answers 200 with object — ok.

Endpoints

EndpointWhat it does
PATCH /v1/captable/classes/{id}Amend a share class
GET /v1/captable/classesReturns the caller org's share classes, in creation order.
POST /v1/captable/classesDefine a share class
GET /v1/captable/companyReturns the caller org's cap-table company record.
PUT /v1/captable/companySets the caller org's company name and incorporation details.
DELETE /v1/captable/convertibles/{id}Removes one of the caller org's convertible notes, taking its principal out of the cap table's unconverted-instrument totals.
GET /v1/captable/convertiblesReturns the caller org's convertible notes, newest first.
POST /v1/captable/convertiblesRecord a convertible note
GET /v1/captable/investmentsReturns the caller org's investments, newest first.
DELETE /v1/captable/options/{id}Removes one of the caller org's option grants, taking its shares out of the cap table's granted-options and fully-diluted counts.
GET /v1/captable/optionsReturns the caller org's option grants, newest first.
POST /v1/captable/optionsGrant options from an equity plan
GET /v1/captable/plansReturns the caller org's equity plans, newest first.
POST /v1/captable/plansOpen an equity incentive plan
POST /v1/captable/rounds/{id}/closeCloses one of the caller org's fundraising rounds, recording the close date and moving its status to CLOSED.
POST /v1/captable/rounds/{id}/investmentsRecord an investment into a round
GET /v1/captable/rounds/{id}Returns one of the caller org's fundraising rounds together with every investment written into it, oldest first.
GET /v1/captable/roundsReturns the caller org's fundraising rounds, newest first.
POST /v1/captable/roundsOpen a funding round
DELETE /v1/captable/safes/{id}Removes one of the caller org's SAFEs, taking its capital out of the cap table's unconverted-instrument totals.
GET /v1/captable/safesReturns the caller org's SAFEs, newest first.
POST /v1/captable/safesRecord a SAFE
DELETE /v1/captable/shares/{id}Removes one of the caller org's share certificates, taking its shares out of the cap table's outstanding and fully-diluted counts.
POST /v1/captable/shares/transferTransfer shares to another stakeholder
GET /v1/captable/sharesReturns the caller org's share certificates, newest first.
POST /v1/captable/sharesIssue a share certificate
PATCH /v1/captable/stakeholders/{id}Changes one of the caller org's stakeholders.
DELETE /v1/captable/stakeholders/{id}Removes one of the caller org's stakeholders.
GET /v1/captable/stakeholdersReturns the caller org's stakeholders, newest first.
POST /v1/captable/stakeholdersAdd stakeholders to the cap table
GET /v1/captable/summaryComputes the caller org's cap table.

All Hanzo APIs · Interactive reference

How is this guide?

On this page