Pick a tech stack from a PRD and see what it costs now and at scale, after startup perks.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Assess the stack an existing app runs on and recommend what to do. For each service: its plan, cost now and in 12 months, must-haves it lacks, cost jumps in the next year, lock-in, and a verdict (keep, watch, optimise, switch or fix) with savings, one-off moving cost and the month a switch pays off. Then a scorecard (cost, requirements, ready to scale, lock-in; A to F, weighted by priorities 0-3) and recommendations ranked by what the user cares about. Categories the requirements need but nothing covers are suggested too. Use the vendor ids from detect_stack; pass plan_id when the user knows their plan (otherwise the cheapest that serves today's users is assumed and said so). How to present: lead with the summary and overall grade, then the ranked recommendations with savings or added cost, then the scorecard; show every caveat and assumption word for word.
Optional company details used only to find startup perks.
Services detect_stack found that the catalogue can't price (its "unknown" list).
How much each criterion matters, 0 (not at all) to 3 (most). Defaults: cost 3, requirements 3, scale 2, lock_in 1.
Usage profile, e.g. b2b_saas, consumer_app, booking_marketplace, content_site, ios_on_device.
Monthly active users expected in 12 months.
Tool list last changed Oct 25, 2026.
Assess the stack an existing app runs on and recommend what to do. For each service: its plan, cost now and in 12 months, must-haves it lacks, cost jumps in the next year, lock-in, and a verdict (keep, watch, optimise, switch or fix) with savings, one-off moving cost and the month a switch pays off. Then a scorecard (cost, requirements, ready to scale, lock-in; A to F, weighted by priorities 0-3) and recommendations ranked by what the user cares about. Categories the requirements need but nothing covers are suggested too. Use the vendor ids from detect_stack; pass plan_id when the user knows their plan (otherwise the cheapest that serves today's users is assumed and said so). How to present: lead with the summary and overall grade, then the ranked recommendations with savings or added cost, then the scorecard; show every caveat and assumption word for word.
| Argument | Type | Description |
|---|---|---|
| stack* | array | |
| founder | object | Optional company details used only to find startup perks. |
| mau_now* | number | |
| unpriced | array | Services detect_stack found that the catalogue can't price (its "unknown" list). |
| switching | object | |
| priorities | object | How much each criterion matters, 0 (not at all) to 3 (most). Defaults: cost 3, requirements 3, scale 2, lock_in 1. |
| profile_id | string | Usage profile, e.g. b2b_saas, consumer_app, booking_marketplace, content_site, ios_on_device. |
| mau_forecast | number | Monthly active users expected in 12 months. |
| requirements | array | |
| budget_monthly_usd | number | |
| revenue_per_mau_usd | number |
{
"type": "object",
"required": [
"stack",
"mau_now"
],
"properties": {
"stack": {
"type": "array",
"items": {
"type": "object",
"required": [
"vendor_id"
],
"properties": {
"plan_id": {
"type": "string"
},
"vendor_id": {
"type": "string"
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
}
}
}
},
"maxItems": 20,
"minItems": 1
},
"founder": {
"type": "object",
"properties": {
"arr_usd": {
"type": "number",
"minimum": 0
},
"gateways": {
"type": "array",
"items": {
"type": "string"
},
"description": "Perk gateways you belong to, e.g. [\"mercury\", \"stripe_atlas\", \"yc\"]."
},
"venture_backed": {
"type": "boolean"
},
"existing_customer_of": {
"type": "array",
"items": {
"type": "string"
},
"description": "Vendor ids you already pay (new-customer perks are excluded)."
}
},
"description": "Optional company details used only to find startup perks."
},
"mau_now": {
"type": "number",
"minimum": 0
},
"unpriced": {
"type": "array",
"items": {
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string"
},
"category": {
"type": "string"
},
"self_hosted": {
"type": "boolean"
}
}
},
"description": "Services detect_stack found that the catalogue can't price (its \"unknown\" list)."
},
"switching": {
"type": "object",
"properties": {
"hours": {
"type": "number",
"minimum": 0
},
"hourly_rate_usd": {
"type": "number",
"minimum": 0
}
}
},
"priorities": {
"type": "object",
"properties": {
"cost": {
"type": "integer",
"maximum": 3,
"minimum": 0
},
"scale": {
"type": "integer",
"maximum": 3,
"minimum": 0
},
"lock_in": {
"type": "integer",
"maximum": 3,
"minimum": 0
},
"requirements": {
"type": "integer",
"maximum": 3,
"minimum": 0
}
},
"description": "How much each criterion matters, 0 (not at all) to 3 (most). Defaults: cost 3, requirements 3, scale 2, lock_in 1."
},
"profile_id": {
"type": "string",
"description": "Usage profile, e.g. b2b_saas, consumer_app, booking_marketplace, content_site, ios_on_device."
},
"mau_forecast": {
"type": "number",
"minimum": 0,
"description": "Monthly active users expected in 12 months."
},
"requirements": {
"type": "array",
"items": {
"type": "object",
"required": [
"capability"
],
"properties": {
"must": {
"type": "boolean",
"description": "true (default) = must-have; false = nice-to-have."
},
"source": {
"type": "string",
"maxLength": 500,
"description": "The PRD sentence this came from."
},
"capability": {
"type": "string",
"description": "Capability id from list_capabilities, e.g. auth.saml_sso."
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
}
}
}
}
},
"budget_monthly_usd": {
"type": "number",
"minimum": 0
},
"revenue_per_mau_usd": {
"type": "number",
"minimum": 0
}
}
}Link the GitHub repo a saved plan is being built in, and check it: detect_stack reads the repo and each planned service is marked found or not found yet (normal while building), services the repo uses that the plan doesn't are listed as drift, and when every planned service is found the plan is marked built. Pass id (a saved plan from list_saved_stacks) and repo the first time; after that, id alone checks the linked repo again. Private repos need the Kong Reviews GitHub App on that repo. How to present: say whether it is built; list what is not found yet as the next setup steps (from get_build_pack); for each drift service, ask the user whether to keep it (price it with diff_stack, or re-plan) or take it out of the code. Needs sign-in.
| Argument | Type | Description |
|---|---|---|
| id* | string | A saved plan id from list_saved_stacks. |
| repo | string | GitHub repo: owner/name or its github.com address. Links it to the plan, replacing any linked before. |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 64,
"description": "A saved plan id from list_saved_stacks."
},
"repo": {
"type": "string",
"maxLength": 200,
"description": "GitHub repo: owner/name or its github.com address. Links it to the plan, replacing any linked before."
}
}
}For each vendor, show whether each requirement is met on its cheapest plan, needs a higher tier (with that tier's cost), or is missing.
| Argument | Type | Description |
|---|---|---|
| mau | number | |
| seats | integer | Team members on per-seat plans. Default 1. |
| per_user | object | Usage per active user a month, e.g. {"sms": 2, "file_storage_gb": 0.2, "ai_input_ktok": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| per_1k_mau | object | Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| profile_id | string | Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile. |
| vendor_ids |
Asks the questions that decide an architecture (static or dynamic, who signs in, how much data, uploads and media, background jobs and live updates, data sensitivity, threat profile, cost of downtime, who runs the servers), then rates every architecture against the answers: static site on a CDN, frontend with Supabase, serverless Next.js on Vercel, Cloudflare Workers, containers on DigitalOcean App Platform, one server, one server behind Cloudflare, Firebase, or on the phone with no server. Returns the top three with a 0-100 fit, what each service does, the monthly cost now and at forecast, strengths and concerns; the rest it considered; and the ones ruled out with the reason. Interview loop: call it with the description first; show the user the questions (each has options, why it matters, and sometimes a suggested answer read from the description); ask the unanswered ones, a few at a time, and call it again with answers. It ranks with whatever is answered, so it is fine to stop early. No model is used for the questions or the ratings. Each architecture has plan_stack_arguments and share_url for the full plan. When the user adds a requirement later, call it again with the same answers and the new description or requirements.
| Argument | Type | Description |
|---|---|---|
| answers | object | Interview answers by question id, each an option id from compare_architectures, e.g. {"server":"dynamic","logins":"consumers","threat":"public","ops":"managed"}. |
| founder | object | Optional company details used only to find startup perks. |
| mau_now | number | |
| app_type | string | Kind of app, from Product Hunt's categories: productivity, engineering_development, design_creative, finance, marketing_sales, data_analysis, platforms, no_code, product_addons, llms, ai_agents, voice_ai, social_community, health_fitness, family, lifestyle, web3, ecommerce, travel, physical_products, booking_marketplace, content_site, ios_on_device. Sets the usage profile and, when users are not given, typical users at launch and after the forecast period. |
Rank vendors in one category at a given scale. Vendors meeting every must-have come first, then cheapest. Ranking is by cost and fit only. Example: {"category":"auth","mau":50000,"requirements":[{"capability":"auth.saml_sso"}]}.
| Argument | Type | Description |
|---|---|---|
| mau* | number | |
| seats | integer | Team members on per-seat plans. Default 1. |
| exclude | array | |
| category* | string | |
| per_user | object | Usage per active user a month, e.g. {"sms": 2, "file_storage_gb": 0.2, "ai_input_ktok": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| per_1k_mau | object | Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
Permanently delete the signed-in user's Kong Reviews account: their GitHub sign-in link and every saved stack. This cannot be undone. Only call it when the user explicitly asks to delete their account, after telling them what will be removed; pass confirm exactly as "delete my account". Needs sign-in.
| Argument | Type | Description |
|---|---|---|
| confirm* | string |
{
"type": "object",
"required": [
"confirm"
],
"properties": {
"confirm": {
"enum": [
"delete my account"
],
"type": "string"
}
}
}Permanently delete one of the signed-in user's saved stacks by id. Only call it when the user explicitly asks to delete that stack; confirm the name first. Needs sign-in.
| Argument | Type | Description |
|---|---|---|
| id* | string |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 64
}
}
}Work out which services an existing app runs on, from its files, a public GitHub repo, or its live website. From an AI coding app, read the project yourself and pass the files that say the most: package.json (or requirements.txt, pyproject.toml, Gemfile, go.mod, pubspec.yaml, Podfile), .env.example (names only, never secret values), and deploy configs (vercel.json, netlify.toml, fly.toml, render.yaml, .do/app.yaml, wrangler.toml, firebase.json, Dockerfile). Returns each catalogue vendor found with its evidence and confidence, services the catalogue can't price yet, frameworks and platforms. Rules only; nothing is stored. Then call audit_stack with the services (and their plans, if the user knows them).
| Argument | Type | Description |
|---|---|---|
| url | string | The live website, to read its hosting headers and the scripts it loads. |
| repo | string | Public GitHub repo: owner/name or its github.com address. |
| text | string | Pasted text, e.g. a package.json or a list of services, when there are no file names. |
| files | array |
{
"type": "object",
"properties": {
"url": {
"type": "string",
"maxLength": 500,
"description": "The live website, to read its hosting headers and the scripts it loads."
},
"repo": {
"type": "string",
"maxLength": 200,
"description": "Public GitHub repo: owner/name or its github.com address."
},
"text": {
"type": "string",
"maxLength": 500000,
"description": "Pasted text, e.g. a package.json or a list of services, when there are no file names."
},
"files": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"content"
],
"properties": {
"path": {
"type": "string",
"maxLength": 300
},
"content": {
"type": "string",
"maxLength": 500000
}
}
},
"maxItems": 200
}
}
}What a code change does to an app's bill: pass the files before (base_files) and after (head_files) the change, such as package.json, requirements.txt, .env.example (names only) and deploy configs. Returns the services added and removed, each priced now and in 12 months at the given users, with cheaper options and cost jumps, the change in monthly cost, and comment_markdown ready to post on a pull request. Use it before adding a paid service or SDK, or to review a pull request. Rules only; nothing is stored.
| Argument | Type | Description |
|---|---|---|
| plans | array | Plans the app is on, when known. |
| mau_now | number | Monthly active users today. Default 1,000, and the comment says it was assumed. |
| base_files* | array | |
| head_files* | array | |
| profile_id | string | |
| mau_forecast | number | Users expected in 12 months. |
| requirements | array |
{
"type": "object",
"required": [
"base_files",
"head_files"
],
"properties": {
"plans": {
"type": "array",
"items": {
"type": "object",
"required": [
"vendor_id",
"plan_id"
],
"properties": {
"plan_id": {
"type": "string"
},
"vendor_id": {
"type": "string"
}
}
},
"maxItems": 20,
"description": "Plans the app is on, when known."
},
"mau_now": {
"type": "number",
"minimum": 0,
"description": "Monthly active users today. Default 1,000, and the comment says it was assumed."
},
"base_files": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"content"
],
"properties": {
"path": {
"type": "string",
"maxLength": 300
},
"content": {
"type": "string",
"maxLength": 200000
}
}
},
"maxItems": 100
},
"head_files": {
"type": "array",
"items": {
"type": "object",
"required": [
"path",
"content"
],
"properties": {
"path": {
"type": "string",
"maxLength": 300
},
"content": {
"type": "string",
"maxLength": 200000
}
}
},
"maxItems": 100
},
"profile_id": {
"type": "string"
},
"mau_forecast": {
"type": "number",
"minimum": 0,
"description": "Users expected in 12 months."
},
"requirements": {
"type": "array",
"items": {
"type": "object",
"required": [
"capability"
],
"properties": {
"must": {
"type": "boolean",
"description": "true (default) = must-have; false = nice-to-have."
},
"source": {
"type": "string",
"maxLength": 500,
"description": "The PRD sentence this came from."
},
"capability": {
"type": "string",
"description": "Capability id from list_capabilities, e.g. auth.saml_sso."
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
}
}
}
}
}
}
}Monthly cost for one vendor at a given scale. Give mau (monthly active users) and a usage profile, or exact usage numbers. Picks the cheapest plan that fits unless plan_id is set. Example: {"vendor_id":"clerk","mau":25000}.
| Argument | Type | Description |
|---|---|---|
| mau | number | |
| seats | integer | Team members on per-seat plans. Default 1. |
| usage | object | Exact monthly usage by unit (mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record); overrides values derived from mau. |
| plan_id | string | |
| per_user | object | Usage per active user a month, e.g. {"sms": 2, "file_storage_gb": 0.2, "ai_input_ktok": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| required |
When something changes (bill too high, growth, a missing feature such as SSO, a renewal), compare staying on the current plan, optimising with the same vendor, and switching vendor, including one-off switching cost and the month switching pays off. Example: {"category":"auth","current":{"vendor_id":"clerk","plan_id":"pro"},"mau_now":11000,"monthly_growth":0.18,"requirements":[{"capability":"auth.saml_sso"}]}. How to present the result: show stay, optimise and switch side by side with cumulative cost at each month given, the break-even month and any problems; show every caveat word for word.
| Argument | Type | Description |
|---|---|---|
| seats | integer | Team members on per-seat plans. Default 1. |
| months | integer | |
| current* | object | |
| founder | object | Optional company details used only to find startup perks. |
| mau_now* | number | |
| category* | string | |
| per_user | object | Usage per active user a month, e.g. {"sms": 2, "file_storage_gb": 0.2, "ai_input_ktok": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
Turn PRD or spec text into structured requirements (scale, capabilities, compliance, regions, budget) for plan_stack, without planning. recommend_stack does both in one call. Each requirement quotes the sentence it came from. Show them to the user to confirm before planning. Without a model on the server it reads the PRD with keyword rules (mode "rules") and also returns the capability vocabulary and instructions: as an assistant, prefer doing the extraction yourself and use the rules result only as a starting point.
| Argument | Type | Description |
|---|---|---|
| prd_text* | string |
{
"type": "object",
"required": [
"prd_text"
],
"properties": {
"prd_text": {
"type": "string",
"maxLength": 60000
}
}
}Scan a range of user counts for one vendor and report where cost jumps: forced plan upgrades and points where usage charges start. Example: {"vendor_id":"clerk","from_mau":1000,"to_mau":200000}.
| Argument | Type | Description |
|---|---|---|
| seats | integer | Team members on per-seat plans. Default 1. |
| to_mau | number | |
| from_mau | number | |
| per_user | object | Usage per active user a month, e.g. {"sms": 2, "file_storage_gb": 0.2, "ai_input_ktok": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| required | array | |
| vendor_id* | string | |
| per_1k_mau | object | Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
Startup credits for the given vendors, and whether you qualify based on your bank, incorporation provider, accelerator or funding. Example: {"vendor_ids":["supabase","posthog"],"founder":{"gateways":["mercury"]}}.
| Argument | Type | Description |
|---|---|---|
| founder | object | Optional company details used only to find startup perks. |
| vendor_ids | array |
{
"type": "object",
"properties": {
"founder": {
"type": "object",
"properties": {
"arr_usd": {
"type": "number",
"minimum": 0
},
"gateways": {
"type": "array",
"items": {
"type": "string"
},
"description": "Perk gateways you belong to, e.g. [\"mercury\", \"stripe_atlas\", \"yc\"]."
},
"venture_backed": {
"type": "boolean"
},
"existing_customer_of": {
"type": "array",
"items": {
"type": "string"
},
"description": "Vendor ids you already pay (new-customer perks are excluded)."
}
},
"description": "Optional company details used only to find startup perks."
},
"vendor_ids": {
"type": "array",
"items": {
"type": "string"
}
}
}
}Everything needed to start building a planned stack: the decisions (each service, what it covers, the runner-up, cost now and at forecast), setup steps in order (domain, hosting, database, sign-in, email, payments, monitoring), the variable names as .env.example, KONG.md (a brief for the AI assistant: the stack, rules, cost cliffs, setup order, and what "done" means), an acceptance checklist and cost guardrails. For a plan built with an app builder (builder_profile non_developer), KONG.md is one prompt to paste into the builder instead. Pass the share_url of a plan (no sign-in needed) or the id of a saved plan (needs sign-in). Write KONG.md and .env.example into the project root, then follow the steps with the user. Values never pass through Kong Reviews: only variable names.
| Argument | Type | Description |
|---|---|---|
| id | string | A saved plan id from list_saved_stacks (needs sign-in). |
| framework | string | What the app is built with: nextjs (default), remix (Remix or React Router), sveltekit, astro, expo (React Native) or node (Express). Picks each service's package, variable names and guide for it. Read it from the project (package.json) when you can. |
| share_url | string | A plan link from plan_stack, recommend_stack or kongreviews.com. |
{
"type": "object",
"properties": {
"id": {
"type": "string",
"maxLength": 64,
"description": "A saved plan id from list_saved_stacks (needs sign-in)."
},
"framework": {
"enum": [
"nextjs",
"remix",
"sveltekit",
"astro",
"expo",
"node"
],
"type": "string",
"description": "What the app is built with: nextjs (default), remix (Remix or React Router), sveltekit, astro, expo (React Native) or node (Express). Picks each service's package, variable names and guide for it. Read it from the project (package.json) when you can."
},
"share_url": {
"type": "string",
"maxLength": 4000,
"description": "A plan link from plan_stack, recommend_stack or kongreviews.com."
}
}
}Structured pricing and capabilities for one vendor: every plan, base fee, metered usage, limits, unmodelled costs, lock-in notes, source URL and verification date.
| Argument | Type | Description |
|---|---|---|
| vendor_id* | string |
{
"type": "object",
"required": [
"vendor_id"
],
"properties": {
"vendor_id": {
"type": "string"
}
}
}Re-run a saved stack or audit with today's catalogue and compare it with when it was saved: monthly cost then and now, picks (or plans) that changed, vendors whose pricing changed and, for audits, the grade; and any bills recorded with record_bills next to the estimate. Use it when the user asks about a stack they saved, or whether prices moved. Pass the id from list_saved_stacks. Needs sign-in. How to present the result: show the stack table (pick and runner-up per category, monthly cost now and at forecast) and the totals. For each price give its source's verified date, or say it is unverified seed data. Show every caveat word for word; never round, merge or drop one. Mention requirement gaps, blockers and trade-offs, and each pick's "weighed" note when a cheaper vendor lost on lock-in, setup work or seed prices. List not_planned as needed but not in the totals. Ask the questions (most expensive first) and re-plan with the answers in the argument each names (per_user, revenue_per_mau_usd, builder_profile). Link share_url so the user can open the full result on kongreviews.com. Do not add vendors or prices that are not in the result.
| Argument | Type | Description |
|---|---|---|
| id* | string | |
| bills_month | string | Month (YYYY-MM) for the bills comparison; default the newest with bills. |
| task_intent | string | What the user is trying to do, in a few words, e.g. "pick a stack for an MVP" or "cost section for an investor deck". No names or contact details. |
| output_destination | string | Where the answer goes. chat (default): summary for the conversation. document: a section ready to paste into a PRD. code: adds npm packages and environment variable names for each pick. |
List capability ids (features, compliance, regions) that requirements can reference, e.g. auth.saml_sso, region.eu, payments.merchant_of_record.
| Argument | Type | Description |
|---|---|---|
| category | string |
{
"type": "object",
"properties": {
"category": {
"type": "string"
}
}
}List the stacks the signed-in user saved, newest first, with their monthly cost when saved and a link to each. Use it when the user mentions "my stacks", "the stack I saved" or a saved stack by name; then call get_saved_stack for one of them. Needs sign-in. How to present: a short table of name, saved date and monthly cost when saved, each name linked to its share_url.
No arguments.
{
"type": "object",
"properties": {}
}List vendors in the pricing catalogue, optionally for one category (hosting, database, auth, email, payments, observability, ai, edge, app_store, coding).
| Argument | Type | Description |
|---|---|---|
| category | string |
{
"type": "object",
"properties": {
"category": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
}
}
}Given confirmed requirements as capability ids, recommend a vendor per category with a runner-up, requirement fit, cost now, cost at forecast scale, year-one cost after perks, cost cliffs, trade-offs and blockers. Use recommend_stack instead when you only have a plain-language description. Example: {"mau_now":5000,"mau_forecast":20000,"profile_id":"booking_marketplace","requirements":[{"capability":"auth.google"},{"capability":"payments.merchant_of_record"}],"founder":{"gateways":["mercury"]}}. How to present the result: show the stack table (pick and runner-up per category, monthly cost now and at forecast) and the totals. For each price give its source's verified date, or say it is unverified seed data. Show every caveat word for word; never round, merge or drop one. Mention requirement gaps, blockers and trade-offs, and each pick's "weighed" note when a cheaper vendor lost on lock-in, setup work or seed prices. List not_planned as needed but not in the totals. Ask the questions (most expensive first) and re-plan with the answers in the argument each names (per_user, revenue_per_mau_usd, builder_profile). Link share_url so the user can open the full result on kongreviews.com. Do not add vendors or prices that are not in the result.
| Argument | Type | Description |
|---|---|---|
| needs | array | Needs the plan should list even though they are not priced (directory category ids such as maps, vector_db, speech), from recommend_stack's requirements_read.needs. |
| seats | integer | Team members on per-seat plans. Default 1. |
| answers | object | Interview answers by question id, each an option id from compare_architectures, e.g. {"server":"dynamic","logins":"consumers","threat":"public","ops":"managed"}. |
Month-by-month cost for a whole stack as users grow, with startup perks applied if requested, plus cost cliffs (forced plan upgrades, sharp jumps, credits running out). Example: {"vendors":[{"vendor_id":"supabase"},{"vendor_id":"vercel"}],"start_mau":2000,"monthly_growth":0.15,"months":24,"apply_perks":true,"founder":{"gateways":["mercury"]}}.
| Argument | Type | Description |
|---|---|---|
| seats | integer | Team members on per-seat plans. Default 1. |
| months | integer | |
| founder | object | Optional company details used only to find startup perks. |
| vendors* | array | |
| per_user | object | Usage per active user a month, e.g. {"sms": 2, "file_storage_gb": 0.2, "ai_input_ktok": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| start_mau* | number | |
Start here. Describe the product in plain language and get a recommended stack with costs now and at scale, in one call. Write the description as the user would: what the product does, who signs in and how, how it charges, which emails it sends, compliance or data-region needs, and expected users now and in 12 months. Example: "A booking app for yoga studios. Members sign in with Google and pay a monthly subscription. Studios get paid out. EU data. 2,000 users at launch, 20,000 within a year." Pass a PRD as the description if you have one. Numbers you pass (mau_now, mau_forecast, budget_monthly_usd, profile_id, app_type) override what is read from the text. Without a user count it assumes a new product of that app type at launch and its typical growth, and lists that in defaults_used. Before treating the plan as final, show the user the requirements it read (each quotes its sentence) and the open questions, and ask them to confirm or correct them; then call plan_stack with the corrected requirements. How to present the result: show the stack table (pick and runner-up per category, monthly cost now and at forecast) and the totals. For each price give its source's verified date, or say it is unverified seed data. Show every caveat word for word; never round, merge or drop one. Mention requirement gaps, blockers and trade-offs, and each pick's "weighed" note when a cheaper vendor lost on lock-in, setup work or seed prices. List not_planned as needed but not in the totals. Ask the questions (most expensive first) and re-plan with the answers in the argument each names (per_user, revenue_per_mau_usd, builder_profile). Link share_url so the user can open the full result on kongreviews.com. Do not add vendors or prices that are not in the result.
| Argument | Type | Description |
|---|---|---|
| seats | integer | Team members on per-seat plans. Default 1. |
| founder | object | Optional company details used only to find startup perks. |
Record what the user actually paid each service in a month, on one of their saved stacks or audits, and compare it with the estimate at the users it was saved with. Without id, the bills are kept on the account, not on a stack (for someone who has not saved one): they are recorded and listed, with no estimate to compare, and can be moved onto a stack later on the Billing page. Pass bills (month as YYYY-MM, the service as vendor_id or name as on the invoice, amount_usd), or csv: a billing export with columns for month or date, service or vendor, and amount, or invoice_text: an invoice's text as pasted from the PDF or email (the service, month and total are read from it, each with the invoice line it came from; preview: true reads it without saving, so the user can check it first). A bill replaces any earlier one for the same service and month; remove deletes a month (or one service in it). Returns, per service, the estimate, the bill and the difference, flagging bills more than 20% (and $5) over or under. With alerts on, a bill well over its estimate is emailed too. Needs sign-in. How to present: a table of service, estimate, billed and difference; then say what could explain the biggest gap (more users or usage than given, a plan above the one assumed, add-ons the catalogue does not price) and offer update_saved_stack with the real user count.
| Argument | Type | Description |
|---|---|---|
| id | string | The saved stack or audit. Leave out to keep the bills on the account. |
| csv | string | A CSV billing export: a header row, then one row per charge. |
| bills | array | |
| month | string | Which month to compare. Default: the newest with bills. |
| remove | object |
Save a recommended stack, or an audit of an existing app, to the signed-in user's Kong Reviews account so it can be reopened later and compared with current prices. Pass the share_url from recommend_stack, plan_stack or audit_stack (preferred) or the plan_stack arguments, plus a short name such as "Yoga booking MVP". Afterwards, offer update_saved_stack with alerts: true so they are emailed when prices or cost jumps change it. Needs sign-in: if the user isn't signed in, the app will ask them to connect their Kong Reviews account. Only save when the user asks to keep or save a stack.
| Argument | Type | Description |
|---|---|---|
| name* | string | Short name the user will recognise. |
| plan | object | plan_stack arguments, if there is no share_url. |
| share_url | string | share_url returned by recommend_stack, plan_stack or audit_stack. |
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"maxLength": 80,
"description": "Short name the user will recognise."
},
"plan": {
"type": "object",
"description": "plan_stack arguments, if there is no share_url."
},
"share_url": {
"type": "string",
"maxLength": 4000,
"description": "share_url returned by recommend_stack, plan_stack or audit_stack."
}
}
}Search the directory of developer services (hosting, databases, auth, email, SMS, payments, analytics, monitoring, LLM APIs, vector databases, search, CMS and more): what each provider sells, its free tier, its lowest paid price, caveats and the pricing page it came from. Wider than the cost engine: use it to find options or check a provider the other tools don't price. Entries marked candidate have no prices yet. Example: {"query":"postgres","category":"database"}.
| Argument | Type | Description |
|---|---|---|
| id | string | Exact provider id, e.g. "neon". |
| limit | integer | |
| query | string | Words to match, e.g. "sms", "vector", "merchant of record", "Supabase", or a company such as "Amazon" for all its services. |
| offset | integer | |
| status | string | researched: prices read from the pricing page. candidate: listed without prices. |
| category | string | Directory category id, e.g. database, email, sms, llm, vector_db. Every result lists the categories with counts. |
| in_cost_engine | boolean | true: only providers estimate_cost and plan_stack can price. |
Browse products startups have built and the stack each runs on: what it is, whether it is a website, a mobile app, both or a desktop app, its category and pricing, and every framework and service it uses (services link to the directory and, where priced, the cost engine). Use it to find real stacks to learn from, e.g. apps built with Supabase. Results list each filter value with counts. Example: {"tech":"supabase","form":"app"}.
| Argument | Type | Description |
|---|---|---|
| id | string | Exact product id. |
| form | string | How it ships. web: a website (maybe with a desktop app). app: a mobile app only. both: a website and a mobile app. desktop: a desktop app with no website (maybe with a mobile app). |
| sort | string | stars: most GitHub stars first. |
| tech | string | A tech key from facets.tech: a directory id such as "supabase", or a framework such as "next-js". |
| limit | integer | |
| query | string | Words to match against names, taglines, makers and the stack, e.g. "habit tracker", "Expo". |
| offset | integer | |
| pricing | string |
Change one of the signed-in user's saved stacks or audits: its name, how many users it has now (mau_now) or expects (mau_forecast), whether to email them when it changes (alerts), and which build-pack steps are done (build_progress, for plans). With alerts on, Kong Reviews checks it every day and emails when a vendor's price change moves its monthly cost, when a cost jump or a startup credit running out is about 45 days away, and once a month to ask for the current user count. Alerts need a confirmed email: pass email to send the confirmation link (GitHub sign-in shares no email). Updating users re-prices it and makes today the start of its projection. Needs sign-in.
| Argument | Type | Description |
|---|---|---|
| id* | string | |
| name | string | |
| string | Where alerts go. A confirmation link is sent first; only ask for it when the user wants alerts. | |
| alerts | boolean | Email the user when prices, cost jumps or credits change this stack. |
| mau_now | number | Monthly active users today. |
| unlink_repo | boolean | Forget the repo linked with check_build, and its last check. |
| mau_forecast | number | Users expected at the end of the forecast (12 months for audits). |
| array |
| requirements* | array |
| revenue_per_mau_usd | number | Average monthly revenue per active user (for payment fees). |
| transactions_per_mau | number | Payments per active user per month. |
{
"type": "object",
"required": [
"vendor_ids",
"requirements"
],
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"seats": {
"type": "integer",
"maximum": 10000,
"minimum": 1,
"description": "Team members on per-seat plans. Default 1."
},
"per_user": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Usage per active user a month, e.g. {\"sms\": 2, \"file_storage_gb\": 0.2, \"ai_input_ktok\": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"per_1k_mau": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"profile_id": {
"type": "string",
"description": "Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile."
},
"vendor_ids": {
"type": "array",
"items": {
"type": "string"
},
"maxItems": 20,
"minItems": 1
},
"requirements": {
"type": "array",
"items": {
"type": "object",
"required": [
"capability"
],
"properties": {
"must": {
"type": "boolean",
"description": "true (default) = must-have; false = nice-to-have."
},
"source": {
"type": "string",
"maxLength": 500,
"description": "The PRD sentence this came from."
},
"capability": {
"type": "string",
"description": "Capability id from list_capabilities, e.g. auth.saml_sso."
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
}
}
}
},
"minItems": 1
},
"revenue_per_mau_usd": {
"type": "number",
"minimum": 0,
"description": "Average monthly revenue per active user (for payment fees)."
},
"transactions_per_mau": {
"type": "number",
"minimum": 0,
"description": "Payments per active user per month."
}
}
}| platforms | array | Where the app runs. A phone app with no web platform gets the phone questions and options. |
| categories | array | Categories to plan before the answers adjust them. Default: what the description needs. |
| profile_id | string | Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile. |
| description | string | Plain-language product description or PRD text. Read for requirements (unless requirements is given) and for suggested answers. |
| task_intent | string | What the user is trying to do, in a few words, e.g. "pick a stack for an MVP" or "cost section for an investor deck". No names or contact details. |
| mau_forecast | number |
| requirements | array | Requirements already confirmed; when given, the description is not read for requirements again. |
| forecast_months | integer |
| budget_monthly_usd | number |
| output_destination | string | Where the answer goes. chat (default): summary for the conversation. document: a section ready to paste into a PRD. code: adds npm packages and environment variable names for each pick. |
| revenue_per_mau_usd | number | Average monthly revenue per active user (for payment fees). |
{
"type": "object",
"properties": {
"answers": {
"type": "object",
"properties": {
"ops": {
"enum": [
"managed",
"some",
"admin"
],
"type": "string"
},
"data": {
"enum": [
"little",
"lots"
],
"type": "string"
},
"media": {
"enum": [
"none",
"images",
"video"
],
"type": "string"
},
"logins": {
"enum": [
"none",
"consumers",
"teams",
"enterprise"
],
"type": "string"
},
"server": {
"enum": [
"static",
"mixed",
"dynamic",
"none",
"proxy",
"full"
],
"type": "string"
},
"threat": {
"enum": [
"low",
"public",
"target"
],
"type": "string"
},
"uptime": {
"enum": [
"fine",
"costly",
"critical"
],
"type": "string"
},
"background": {
"enum": [
"none",
"jobs",
"realtime"
],
"type": "string"
},
"sensitivity": {
"enum": [
"low",
"eu",
"business",
"health"
],
"type": "string"
}
},
"description": "Interview answers by question id, each an option id from compare_architectures, e.g. {\"server\":\"dynamic\",\"logins\":\"consumers\",\"threat\":\"public\",\"ops\":\"managed\"}.",
"additionalProperties": false
},
"founder": {
"type": "object",
"properties": {
"arr_usd": {
"type": "number",
"minimum": 0
},
"gateways": {
"type": "array",
"items": {
"type": "string"
},
"description": "Perk gateways you belong to, e.g. [\"mercury\", \"stripe_atlas\", \"yc\"]."
},
"venture_backed": {
"type": "boolean"
},
"existing_customer_of": {
"type": "array",
"items": {
"type": "string"
},
"description": "Vendor ids you already pay (new-customer perks are excluded)."
}
},
"description": "Optional company details used only to find startup perks."
},
"mau_now": {
"type": "number",
"maximum": 100000000,
"minimum": 0
},
"app_type": {
"type": "string",
"description": "Kind of app, from Product Hunt's categories: productivity, engineering_development, design_creative, finance, marketing_sales, data_analysis, platforms, no_code, product_addons, llms, ai_agents, voice_ai, social_community, health_fitness, family, lifestyle, web3, ecommerce, travel, physical_products, booking_marketplace, content_site, ios_on_device. Sets the usage profile and, when users are not given, typical users at launch and after the forecast period."
},
"platforms": {
"type": "array",
"items": {
"enum": [
"web",
"ios",
"android",
"desktop"
],
"type": "string"
},
"description": "Where the app runs. A phone app with no web platform gets the phone questions and options."
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
},
"description": "Categories to plan before the answers adjust them. Default: what the description needs."
},
"profile_id": {
"type": "string",
"description": "Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile."
},
"description": {
"type": "string",
"maxLength": 60000,
"description": "Plain-language product description or PRD text. Read for requirements (unless requirements is given) and for suggested answers."
},
"task_intent": {
"type": "string",
"maxLength": 300,
"description": "What the user is trying to do, in a few words, e.g. \"pick a stack for an MVP\" or \"cost section for an investor deck\". No names or contact details."
},
"mau_forecast": {
"type": "number",
"maximum": 100000000,
"minimum": 0
},
"requirements": {
"type": "array",
"items": {
"type": "object",
"required": [
"capability"
],
"properties": {
"must": {
"type": "boolean",
"description": "true (default) = must-have; false = nice-to-have."
},
"source": {
"type": "string",
"maxLength": 500,
"description": "The PRD sentence this came from."
},
"capability": {
"type": "string",
"description": "Capability id from list_capabilities, e.g. auth.saml_sso."
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
}
}
}
},
"description": "Requirements already confirmed; when given, the description is not read for requirements again."
},
"forecast_months": {
"type": "integer",
"maximum": 36,
"minimum": 1
},
"budget_monthly_usd": {
"type": "number",
"minimum": 0
},
"output_destination": {
"enum": [
"chat",
"document",
"code"
],
"type": "string",
"description": "Where the answer goes. chat (default): summary for the conversation. document: a section ready to paste into a PRD. code: adds npm packages and environment variable names for each pick."
},
"revenue_per_mau_usd": {
"type": "number",
"minimum": 0,
"description": "Average monthly revenue per active user (for payment fees)."
}
}
}| profile_id | string | Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile. |
| requirements | array |
| revenue_per_mau_usd | number | Average monthly revenue per active user (for payment fees). |
| transactions_per_mau | number | Payments per active user per month. |
{
"type": "object",
"required": [
"category",
"mau"
],
"properties": {
"mau": {
"type": "number",
"maximum": 100000000,
"minimum": 0
},
"seats": {
"type": "integer",
"maximum": 10000,
"minimum": 1,
"description": "Team members on per-seat plans. Default 1."
},
"exclude": {
"type": "array",
"items": {
"type": "string"
}
},
"category": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
},
"per_user": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Usage per active user a month, e.g. {\"sms\": 2, \"file_storage_gb\": 0.2, \"ai_input_ktok\": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"per_1k_mau": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"profile_id": {
"type": "string",
"description": "Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile."
},
"requirements": {
"type": "array",
"items": {
"type": "object",
"required": [
"capability"
],
"properties": {
"must": {
"type": "boolean",
"description": "true (default) = must-have; false = nice-to-have."
},
"source": {
"type": "string",
"maxLength": 500,
"description": "The PRD sentence this came from."
},
"capability": {
"type": "string",
"description": "Capability id from list_capabilities, e.g. auth.saml_sso."
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
}
}
}
}
},
"revenue_per_mau_usd": {
"type": "number",
"minimum": 0,
"description": "Average monthly revenue per active user (for payment fees)."
},
"transactions_per_mau": {
"type": "number",
"minimum": 0,
"description": "Payments per active user per month."
}
}
}| array |
| Capability ids the plan must include. |
| vendor_id* | string |
| per_1k_mau | object | Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| profile_id | string | Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile. |
| revenue_per_mau_usd | number | Average monthly revenue per active user (for payment fees). |
| transactions_per_mau | number | Payments per active user per month. |
{
"type": "object",
"required": [
"vendor_id"
],
"properties": {
"mau": {
"type": "number",
"maximum": 100000000,
"minimum": 0
},
"seats": {
"type": "integer",
"maximum": 10000,
"minimum": 1,
"description": "Team members on per-seat plans. Default 1."
},
"usage": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Exact monthly usage by unit (mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record); overrides values derived from mau.",
"additionalProperties": false
},
"plan_id": {
"type": "string"
},
"per_user": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Usage per active user a month, e.g. {\"sms\": 2, \"file_storage_gb\": 0.2, \"ai_input_ktok\": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"required": {
"type": "array",
"items": {
"type": "string"
},
"description": "Capability ids the plan must include."
},
"vendor_id": {
"type": "string"
},
"per_1k_mau": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"profile_id": {
"type": "string",
"description": "Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile."
},
"revenue_per_mau_usd": {
"type": "number",
"minimum": 0,
"description": "Average monthly revenue per active user (for payment fees)."
},
"transactions_per_mau": {
"type": "number",
"minimum": 0,
"description": "Payments per active user per month."
}
}
}| switch_to | string | Evaluate a specific vendor instead of the best-ranked alternative. |
| switching | object |
| per_1k_mau | object | Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| profile_id | string | Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile. |
| requirements | array |
| monthly_growth | number |
| revenue_per_mau_usd | number | Average monthly revenue per active user (for payment fees). |
| transactions_per_mau | number | Payments per active user per month. |
{
"type": "object",
"required": [
"category",
"current",
"mau_now"
],
"properties": {
"seats": {
"type": "integer",
"maximum": 10000,
"minimum": 1,
"description": "Team members on per-seat plans. Default 1."
},
"months": {
"type": "integer",
"maximum": 60,
"minimum": 3
},
"current": {
"type": "object",
"required": [
"vendor_id",
"plan_id"
],
"properties": {
"plan_id": {
"type": "string"
},
"vendor_id": {
"type": "string"
}
}
},
"founder": {
"type": "object",
"properties": {
"arr_usd": {
"type": "number",
"minimum": 0
},
"gateways": {
"type": "array",
"items": {
"type": "string"
},
"description": "Perk gateways you belong to, e.g. [\"mercury\", \"stripe_atlas\", \"yc\"]."
},
"venture_backed": {
"type": "boolean"
},
"existing_customer_of": {
"type": "array",
"items": {
"type": "string"
},
"description": "Vendor ids you already pay (new-customer perks are excluded)."
}
},
"description": "Optional company details used only to find startup perks."
},
"mau_now": {
"type": "number",
"minimum": 0
},
"category": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
},
"per_user": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Usage per active user a month, e.g. {\"sms\": 2, \"file_storage_gb\": 0.2, \"ai_input_ktok\": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"switch_to": {
"type": "string",
"description": "Evaluate a specific vendor instead of the best-ranked alternative."
},
"switching": {
"type": "object",
"properties": {
"hours": {
"type": "number",
"minimum": 0
},
"hourly_rate_usd": {
"type": "number",
"minimum": 0
}
}
},
"per_1k_mau": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"profile_id": {
"type": "string",
"description": "Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile."
},
"requirements": {
"type": "array",
"items": {
"type": "object",
"required": [
"capability"
],
"properties": {
"must": {
"type": "boolean",
"description": "true (default) = must-have; false = nice-to-have."
},
"source": {
"type": "string",
"maxLength": 500,
"description": "The PRD sentence this came from."
},
"capability": {
"type": "string",
"description": "Capability id from list_capabilities, e.g. auth.saml_sso."
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
}
}
}
}
},
"monthly_growth": {
"type": "number",
"maximum": 2,
"minimum": -0.5
},
"revenue_per_mau_usd": {
"type": "number",
"minimum": 0,
"description": "Average monthly revenue per active user (for payment fees)."
},
"transactions_per_mau": {
"type": "number",
"minimum": 0,
"description": "Payments per active user per month."
}
}
}| profile_id | string | Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile. |
| revenue_per_mau_usd | number | Average monthly revenue per active user (for payment fees). |
| transactions_per_mau | number | Payments per active user per month. |
{
"type": "object",
"required": [
"vendor_id"
],
"properties": {
"seats": {
"type": "integer",
"maximum": 10000,
"minimum": 1,
"description": "Team members on per-seat plans. Default 1."
},
"to_mau": {
"type": "number",
"maximum": 10000000
},
"from_mau": {
"type": "number",
"minimum": 100
},
"per_user": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Usage per active user a month, e.g. {\"sms\": 2, \"file_storage_gb\": 0.2, \"ai_input_ktok\": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"required": {
"type": "array",
"items": {
"type": "string"
}
},
"vendor_id": {
"type": "string"
},
"per_1k_mau": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"profile_id": {
"type": "string",
"description": "Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile."
},
"revenue_per_mau_usd": {
"type": "number",
"minimum": 0,
"description": "Average monthly revenue per active user (for payment fees)."
},
"transactions_per_mau": {
"type": "number",
"minimum": 0,
"description": "Payments per active user per month."
}
}
}{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 64
},
"bills_month": {
"type": "string",
"maxLength": 20,
"description": "Month (YYYY-MM) for the bills comparison; default the newest with bills."
},
"task_intent": {
"type": "string",
"maxLength": 300,
"description": "What the user is trying to do, in a few words, e.g. \"pick a stack for an MVP\" or \"cost section for an investor deck\". No names or contact details."
},
"output_destination": {
"enum": [
"chat",
"document",
"code"
],
"type": "string",
"description": "Where the answer goes. chat (default): summary for the conversation. document: a section ready to paste into a PRD. code: adds npm packages and environment variable names for each pick."
}
}
}| founder |
| object |
| Optional company details used only to find startup perks. |
| mau_now | number | Monthly active users now. Without it, a new product of the app type at launch. |
| ranking | string | balanced (default): monthly cost plus a small monthly weight for lock-in, setup work before first use and unverified seed prices, so a slightly cheaper vendor that is hard to leave or slow to set up can lose. cost: price alone. |
| app_type | string | Kind of app, from Product Hunt's categories: productivity, engineering_development, design_creative, finance, marketing_sales, data_analysis, platforms, no_code, product_addons, llms, ai_agents, voice_ai, social_community, health_fitness, family, lifestyle, web3, ecommerce, travel, physical_products, booking_marketplace, content_site, ios_on_device. Sets the usage profile and, when users are not given, typical users at launch and after the forecast period. |
| per_user | object | Usage per active user a month, e.g. {"sms": 2, "file_storage_gb": 0.2, "ai_input_ktok": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| platforms | array | Where the app runs; with architecture, a phone-only app leaves out web hosting. |
| categories | array |
| per_1k_mau | object | Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| profile_id | string | Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile. |
| apply_perks | boolean |
| task_intent | string | What the user is trying to do, in a few words, e.g. "pick a stack for an MVP" or "cost section for an investor deck". No names or contact details. |
| architecture | string | Plan with only this architecture's vendors, from compare_architectures (e.g. serverless_nextjs, cloudflare_edge, vps_cloudflare). |
| mau_forecast | number |
| requirements | array |
| builder_profile | string | Who builds it. developer picks an AI coding agent, non_developer an app builder (and counts setup work double). Given without categories, the plan adds the build tool (coding). |
| exclude_vendors | array |
| forecast_months | integer |
| budget_monthly_usd | number |
| output_destination | string | Where the answer goes. chat (default): summary for the conversation. document: a section ready to paste into a PRD. code: adds npm packages and environment variable names for each pick. |
| revenue_per_mau_usd | number | Average monthly revenue per active user (for payment fees). |
| transactions_per_mau | number | Payments per active user per month. |
{
"type": "object",
"properties": {
"needs": {
"type": "array",
"items": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 40
},
"label": {
"type": "string",
"maxLength": 80
},
"source": {
"type": "string",
"maxLength": 500
}
}
},
"maxItems": 20,
"description": "Needs the plan should list even though they are not priced (directory category ids such as maps, vector_db, speech), from recommend_stack's requirements_read.needs."
},
"seats": {
"type": "integer",
"maximum": 10000,
"minimum": 1,
"description": "Team members on per-seat plans. Default 1."
},
"answers": {
"type": "object",
"properties": {
"ops": {
"enum": [
"managed",
"some",
"admin"
],
"type": "string"
},
"data": {
"enum": [
"little",
"lots"
],
"type": "string"
},
"media": {
"enum": [
"none",
"images",
"video"
],
"type": "string"
},
"logins": {
"enum": [
"none",
"consumers",
"teams",
"enterprise"
],
"type": "string"
},
"server": {
"enum": [
"static",
"mixed",
"dynamic",
"none",
"proxy",
"full"
],
"type": "string"
},
"threat": {
"enum": [
"low",
"public",
"target"
],
"type": "string"
},
"uptime": {
"enum": [
"fine",
"costly",
"critical"
],
"type": "string"
},
"background": {
"enum": [
"none",
"jobs",
"realtime"
],
"type": "string"
},
"sensitivity": {
"enum": [
"low",
"eu",
"business",
"health"
],
"type": "string"
}
},
"description": "Interview answers by question id, each an option id from compare_architectures, e.g. {\"server\":\"dynamic\",\"logins\":\"consumers\",\"threat\":\"public\",\"ops\":\"managed\"}.",
"additionalProperties": false
},
"founder": {
"type": "object",
"properties": {
"arr_usd": {
"type": "number",
"minimum": 0
},
"gateways": {
"type": "array",
"items": {
"type": "string"
},
"description": "Perk gateways you belong to, e.g. [\"mercury\", \"stripe_atlas\", \"yc\"]."
},
"venture_backed": {
"type": "boolean"
},
"existing_customer_of": {
"type": "array",
"items": {
"type": "string"
},
"description": "Vendor ids you already pay (new-customer perks are excluded)."
}
},
"description": "Optional company details used only to find startup perks."
},
"mau_now": {
"type": "number",
"maximum": 100000000,
"minimum": 0,
"description": "Monthly active users now. Without it, a new product of the app type at launch."
},
"ranking": {
"enum": [
"balanced",
"cost"
],
"type": "string",
"description": "balanced (default): monthly cost plus a small monthly weight for lock-in, setup work before first use and unverified seed prices, so a slightly cheaper vendor that is hard to leave or slow to set up can lose. cost: price alone."
},
"app_type": {
"type": "string",
"description": "Kind of app, from Product Hunt's categories: productivity, engineering_development, design_creative, finance, marketing_sales, data_analysis, platforms, no_code, product_addons, llms, ai_agents, voice_ai, social_community, health_fitness, family, lifestyle, web3, ecommerce, travel, physical_products, booking_marketplace, content_site, ios_on_device. Sets the usage profile and, when users are not given, typical users at launch and after the forecast period."
},
"per_user": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Usage per active user a month, e.g. {\"sms\": 2, \"file_storage_gb\": 0.2, \"ai_input_ktok\": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"platforms": {
"type": "array",
"items": {
"enum": [
"web",
"ios",
"android",
"desktop"
],
"type": "string"
},
"description": "Where the app runs; with architecture, a phone-only app leaves out web hosting."
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
}
},
"per_1k_mau": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"profile_id": {
"type": "string",
"description": "Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile."
},
"apply_perks": {
"type": "boolean"
},
"task_intent": {
"type": "string",
"maxLength": 300,
"description": "What the user is trying to do, in a few words, e.g. \"pick a stack for an MVP\" or \"cost section for an investor deck\". No names or contact details."
},
"architecture": {
"enum": [
"static_cdn",
"frontend_baas",
"serverless_nextjs",
"cloudflare_edge",
"managed_containers",
"single_vps",
"vps_cloudflare",
"firebase",
"on_device"
],
"type": "string",
"description": "Plan with only this architecture's vendors, from compare_architectures (e.g. serverless_nextjs, cloudflare_edge, vps_cloudflare)."
},
"mau_forecast": {
"type": "number",
"maximum": 100000000,
"minimum": 0
},
"requirements": {
"type": "array",
"items": {
"type": "object",
"required": [
"capability"
],
"properties": {
"must": {
"type": "boolean",
"description": "true (default) = must-have; false = nice-to-have."
},
"source": {
"type": "string",
"maxLength": 500,
"description": "The PRD sentence this came from."
},
"capability": {
"type": "string",
"description": "Capability id from list_capabilities, e.g. auth.saml_sso."
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
}
}
}
}
},
"builder_profile": {
"enum": [
"developer",
"non_developer",
"unknown"
],
"type": "string",
"description": "Who builds it. developer picks an AI coding agent, non_developer an app builder (and counts setup work double). Given without categories, the plan adds the build tool (coding)."
},
"exclude_vendors": {
"type": "array",
"items": {
"type": "string"
}
},
"forecast_months": {
"type": "integer",
"maximum": 36,
"minimum": 1
},
"budget_monthly_usd": {
"type": "number",
"minimum": 0
},
"output_destination": {
"enum": [
"chat",
"document",
"code"
],
"type": "string",
"description": "Where the answer goes. chat (default): summary for the conversation. document: a section ready to paste into a PRD. code: adds npm packages and environment variable names for each pick."
},
"revenue_per_mau_usd": {
"type": "number",
"minimum": 0,
"description": "Average monthly revenue per active user (for payment fees)."
},
"transactions_per_mau": {
"type": "number",
"minimum": 0,
"description": "Payments per active user per month."
}
}
}| mau_series |
| array |
| Explicit MAU per month; overrides start_mau, monthly_growth and months. |
| per_1k_mau | object | Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| profile_id | string | Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile. |
| apply_perks | boolean |
| monthly_growth | number | e.g. 0.1 = 10% per month |
| revenue_per_mau_usd | number | Average monthly revenue per active user (for payment fees). |
| transactions_per_mau | number | Payments per active user per month. |
{
"type": "object",
"required": [
"vendors",
"start_mau"
],
"properties": {
"seats": {
"type": "integer",
"maximum": 10000,
"minimum": 1,
"description": "Team members on per-seat plans. Default 1."
},
"months": {
"type": "integer",
"maximum": 60,
"minimum": 1
},
"founder": {
"type": "object",
"properties": {
"arr_usd": {
"type": "number",
"minimum": 0
},
"gateways": {
"type": "array",
"items": {
"type": "string"
},
"description": "Perk gateways you belong to, e.g. [\"mercury\", \"stripe_atlas\", \"yc\"]."
},
"venture_backed": {
"type": "boolean"
},
"existing_customer_of": {
"type": "array",
"items": {
"type": "string"
},
"description": "Vendor ids you already pay (new-customer perks are excluded)."
}
},
"description": "Optional company details used only to find startup perks."
},
"vendors": {
"type": "array",
"items": {
"type": "object",
"required": [
"vendor_id"
],
"properties": {
"plan_id": {
"type": "string"
},
"required": {
"type": "array",
"items": {
"type": "string"
}
},
"vendor_id": {
"type": "string"
}
}
},
"maxItems": 20,
"minItems": 1
},
"per_user": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Usage per active user a month, e.g. {\"sms\": 2, \"file_storage_gb\": 0.2, \"ai_input_ktok\": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"start_mau": {
"type": "number",
"minimum": 0
},
"mau_series": {
"type": "array",
"items": {
"type": "number",
"minimum": 0
},
"maxItems": 60,
"minItems": 1,
"description": "Explicit MAU per month; overrides start_mau, monthly_growth and months."
},
"per_1k_mau": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Override usage per 1,000 MAU. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"profile_id": {
"type": "string",
"description": "Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile."
},
"apply_perks": {
"type": "boolean"
},
"monthly_growth": {
"type": "number",
"maximum": 2,
"minimum": -0.5,
"description": "e.g. 0.1 = 10% per month"
},
"revenue_per_mau_usd": {
"type": "number",
"minimum": 0,
"description": "Average monthly revenue per active user (for payment fees)."
},
"transactions_per_mau": {
"type": "number",
"minimum": 0,
"description": "Payments per active user per month."
}
}
}| mau_now | number | Monthly active users now or at launch. |
| ranking | string | balanced (default): monthly cost plus a small monthly weight for lock-in, setup work before first use and unverified seed prices, so a slightly cheaper vendor that is hard to leave or slow to set up can lose. cost: price alone. |
| app_type | string | Kind of app, from Product Hunt's categories: productivity, engineering_development, design_creative, finance, marketing_sales, data_analysis, platforms, no_code, product_addons, llms, ai_agents, voice_ai, social_community, health_fitness, family, lifestyle, web3, ecommerce, travel, physical_products, booking_marketplace, content_site, ios_on_device. Sets the usage profile and, when users are not given, typical users at launch and after the forecast period. |
| per_user | object | Usage per active user a month, e.g. {"sms": 2, "file_storage_gb": 0.2, "ai_input_ktok": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record. |
| categories | array | Plan these categories. Default: a build tool (an AI app builder or coding agent) plus what the description needs (a web app gets hosting, database, sign-in, email, payments and observability; an on-device app only what it names; a one-sentence idea such as a pantry tracker what that kind of app usually needs). |
| profile_id | string | Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile. |
| description* | string | Plain-language product description or PRD text. |
| task_intent | string | What the user is trying to do, in a few words, e.g. "pick a stack for an MVP" or "cost section for an investor deck". No names or contact details. |
| mau_forecast | number | Monthly active users expected at the forecast horizon. |
| builder_profile | string | Who builds it. developer picks an AI coding agent, non_developer an app builder (and counts setup work double). Given without categories, the plan adds the build tool (coding). |
| forecast_months | integer |
| budget_monthly_usd | number |
| output_destination | string | Where the answer goes. chat (default): summary for the conversation. document: a section ready to paste into a PRD. code: adds npm packages and environment variable names for each pick. |
| revenue_per_mau_usd | number | Average monthly revenue per active user (for payment fees). |
| transactions_per_mau | number | Payments per active user per month. |
{
"type": "object",
"required": [
"description"
],
"properties": {
"seats": {
"type": "integer",
"maximum": 10000,
"minimum": 1,
"description": "Team members on per-seat plans. Default 1."
},
"founder": {
"type": "object",
"properties": {
"arr_usd": {
"type": "number",
"minimum": 0
},
"gateways": {
"type": "array",
"items": {
"type": "string"
},
"description": "Perk gateways you belong to, e.g. [\"mercury\", \"stripe_atlas\", \"yc\"]."
},
"venture_backed": {
"type": "boolean"
},
"existing_customer_of": {
"type": "array",
"items": {
"type": "string"
},
"description": "Vendor ids you already pay (new-customer perks are excluded)."
}
},
"description": "Optional company details used only to find startup perks."
},
"mau_now": {
"type": "number",
"maximum": 100000000,
"minimum": 0,
"description": "Monthly active users now or at launch."
},
"ranking": {
"enum": [
"balanced",
"cost"
],
"type": "string",
"description": "balanced (default): monthly cost plus a small monthly weight for lock-in, setup work before first use and unverified seed prices, so a slightly cheaper vendor that is hard to leave or slow to set up can lose. cost: price alone."
},
"app_type": {
"type": "string",
"description": "Kind of app, from Product Hunt's categories: productivity, engineering_development, design_creative, finance, marketing_sales, data_analysis, platforms, no_code, product_addons, llms, ai_agents, voice_ai, social_community, health_fitness, family, lifestyle, web3, ecommerce, travel, physical_products, booking_marketplace, content_site, ios_on_device. Sets the usage profile and, when users are not given, typical users at launch and after the forecast period."
},
"per_user": {
"type": "object",
"properties": {
"mau": {
"type": "number",
"minimum": 0
},
"sms": {
"type": "number",
"minimum": 0
},
"seat": {
"type": "number",
"minimum": 0
},
"email": {
"type": "number",
"minimum": 0
},
"event": {
"type": "number",
"minimum": 0
},
"ci_hour": {
"type": "number",
"minimum": 0
},
"job_run": {
"type": "number",
"minimum": 0
},
"kv_read": {
"type": "number",
"minimum": 0
},
"kv_write": {
"type": "number",
"minimum": 0
},
"egress_gb": {
"type": "number",
"minimum": 0
},
"file_read": {
"type": "number",
"minimum": 0
},
"file_write": {
"type": "number",
"minimum": 0
},
"error_event": {
"type": "number",
"minimum": 0
},
"revenue_usd": {
"type": "number",
"minimum": 0
},
"transaction": {
"type": "number",
"minimum": 0
},
"bandwidth_gb": {
"type": "number",
"minimum": 0
},
"compute_hour": {
"type": "number",
"minimum": 0
},
"ai_input_ktok": {
"type": "number",
"minimum": 0
},
"db_storage_gb": {
"type": "number",
"minimum": 0
},
"search_record": {
"type": "number",
"minimum": 0
},
"ai_output_ktok": {
"type": "number",
"minimum": 0
},
"file_egress_gb": {
"type": "number",
"minimum": 0
},
"search_request": {
"type": "number",
"minimum": 0
},
"file_storage_gb": {
"type": "number",
"minimum": 0
},
"request_million": {
"type": "number",
"minimum": 0
},
"video_min_stored": {
"type": "number",
"minimum": 0
},
"video_min_delivered": {
"type": "number",
"minimum": 0
}
},
"description": "Usage per active user a month, e.g. {\"sms\": 2, \"file_storage_gb\": 0.2, \"ai_input_ktok\": 30}. Replaces the profile and the filled-in defaults for those units; a plan's questions say which unit each answer sets. Units: mau, seat, request_million, bandwidth_gb, db_storage_gb, egress_gb, email, event, error_event, compute_hour, revenue_usd, transaction, ai_input_ktok, ai_output_ktok, kv_read, kv_write, ci_hour, file_storage_gb, file_egress_gb, file_write, file_read, video_min_stored, video_min_delivered, sms, job_run, search_request, search_record.",
"additionalProperties": false
},
"categories": {
"type": "array",
"items": {
"enum": [
"hosting",
"database",
"auth",
"email",
"payments",
"observability",
"ai",
"edge",
"app_store",
"coding",
"storage",
"video",
"sms",
"push",
"jobs",
"search"
],
"type": "string"
},
"description": "Plan these categories. Default: a build tool (an AI app builder or coding agent) plus what the description needs (a web app gets hosting, database, sign-in, email, payments and observability; an on-device app only what it names; a one-sentence idea such as a pantry tracker what that kind of app usually needs)."
},
"profile_id": {
"type": "string",
"description": "Usage profile: b2b_saas (default), consumer_app, booking_marketplace, content_site, ios_on_device. Defaults to the app type's profile."
},
"description": {
"type": "string",
"maxLength": 60000,
"description": "Plain-language product description or PRD text."
},
"task_intent": {
"type": "string",
"maxLength": 300,
"description": "What the user is trying to do, in a few words, e.g. \"pick a stack for an MVP\" or \"cost section for an investor deck\". No names or contact details."
},
"mau_forecast": {
"type": "number",
"maximum": 100000000,
"minimum": 0,
"description": "Monthly active users expected at the forecast horizon."
},
"builder_profile": {
"enum": [
"developer",
"non_developer",
"unknown"
],
"type": "string",
"description": "Who builds it. developer picks an AI coding agent, non_developer an app builder (and counts setup work double). Given without categories, the plan adds the build tool (coding)."
},
"forecast_months": {
"type": "integer",
"maximum": 36,
"minimum": 1
},
"budget_monthly_usd": {
"type": "number",
"minimum": 0
},
"output_destination": {
"enum": [
"chat",
"document",
"code"
],
"type": "string",
"description": "Where the answer goes. chat (default): summary for the conversation. document: a section ready to paste into a PRD. code: adds npm packages and environment variable names for each pick."
},
"revenue_per_mau_usd": {
"type": "number",
"minimum": 0,
"description": "Average monthly revenue per active user (for payment fees)."
},
"transactions_per_mau": {
"type": "number",
"minimum": 0,
"description": "Payments per active user per month."
}
}
}| preview | boolean | With invoice_text: read it and show what was found, without saving. |
| invoice_text | string | One invoice's text, copied from the PDF or the email. |
{
"type": "object",
"properties": {
"id": {
"type": "string",
"maxLength": 64,
"description": "The saved stack or audit. Leave out to keep the bills on the account."
},
"csv": {
"type": "string",
"maxLength": 200000,
"description": "A CSV billing export: a header row, then one row per charge."
},
"bills": {
"type": "array",
"items": {
"type": "object",
"required": [
"month",
"amount_usd"
],
"properties": {
"name": {
"type": "string",
"maxLength": 120
},
"month": {
"type": "string",
"maxLength": 20
},
"vendor_id": {
"type": "string",
"maxLength": 64
},
"amount_usd": {
"description": "Dollars, as a number or text such as \"$1,204.50\"."
}
}
},
"maxItems": 200
},
"month": {
"type": "string",
"description": "Which month to compare. Default: the newest with bills."
},
"remove": {
"type": "object",
"required": [
"month"
],
"properties": {
"month": {
"type": "string"
},
"vendor": {
"type": "string",
"description": "vendor_id or name; leave out to remove the whole month."
}
}
},
"preview": {
"type": "boolean",
"description": "With invoice_text: read it and show what was found, without saving."
},
"invoice_text": {
"type": "string",
"maxLength": 60000,
"description": "One invoice's text, copied from the PDF or the email."
}
}
}{
"type": "object",
"properties": {
"id": {
"type": "string",
"maxLength": 80,
"description": "Exact provider id, e.g. \"neon\"."
},
"limit": {
"type": "integer",
"default": 25,
"maximum": 500,
"minimum": 1
},
"query": {
"type": "string",
"maxLength": 200,
"description": "Words to match, e.g. \"sms\", \"vector\", \"merchant of record\", \"Supabase\", or a company such as \"Amazon\" for all its services."
},
"offset": {
"type": "integer",
"default": 0,
"minimum": 0
},
"status": {
"enum": [
"researched",
"candidate"
],
"type": "string",
"description": "researched: prices read from the pricing page. candidate: listed without prices."
},
"category": {
"type": "string",
"maxLength": 40,
"description": "Directory category id, e.g. database, email, sms, llm, vector_db. Every result lists the categories with counts."
},
"in_cost_engine": {
"type": "boolean",
"description": "true: only providers estimate_cost and plan_stack can price."
}
},
"additionalProperties": false
}| category | string |
| platform | string | Runs on this platform, whatever else it runs on. |
{
"type": "object",
"properties": {
"id": {
"type": "string",
"maxLength": 80,
"description": "Exact product id."
},
"form": {
"enum": [
"web",
"app",
"both",
"desktop"
],
"type": "string",
"description": "How it ships. web: a website (maybe with a desktop app). app: a mobile app only. both: a website and a mobile app. desktop: a desktop app with no website (maybe with a mobile app)."
},
"sort": {
"enum": [
"featured",
"newest",
"name",
"stars"
],
"type": "string",
"default": "featured",
"description": "stars: most GitHub stars first."
},
"tech": {
"type": "string",
"maxLength": 80,
"description": "A tech key from facets.tech: a directory id such as \"supabase\", or a framework such as \"next-js\"."
},
"limit": {
"type": "integer",
"default": 24,
"maximum": 200,
"minimum": 1
},
"query": {
"type": "string",
"maxLength": 200,
"description": "Words to match against names, taglines, makers and the stack, e.g. \"habit tracker\", \"Expo\"."
},
"offset": {
"type": "integer",
"default": 0,
"minimum": 0
},
"pricing": {
"enum": [
"free",
"freemium",
"paid",
"open_source"
],
"type": "string"
},
"category": {
"enum": [
"ai",
"commerce",
"customer_support",
"data_analytics",
"design",
"developer_tools",
"education",
"energy",
"finance",
"government",
"hardware",
"health",
"hr",
"industrial",
"legal",
"marketing",
"media",
"productivity",
"sales",
"security",
"social",
"travel",
"other"
],
"type": "string"
},
"platform": {
"enum": [
"web",
"ios",
"android",
"desktop"
],
"type": "string",
"description": "Runs on this platform, whatever else it runs on."
}
},
"additionalProperties": false
}| build_progress |
| object |
| Tick build-pack steps done (true) or not done (false), by step id from get_build_pack: domain or the step's category (hosting, database, auth…). Plans only. |
{
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "string",
"maxLength": 64
},
"name": {
"type": "string",
"maxLength": 80
},
"email": {
"type": "string",
"maxLength": 254,
"description": "Where alerts go. A confirmation link is sent first; only ask for it when the user wants alerts."
},
"alerts": {
"type": "boolean",
"description": "Email the user when prices, cost jumps or credits change this stack."
},
"mau_now": {
"type": "number",
"minimum": 0,
"description": "Monthly active users today."
},
"unlink_repo": {
"type": "boolean",
"description": "Forget the repo linked with check_build, and its last check."
},
"mau_forecast": {
"type": "number",
"minimum": 0,
"description": "Users expected at the end of the forecast (12 months for audits)."
},
"build_progress": {
"type": "object",
"properties": {
"ai": {
"type": "boolean"
},
"sms": {
"type": "boolean"
},
"auth": {
"type": "boolean"
},
"edge": {
"type": "boolean"
},
"jobs": {
"type": "boolean"
},
"push": {
"type": "boolean"
},
"email": {
"type": "boolean"
},
"video": {
"type": "boolean"
},
"coding": {
"type": "boolean"
},
"domain": {
"type": "boolean"
},
"search": {
"type": "boolean"
},
"hosting": {
"type": "boolean"
},
"storage": {
"type": "boolean"
},
"database": {
"type": "boolean"
},
"payments": {
"type": "boolean"
},
"app_store": {
"type": "boolean"
},
"observability": {
"type": "boolean"
}
},
"description": "Tick build-pack steps done (true) or not done (false), by step id from get_build_pack: domain or the step's category (hosting, database, auth…). Plans only.",
"additionalProperties": false
}
}
}