Net Present Value
finance.npv
Overview
You send cashflows numbered by period and one per-period discount rate; we return the net present value in exact decimals, plus the discount factor and the discounted amount behind every period so the total can be checked line by line. Periods are ordinals, not dates: period 0 is undiscounted. We never look a rate up for you and never pick a day-count convention. Use finance.irr when you want the rate that makes this zero.
A discounted cashflow done in a spreadsheet hides its factors and rounds where the cell format happens to fall, so two people get two answers. You keep the rate and the periods; we do the arithmetic in exact decimals and hand back every factor we used.
Reliability
Same series, same rate, same answer — no clock, no rate lookup, and the order you list cashflows in is irrelevant because we sort by period first. Factors and discounted amounts come back unrounded to 12 decimals so they add up to npv; only npv itself is rounded, and only when you gave us a scale to round to. NPV_CASHFLOWS_INVALID — trigger: an empty series, or an entry whose period is missing or negative, whose amount we cannot read as a plain decimal, or that carries a property we do not define; retryable: no, the same payload always fails; next: check the position we name in the details and send period as a whole number from 0 and amount as a plain decimal string. NPV_PERIOD_DUPLICATE — trigger: two cashflows claim the same period, so it is ambiguous which one to discount; retryable: no; next: add the amounts for that period together and send one entry. NPV_RATE_INVALID — trigger: a rate we cannot read, or one at or below -1 where the discount factor would divide by zero; retryable: no; next: send a plain decimal such as "0.05" for five percent, with no percent sign, separators or exponent. NPV_CASHFLOW_LIMIT_EXCEEDED — trigger: more than 1,200 cashflows in one call, whether you overshoot by one entry or by a thousand — the limit is ours and it is the rule you are told you broke, with your actual count in the details; retryable: no; next: split the series, value each part, and add the present values yourself. AMOUNT_MAGNITUDE_EXCEEDED — trigger: an amount beyond the 1E18 cap we can hold exactly; retryable: no; next: scale your series to thousands or millions and read the result in the same unit. CURRENCY_UNKNOWN — trigger: a currency code we do not support; retryable: no; next: send one of the supported ISO 4217 codes, or leave currency out and round the exact value yourself.
Context footprint
Returns one value plus one factor row per period, so a 12-period series stays small enough to keep in a conversation. A successful call costs 1 execution credit.
When to use it
When you already know the per-period rate and each cashflow's period, and you want a value you can defend line by line.
When you still need to turn dates into periods, convert currencies, or find the rate that brings the series to zero.
Scope
Non-goals
- Does not fetch or infer a discount rate, an inflation figure or a hurdle rate
- Does not convert dates to periods or choose a day-count convention
- Does not mix currencies in one series or convert between them
- Does not solve for the rate — that is finance.irr
Properties
Capabilities
Cost & billing
Flat rate per invocation
File handling
How we handle your data
What this means
This skill may receive sensitive content, but it does not target sensitive data and returns only a structured non-sensitive result.
Squillify guarantees
- Runs inside Squillify
- This skill runs inside Squillify.
- Payloads are not sent to an external service by this skill.
- Squillify does not permanently store submitted payloads.
- Telemetry records metadata only, not submitted content.
- Submitted payloads are not used for training.
- Submitted payloads are not reviewed by humans.
- This skill does not target sensitive data.
- The response does not contain submitted content.
Your responsibility
Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.
Input schema
| Field | Type | Required | Description |
|---|---|---|---|
| cashflows | array | Yes | From 1 through 1,200 closed cashflows, each with a unique non-negative period and a signed decimal amount under 1E18. |
| discountRate | object | Yes | Per-period discount rate as a decimal such as 0.05 for five percent, greater than -1, as a string or JSON number. |
| currency | string | No | ISO 4217 code used to round the NPV to minor units; omit it for the exact unrounded value. |
| rounding | object | No | Optional mode and scale for the returned NPV; omit it and a currency's minor units decide, or nothing is rounded. |
Output schema
| Field | Type | Required | Description |
|---|---|---|---|
| npv | string | Yes | Net present value as a decimal string, rounded only when a currency or rounding choice set the scale. |
| discountRate | string | Yes | Per-period rate the answer was actually produced at, normalized without trailing zeros. |
| periods | integer | Yes | How many cashflows were discounted after ordering by period. |
| discountFactors | array | Yes | One factor row per cashflow ordered by period, each with discountFactor and discountedAmount so the total is auditable. |
| currency | string | No | ISO 4217 unit the published NPV is expressed in; omitted when you sent no currency. |
| rounding | object | No | Mode and scale actually applied to npv after currency defaults filled any omitted fields. |
API contract
POST /v1/executecurl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"finance.npv","input":{"cashflows":[{"period":0,"amount":"-1000.00"},{"period":1,"amount":"500.00"},{"period":2,"amount":"500.00"},{"period":3,"amount":"500.00"}],"discountRate":"0.25","currency":"EUR"}}'import { Squillify } from "@squillify/sdk";
const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });
const { output } = await sq.execute("finance.npv", {"currency":"EUR","discountRate":"0.25","cashflows":[{"period":0,"amount":"-1000.00"},{"period":1,"amount":"500.00"},{"period":2,"amount":"500.00"},{"period":3,"amount":"500.00"}]});Errors
| Code | HTTP | Retryable | Description |
|---|---|---|---|
| VALIDATION_ERROR | 400 | No | The request payload failed schema validation. |
| UNAUTHORIZED | 401 | No | Missing or invalid API key. |
| SKILL_NOT_ACCESSIBLE | 403 | No | The API key is not granted access to this skill. |
| SKILL_NOT_AVAILABLE_FOR_PLAN | 403 | No | This skill is not included in your current plan. |
| PLAN_ELIGIBILITY_UNAVAILABLE | 503 | Yes | Plan eligibility could not be decided. Retry after backoff. |
| SKILL_NOT_FOUND | 404 | No | The requested skill or version does not exist. |
| TENANT_RATE_LIMITED | 429 | Yes | Request rate limit exceeded. Retry after backoff. |
| QUOTA_EXCEEDED | 429 | Yes | Plan quota exhausted. Retryable once the quota period resets. |
| EXECUTION_TIMEOUT | 504 | Yes | Execution exceeded the allowed time budget. |
| NPV_CASHFLOWS_INVALID | 400 | No | The request payload failed this skill's input validation. |
| NPV_PERIOD_DUPLICATE | 400 | No | The request payload failed this skill's input validation. |
| NPV_RATE_INVALID | 400 | No | The request payload failed this skill's input validation. |
| NPV_CASHFLOW_LIMIT_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| AMOUNT_MAGNITUDE_EXCEEDED | 429 | No | A size, quota, rate, or time limit was exceeded. |
| CURRENCY_UNKNOWN | 400 | No | The request payload failed this skill's input validation. |
Other limits
| Limit | Value |
|---|---|
| Recommended inline bytes | 32 KiB |
Limitations
- Does not fetch or infer a discount rate, an inflation figure or a hurdle rate
- Does not convert dates to periods or choose a day-count convention
- Does not mix currencies in one series or convert between them
- Does not solve for the rate — that is finance.irr
- Irregular calendar dates you have not yet converted to periods
- Cashflows in several currencies
- Finding the break-even rate of a series
How to call
POST /v1/execute (requires authentication)
{
"skillKey": "finance.npv",
"input": {
"currency": "EUR",
"discountRate": "0.25",
"cashflows": [
{
"period": 0,
"amount": "-1000.00"
},
{
"period": 1,
"amount": "500.00"
},
{
"period": 2,
"amount": "500.00"
},
{
"period": 3,
"amount": "500.00"
}
]
}
}Examples
Three yearly inflows against a 25 percent hurdle rate
{
"currency": "EUR",
"discountRate": "0.25",
"cashflows": [
{
"period": 0,
"amount": "-1000.00"
},
{
"period": 1,
"amount": "500.00"
},
{
"period": 2,
"amount": "500.00"
},
{
"period": 3,
"amount": "500.00"
}
]
}{
"rounding": {
"scale": 2,
"mode": "HALF_UP"
},
"currency": "EUR",
"discountFactors": [
{
"period": 0,
"discountFactor": "1",
"discountedAmount": "-1000"
},
{
"period": 1,
"discountFactor": "0.8",
"discountedAmount": "400"
},
{
"period": 2,
"discountFactor": "0.64",
"discountedAmount": "320"
},
{
"period": 3,
"discountFactor": "0.512",
"discountedAmount": "256"
}
],
"periods": 4,
"npv": "-24.00",
"discountRate": "0.25"
}A zero rate returns the plain sum, unrounded
{
"cashflows": [
{
"period": 0,
"amount": "-500.00"
},
{
"period": 1,
"amount": "200.00"
},
{
"period": 2,
"amount": "350.00"
}
],
"discountRate": "0"
}{
"npv": "50",
"discountRate": "0",
"periods": 3,
"discountFactors": [
{
"period": 0,
"discountFactor": "1",
"discountedAmount": "-500"
},
{
"period": 1,
"discountFactor": "1",
"discountedAmount": "200"
},
{
"period": 2,
"discountFactor": "1",
"discountedAmount": "350"
}
]
}Recommended use cases
- Compare two investment options at an agreed hurdle rate
- Value a lease or a payment plan in today's money
- Show a client the discount factor behind every period of a proposal
Avoid for
- Irregular calendar dates you have not yet converted to periods
- Cashflows in several currencies
- Finding the break-even rate of a series
Related skills
Composable with
Safety & privacy
- Runs without calling any external service.
- Your payloads are never stored permanently.
- Telemetry captures metadata only — never your payload contents.
- We never train models on your payloads.
- No human reviews your payloads.