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

captable

PATCH /v1/captable/classes/{id}

Amend a share class

Rewrites one share class — the amendment path for a class whose authorized count, price, seniority or preference terms have changed.

It REPLACES the class rather than merging into it: every field is taken from this body, so an omitted field resets to the create-time default instead of keeping its current value. Send the full class. The index and the derived prefix are unchanged by an amendment. An id that is not this company's is not found.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

ParameterInTypeRequiredDescription
idpathstringyes

GET /v1/captable/classes

Returns the caller org's share classes, in creation order. A share class is what a certificate is issued in, and every class the company has authorized appears. The response is a bare JSON array, not an envelope.

POST /v1/captable/classes

Define a share class

Creates a class of stock — its authorized share count, votes per share, par and issue price, seniority, conversion rights and liquidation/participation multiples — which is what shares, priced rounds and equity plans are then issued against.

Two fields are the company's to assign, not the caller's: the class index auto-increments per company, and the certificate prefix is DERIVED from the class type (CS for COMMON, PS for anything else), so a prefix in the body is ignored.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

GET /v1/captable/company

Returns the caller org's cap-table company record. The row is seeded when the tenant's store first opens, so it always exists; its name and incorporation details are set with PUT /v1/captable/company.

PUT /v1/captable/company

Sets the caller org's company name and incorporation details. The name is required; the three incorporation fields are optional and each is stored as empty when omitted, so a call that sends only a name CLEARS them. The company row itself is seeded when the tenant's store first opens, so this never creates one.

Request bodyapplication/json (required)

FieldTypeRequiredDescription
incorporationCountryIncorporationCountry is the ISO country the entity is incorporated in. Optional; omitted, null or empty clears it.
incorporationStateIncorporationState is the state or province of incorporation. Optional; omitted, null or empty clears it.
incorporationTypeIncorporationType is the entity kind, e.g. LLC or C_CORP. Optional; omitted, null or empty clears it.
nameName is the company's legal name. Required, and it must be a non-empty string — anything else is refused with the cap…

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. An id this org does not hold is not found.

ParameterInTypeRequiredDescription
idpathstringyesID is the convertible note to delete.

GET /v1/captable/convertibles

Returns the caller org's convertible notes, newest first. A note's principal sits OUTSIDE issued equity until it converts, so it is not part of the share counts.

POST /v1/captable/convertibles

Record a convertible note

Records a convertible note held by a stakeholder: the principal, the conversion cap, discount and interest rate, MFN, and the issue and board-approval dates.

The stakeholder must already exist in this company, and the note's public id must be unused there — a reused id is a conflict rather than an overwrite. Like a SAFE, this records the instrument only; conversion is not performed here.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

GET /v1/captable/investments

Returns the caller org's investments, newest first. It spans every round, so it is the flat ledger of cheques written into the company, each naming its investor and the round it went into.

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. An id this org does not hold is not found.

ParameterInTypeRequiredDescription
idpathstringyesID is the option grant to delete.

GET /v1/captable/options

Returns the caller org's option grants, newest first. Each row is joined to its grantee and its equity plan. Grants that are EXERCISED, EXPIRED or CANCELLED are listed here but do not dilute the cap table.

POST /v1/captable/options

Grant options from an equity plan

Records an option grant to a stakeholder under an equity plan — quantity, exercise price, ISO/NSO type, cliff and vesting years, and the issue, expiration, vesting-start, board-approval and Rule 144 dates.

The stakeholder and the equity plan must both already exist in this company, and the grant id must be unused there — a reused grant id is a conflict, so a grant can never be overwritten by a later one carrying the same number.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

GET /v1/captable/plans

Returns the caller org's equity plans, newest first. An equity plan is an option pool: a reserve of shares, drawn from one share class, that option grants are written against.

POST /v1/captable/plans

Open an equity incentive plan

Reserves a pool of shares out of a share class for option grants, with the board approval and effective dates and what happens to cancelled options.

