Skip to content

API Errors & Validation

All non-2xx responses return a JSON error body. This page covers the error format, HTTP status codes, and common validation rules enforced across the API.


Every error response returns a JSON object with an error field:

{
"error": "journal entry lines do not balance: debits=5000000, credits=3000000"
}

The error string is human-readable and suitable for display. Machine consumers should rely on the HTTP status code to determine the error category.


CodeMeaningWhen it happens
200OKSuccessful request with response body
204No ContentSuccessful request with no response body
400Bad RequestValidation error — empty names, negative amounts, invalid state transitions, unbalanced journal entries
401UnauthorizedMissing or invalid Bearer token / API key
403ForbiddenAPI key lacks the required scope for this operation
404Not FoundEntity, document, or resource does not exist
409ConflictDuplicate resource (e.g., duplicate email, duplicate instrument symbol) or concurrency conflict
429Too Many RequestsRate limit exceeded — back off and retry
500Internal Server ErrorUnexpected server error — report to support

All name fields (entity names, account names, holder names, contact names) must be:

  • Between 1 and 500 characters
  • Non-empty after whitespace trimming
{"error": "name must not be empty"}

Instrument symbols (e.g., CS, SAFE-1) must be:

  • Between 1 and 50 characters
  • Alphanumeric characters, hyphens, underscores, and spaces only
{"error": "symbol contains invalid characters"}

Jurisdiction codes must be a 2-letter uppercase US state code:

"DE", "CA", "WY", "NY", "TX", ...
{"error": "jurisdiction must be a 2-letter US state code"}

All monetary values are in whole cents as positive integers. There are no fractional cents.

Dollar amountCents value
$50,000.005000000
$15,000.001500000
$2,500.00250000
$0.011

Amounts must be positive:

{"error": "amount_cents must be positive"}

Share counts must be positive integers:

{"error": "shares must be a positive integer"}

The entity_type field accepts exactly two values:

  • "c_corp" — C-Corporation
  • "llc" — Limited Liability Company
{"error": "entity_type must be 'c_corp' or 'llc'"}

Equity grants cannot exceed the instrument’s authorized_units. If you try to issue 8,000,000 shares on an instrument authorized for 10,000,000 that already has 5,000,000 issued, the request fails:

{"error": "grant would exceed authorized units: requested=8000000, available=5000000"}

To fix this, either reduce the grant size or increase the instrument’s authorized units (which typically requires a board resolution).

The cap_table_id referenced in equity operations must belong to the entity in the URL path. Cross-entity references are rejected:

{"error": "cap_table_id does not belong to this entity"}

Write operations on dissolved entities are rejected with a 400 error. Once an entity is dissolved via POST /v1/entities/{entity_id}/dissolve, it becomes read-only:

{"error": "entity is dissolved; write operations are not permitted"}

Read operations (GET requests) continue to work on dissolved entities.

Share transfers require the sender to hold enough shares. If the sender’s position is insufficient, the transfer is rejected:

{"error": "sender does not have enough shares: requested=1000000, available=500000"}

Resources with lifecycle state machines (invoices, journal entries, bank accounts, payroll runs, formations) reject invalid transitions:

{"error": "cannot void an invoice in 'paid' status"}
{"error": "cannot post a journal entry in 'voided' status"}

The corp CLI prints the error message to stderr and exits with a non-zero status code:

Terminal window
$ corp finance create-account --account-code "" --account-name "" --currency usd
Error: name must not be empty

Use --json to get structured error output for scripting:

Terminal window
$ corp finance create-account --account-code "" --account-name "" --currency usd --json
{"error": "name must not be empty"}

Check the HTTP status code to detect errors:

Terminal window
response=$(curl -s -w "\n%{http_code}" -X POST \
http://localhost:8000/v1/entities/$ENTITY_ID/grants \
-H "Authorization: Bearer $CORP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"cap_table_id": "'$CAP_TABLE_ID'",
"instrument_id": "'$INSTRUMENT_ID'",
"recipient_name": "Jane Smith",
"grant_type": "common_stock",
"shares": 99000000
}')
http_code=$(echo "$response" | tail -1)
body=$(echo "$response" | head -1)
if [ "$http_code" -ne 200 ]; then
echo "Error ($http_code): $body"
fi

When rate limited, the response includes a Retry-After header with the number of seconds to wait:

Terminal window
HTTP/1.1 429 Too Many Requests
Retry-After: 5
{"error": "rate limit exceeded"}

Back off for the indicated duration before retrying.