Developers
API reference
Deterministic startup-equity math over HTTP: convert SAFEs, model rounds, compute dilution and run exit waterfalls. Single API key, structured JSON errors, idempotent retries. This page is written to be a complete onboarding for a developer or an AI agent — no other docs required.
Authentication
Every request (except the discovery index and OpenAPI manifest) requires a single API key, sent as a bearer token. Create one from /dashboard/api-keys.
Authorization: Bearer fd_live_...A missing or invalid key returns 401 authentication_error. Keys are prefixed fd_live_... or fd_test_... — see “Safe by default” below.
Making a request
Every endpoint is a POST that accepts a JSON body and returns { data, request_id } on success. Every response — success or error — carries an x-request-id header you can use to reference a specific call in support requests.
curl -X POST https://foundily.com/api/v1/safe/convert \
-H "Authorization: Bearer fd_live_..." \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 3f1c2e9a-..." \
-d '{
"safe": { "investment": 200000, "valuationCap": 4000000 },
"round": { "preMoney": 16000000, "investment": 4000000 }
}'const res = await fetch("https://foundily.com/api/v1/safe/convert", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.FOUNDILY_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
safe: { investment: 200_000, valuationCap: 4_000_000 },
round: { preMoney: 16_000_000, investment: 4_000_000 },
}),
});
const { data, request_id } = await res.json();Endpoints
/safe/convertConvert a single SAFE (or convertible note) at a priced round.
| Param | Type | Description |
|---|---|---|
| safe* | Safe | The SAFE terms — investment, valuationCap, discount, type. |
| round* | PricedRound | The priced round the SAFE converts at — preMoney, investment. |
| preRoundFullyDilutedShares | number | Company's pre-round FD share count. Defaults to 10,000,000. |
| existingPoolShares | number | Shares already reserved in the option pool before the round. |
curl -X POST https://foundily.com/api/v1/safe/convert \
-H "Authorization: Bearer fd_live_..." \
-H "Content-Type: application/json" \
-d '{
"safe": { "investment": 200000, "valuationCap": 4000000, "type": "post" },
"round": { "preMoney": 16000000, "investment": 4000000 },
"preRoundFullyDilutedShares": 8000000
}'{
"data": {
"shares": 421053,
"ownership": 0.04,
"pricePerShare": 1.9,
"method": "cap",
"effectiveValuation": 4000000,
"roundPricePerShare": 1.9,
"capTable": { "rows": [ /* full post-round cap table */ ], "totalShares": 10526316 }
},
"request_id": "req_9Xn2kP..."
}/rounds/modelModel a priced round: SAFE/note conversion plus an option-pool top-up.
| Param | Type | Description |
|---|---|---|
| founders* | Holder[] | Pre-round common holders — name + shares. |
| optionPoolShares | number | Existing option-pool shares before the round. Default 0. |
| safes | Safe[] | SAFEs/notes converting alongside the round. Default []. |
| round* | PricedRound | preMoney, investment, targetOptionPool, and preferred terms. |
curl -X POST https://foundily.com/api/v1/rounds/model \
-H "Authorization: Bearer fd_live_..." \
-H "Content-Type: application/json" \
-d '{
"founders": [{ "name": "Founders", "shares": 9000000 }],
"optionPoolShares": 0,
"safes": [],
"round": {
"name": "Series Seed", "preMoney": 8000000, "investment": 2000000,
"targetOptionPool": 0.10
}
}'{
"data": {
"rows": [
{ "name": "Founders", "security": "common", "shares": 9000000, "ownership": 0.70 },
{ "name": "Option Pool", "security": "option", "shares": 1285714, "ownership": 0.10 },
{ "name": "Series Seed", "security": "preferred", "shares": 2571429, "ownership": 0.20,
"pricePerShare": 0.7778, "invested": 2000000 }
],
"totalShares": 12857143,
"pricePerShare": 0.7778,
"optionPoolShares": 1285714,
"newInvestorShares": 2571429
},
"request_id": "req_9Xn2kP..."
}/dilutionRun a multi-round dilution scenario across SAFEs and priced rounds.
| Param | Type | Description |
|---|---|---|
| founders* | Holder[] | Initial common holders. |
| optionPoolShares | number | Initial option-pool shares. Default 0. |
| events* | ScenarioEvent[] | Ordered list of { kind: "safe", safe } or { kind: "round", round } events. |
curl -X POST https://foundily.com/api/v1/dilution \
-H "Authorization: Bearer fd_live_..." \
-H "Content-Type: application/json" \
-d '{
"founders": [{ "name": "Founders", "shares": 8000000 }],
"events": [
{ "kind": "safe", "safe": { "name": "Pre-seed SAFE", "investment": 200000, "valuationCap": 4000000 } },
{ "kind": "round", "round": { "name": "Series A", "preMoney": 16000000, "investment": 4000000 } }
]
}'{
"data": {
"stages": [ { "label": "Series A", "capTable": { /* cap table */ }, "pricePerShare": 1.9 } ],
"final": { "rows": [ /* final cap table rows */ ], "totalShares": 10526316 },
"founderTrail": [ { "label": "Series A", "ownership": 0.76 } ]
},
"request_id": "req_9Xn2kP..."
}/waterfallCompute an exit / liquidation waterfall across preferred series.
| Param | Type | Description |
|---|---|---|
| exitValue* | number | Total exit / sale proceeds. |
| commonShares* | number | Common shares outstanding. |
| optionShares | number | Option-pool shares participating in the residual. |
| series* | WaterfallSeries[] | Preferred series — invested, shares, liquidationMultiple, participating, participationCap, seniority. |
curl -X POST https://foundily.com/api/v1/waterfall \
-H "Authorization: Bearer fd_live_..." \
-H "Content-Type: application/json" \
-d '{
"exitValue": 40000000,
"commonShares": 6000000,
"series": [
{ "name": "Series A", "invested": 3000000, "shares": 2000000,
"liquidationMultiple": 1, "participating": false, "seniority": 1 },
{ "name": "Series B", "invested": 5000000, "shares": 3000000,
"liquidationMultiple": 1, "participating": true, "seniority": 2 }
]
}'{
"data": {
"payouts": [
{ "name": "Common", "security": "common", "proceeds": 19090909, "shares": 6000000 },
{ "name": "Series A", "security": "preferred", "converted": true,
"proceeds": 6363636, "multiple": 2.12, "shares": 2000000 },
{ "name": "Series B", "security": "preferred", "converted": false,
"proceeds": 14545455, "multiple": 2.91, "shares": 3000000 }
],
"exitValue": 40000000,
"totalDistributed": 40000000
},
"request_id": "req_9Xn2kP..."
}Errors
Errors are structured JSON with a stable, machine-parseable error.type — never scrape the human-readable message.
{
"error": {
"type": "invalid_request",
"message": "Expected number, received string",
"param": "safe.investment",
"docs": "/docs/api#errors"
},
"request_id": "req_9Xn2kP..."
}| error.type | HTTP status | When it happens |
|---|---|---|
| invalid_request | 400 | The request body failed validation — check the error's `param` field for which one. |
| authentication_error | 401 | Missing, malformed, or revoked API key. |
| not_found | 404 | The requested resource doesn't exist. |
| rate_limit_exceeded | 429 | Monthly request quota reached (free tier hard-caps; Pro overages bill per call instead). |
| server_error | 500 | Something failed on our end. Safe to retry with the same Idempotency-Key. |
Rate limits & quotas
Every response carries x-ratelimit-limit and x-ratelimit-remaining, reflecting your plan's monthly quota. Free hard-caps at quota (429 rate_limit_exceeded); Pro continues past its included quota at $0.01/call.
| Plan | Included / month | Over quota |
|---|---|---|
| Free | 100 | Hard cap — 429 until next month |
| Pro | 10,000 | $0.01/call, billed monthly |
Idempotency
Send an Idempotency-Key header (any unique string — a UUID works well) to make retries safe. If you retry the same request with the same key, Foundily replays the original response instead of recomputing it, and marks it with an idempotent-replayed: true response header. Since every calculation here is deterministic, replays cost nothing extra against your quota.
Discovery for agents
Three machine-readable entry points, so an agent can self-onboard without a human reading this page:
GET https://foundily.com/api/v1— API index: endpoints, auth, and machine-readable pricing.GET https://foundily.com/api/v1/openapi.json— full OpenAPI 3.1 manifest.- GET https://foundily.com/llms.txt — plain-text onboarding for LLMs.
- MCP server —
npx @foundily/mcp, four tools, zero HTTP code required.
Safe by default
Every key is prefixed fd_live_... or fd_test_...— visible at a glance in your dashboard, your logs, and this page's samples — so a key pasted into an agent prompt or a shared snippet is never ambiguous about which environment it belongs to. Every endpoint is a pure function over its input: there is no destructive action to gate behind confirmation, and the same request with the same body always produces the same numbers, live or test.