The share class must already exist in this company — a plan cannot reserve out of nothing. Note the field name the bundle reads for the cancellation behaviour is defaultCancellatonBehavior; that spelling is the wire, and a correctly spelled key is simply not seen.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

POST /v1/captable/rounds/{id}/close

Closes one of the caller org's fundraising rounds, recording the close date and moving its status to CLOSED. Only an OPEN round can be closed: a round that is already closed — like an id this org does not hold — is not found. Closing a round does not change what was invested in it.

ParameterInTypeRequiredDescription
idpathstringyesID is the round to close. It is the path segment: the URL is the addressing authority, and the org it is resolved in…

Request bodyapplication/json (required)

FieldTypeRequiredDescription
closeDateCloseDate is the date to record the round as closed on. Optional: omitted, null or empty records TODAY.
idstringID is the round to close. It is the path segment: the URL is the addressing authority, and the org it is resolved in…

POST /v1/captable/rounds/{id}/investments

Record an investment into a round

Records what a stakeholder put into a round and adds it to the round's raised total.

On a PRICED round this ISSUES SHARES as well as recording the money: the amount is divided by the round's price per share, rounded DOWN to whole shares, and a new certificate for them is issued to the investor in the round's share class — so an amount too small to buy one whole share is refused rather than recorded as a zero-share investment. On a non-priced round the money is recorded and no shares are issued.

The round must exist in this company and still be OPEN — a closed round refuses further investment — and the investor must already be a stakeholder here. The date defaults to today when omitted.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

ParameterInTypeRequiredDescription
idpathstringyes

GET /v1/captable/rounds/{id}

Returns one of the caller org's fundraising rounds together with every investment written into it, oldest first. A round id that does not exist in the caller's org is not found — including one that exists in another tenant, since the org comes from the caller's principal and is part of the lookup.

ParameterInTypeRequiredDescription
idpathstringyesID is the round to read. It is the path segment: the URL is the addressing authority, and the org it is resolved in…

GET /v1/captable/rounds

Returns the caller org's fundraising rounds, newest first. A round groups a fundraising event; a PRICED round also carries the share class and price per share it issues at.

POST /v1/captable/rounds

Open a funding round

Opens a round with its name, type and target amount. It starts OPEN with nothing raised; investments are then added to it, and closing it is its own call.

A PRICED round is the constrained case: it requires a share class that exists in this company and a price per share above zero, because that price is what converts each investment into issued shares. Its pre-money valuation is optional. A non-priced round carries none of the three.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

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. An id this org does not hold is not found.

ParameterInTypeRequiredDescription
idpathstringyesID is the SAFE to delete.

GET /v1/captable/safes

Returns the caller org's SAFEs, newest first. A SAFE is a simple agreement for future equity: its capital sits OUTSIDE issued equity until it converts, so it is not part of the share counts.

POST /v1/captable/safes

Record a SAFE

Records a Simple Agreement for Future Equity held by a stakeholder: the capital in, the valuation cap and discount, MFN and pro-rata rights, pre- or post-money type, and the issue and board-approval dates.

The stakeholder must already exist in this company, and the SAFE's public id must be unused there — a reused id is a conflict rather than an overwrite. This records the instrument; it does not convert it, so nothing is issued against a share class until a round does that.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

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. An id this org does not hold is not found.

ParameterInTypeRequiredDescription
idpathstringyesID is the share certificate to delete.

POST /v1/captable/shares/transfer

Transfer shares to another stakeholder

Moves shares from one certificate to another stakeholder, in one atomic step.

OMITTING quantity transfers the WHOLE certificate, which simply reassigns it and answers newShareId null — that is the difference between a full and a partial transfer, and it is why quantity is absent rather than zero. A partial transfer shrinks the source certificate and issues a NEW one to the recipient, so it requires a certificateId for that new certificate and refuses a reused one. The quantity must be between 1 and what the source certificate actually holds; the recipient must be a stakeholder of this same company.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

GET /v1/captable/shares

Returns the caller org's share certificates, newest first. Each row is joined to its holder and its share class, so a certificate names who holds it and what class it is in without a second call.

POST /v1/captable/shares

Issue a share certificate

Issues shares of a class to a stakeholder as a certificate: quantity, price and capital contributed, the vesting cliff and term, the legends on the certificate, and the issue, Rule 144, vesting-start and board-approval dates.

Both the stakeholder and the share class must already exist in this company, and the certificate id must be unused there — a reused id is a conflict, never a silent overwrite of an existing certificate.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

PATCH /v1/captable/stakeholders/{id}

Changes one of the caller org's stakeholders. It is a PARTIAL update: only the fields the request names are written, and a field sent as null clears that column. A request that names no updatable field is refused, and an id this org does not hold is not found.

The values are stored as sent. Unlike adding a stakeholder, this route does not check the email's shape or the type and relationship vocabularies, so it can record a value that adding one would have rejected.

ParameterInTypeRequiredDescription
idpathstringyesID is the stakeholder to update. It is the path segment: the URL is the addressing authority, and the org it is…

Request bodyapplication/json (required)

FieldTypeRequiredDescription
cityCity is the stakeholder's city.
currentRelationshipCurrentRelationship is how the stakeholder relates to the company, e.g. FOUNDER, INVESTOR or EMPLOYEE.
emailEmail is the stakeholder's email. This route stores it as sent — unlike adding a stakeholder, it is not checked for…
idstringID is the stakeholder to update. It is the path segment: the URL is the addressing authority, and the org it is…
institutionNameInstitutionName names the institution, when the stakeholder is one.
nameName is the stakeholder's full name.
stakeholderTypeStakeholderType is INDIVIDUAL or INSTITUTION.
stateState is the stakeholder's state or province.
streetAddressStreetAddress is the stakeholder's street address.
taxIdTaxID is the stakeholder's tax identifier.
zipcodeZipcode is the stakeholder's postal code.

DELETE /v1/captable/stakeholders/{id}

Removes one of the caller org's stakeholders. It REFUSES to orphan issued equity: a holder that still holds share certificates or option grants cannot be deleted, and answers 400 saying so — release or transfer the holdings first. An id this org does not hold is not found.

ParameterInTypeRequiredDescription
idpathstringyesID is the stakeholder to delete.

GET /v1/captable/stakeholders

Returns the caller org's stakeholders, newest first. The response is a bare JSON array, not an envelope. Each row carries the holder's contact and address fields alongside the company's name.

POST /v1/captable/stakeholders

Add stakeholders to the cap table

Records the people and institutions that can hold equity — the rows every share, option, SAFE, note and investment is issued to.

The body is ONE stakeholder object or an ARRAY of them, and the array is the point: a whole roster loads in a single call. Email is the identity within the company, so a stakeholder whose email is already on the table is SKIPPED rather than duplicated or rejected — the 201 reports how many rows were actually inserted, which is what makes re-running an import safe. Validation is all-or-nothing across the batch: one bad entry refuses the whole array.

Writes the caller's OWN cap table: the org resolved from the validated principal selects the tenant's store and scopes every row, so there is no field by which a caller can write into another company's table; a request with no validated org is refused. The whole write runs in one transaction, so a refusal leaves nothing behind. Validation is the cap-table bundle's and so is its refusal: a bad body comes back as {success:false, message, errors:[…]} with the failing fields listed, and numeric fields accept a number OR a numeric string. Bodies are capped at 1 MiB.

GET /v1/captable/summary

Computes the caller org's cap table. It answers who owns what on a fully-diluted basis: outstanding shares, granted options, per-stakeholder ownership percentages, each share class's authorized versus issued position, and the capital sitting on SAFEs and convertible notes that have not yet converted. Only non-terminal option grants dilute — EXERCISED, EXPIRED and CANCELLED grants are excluded, so equity issued through an exercised option is never counted twice.


Captable guide · All Hanzo APIs · Interactive reference

How is this guide?

On this page