{
  "openapi": "3.1.0",
  "info": {
    "title": "Fund Momentum API (MCP + REST)",
    "version": "1.4.0",
    "description": "VC fund intelligence over MCP (JSON-RPC at /_api/mcp) and as plain REST under /_api/v1 for Make, Zapier, n8n and HubSpot. The REST endpoints call the same tool handlers as MCP: same validation, auth, quotas, pricing and MPP 402 behaviour. No registration and no email required: free tools answer without any credential, and paid tools accept inline payment over MPP with no account."
  },
  "servers": [{ "url": "https://fundmomentum.vc" }],
  "x-service-info": {
    "categories": ["financial-data", "venture-capital", "research"],
    "docs": {
      "apiReference": "https://fundmomentum.vc/mcp",
      "homepage": "https://fundmomentum.vc",
      "llms": "https://fundmomentum.vc/_api/llms-txt"
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": { "type": "apiKey", "in": "header", "name": "X-API-Key" },
      "Bearer": { "type": "http", "scheme": "bearer" }
    },
    "parameters": {
      "slug": { "name": "slug", "in": "query", "required": true, "description": "Fund slug exactly as returned by GET /_api/v1/funds. Do not construct it from a name.", "schema": { "type": "string" } },
      "stage": { "name": "stage", "in": "query", "description": "Funding stage. Accepted values: GET /_api/v1/enums.", "schema": { "type": "string", "enum": ["growth", "late_stage", "pre_seed", "seed", "series_a", "series_b", "series_c"] } },
      "country": { "name": "country", "in": "query", "description": "Country spelled out in full (not an ISO code for funds).", "schema": { "type": "string" } },
      "industry": { "name": "industry", "in": "query", "description": "Industry focus. Accepted values: GET /_api/v1/enums.", "schema": { "type": "string" } },
      "lpType": { "name": "lp_type", "in": "query", "description": "LP type. Accepted values: GET /_api/v1/enums.", "schema": { "type": "string" } }
    },
    "schemas": {
      "Meta": {
        "type": "object",
        "description": "Usage and billing for this call (calls_remaining, credits, resets_at, payment_receipt when paid).",
        "additionalProperties": true
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["code", "message", "error_reason"],
            "properties": {
              "code": { "type": "string", "example": "invalid_args" },
              "message": { "type": "string" },
              "error_reason": { "type": "string", "example": "invalid_args" },
              "next_step": { "description": "What to do next: a sentence or an object like {action, url}.", "oneOf": [{ "type": "string" }, { "type": "object" }, { "type": "null" }] },
              "resets_at": { "type": "string", "format": "date-time", "description": "Present on 429." },
              "details": { "type": "object", "additionalProperties": true }
            }
          }
        }
      },
      "ToolResult": {
        "type": "object",
        "description": "The tool result object, as the MCP tool returns it, plus _meta.",
        "properties": { "_meta": { "$ref": "#/components/schemas/Meta" } },
        "additionalProperties": true
      }
    },
    "responses": {
      "Ok": { "description": "Tool result with _meta (usage, credits).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ToolResult" } } } },
      "BadRequest": { "description": "invalid_args. Checked before any payment; nothing was charged.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Unauthorized": { "description": "missing_key or invalid_key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "Forbidden": { "description": "lp_access_required or insufficient_tier.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "NotFound": { "description": "not_found (for example an unknown slug). Checked before any payment.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } },
      "PaymentRequired": { "description": "Payment required. Carries a standard WWW-Authenticate: Payment challenge (MPP, Tempo USDC), or no_credits on an agent key." },
      "TooMany": { "description": "Rate or spend limit reached. Carries resets_at.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } } }
    }
  },
  "paths": {
    "/_api/mcp": {
      "post": {
        "summary": "Call an MCP tool. Free tools answer without payment; Pro tools are priced per call.",
        "x-payment-info": {
          "offers": [
            {
              "amount": "120000",
              "currency": "0x20c000000000000000000000b9537d11c60e8b50",
              "method": "tempo",
              "intent": "charge",
              "description": "get_fund_signals - EUR 0.10 per call. Indicative; the 402 challenge is authoritative."
            },
            {
              "amount": "120000",
              "currency": "0x20c000000000000000000000b9537d11c60e8b50",
              "method": "tempo",
              "intent": "charge",
              "description": "get_gp_profile - EUR 0.10 per call. Indicative; the 402 challenge is authoritative."
            },
            {
              "amount": "290000",
              "currency": "0x20c000000000000000000000b9537d11c60e8b50",
              "method": "tempo",
              "intent": "charge",
              "description": "match_startup - EUR 0.25 per call. Indicative; the 402 challenge is authoritative."
            }
          ]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["jsonrpc", "method"],
                "properties": {
                  "jsonrpc": { "type": "string" },
                  "id": { "type": "integer" },
                  "method": { "type": "string" },
                  "params": {
                    "type": "object",
                    "properties": {
                      "name": {
                        "type": "string",
                        "enum": [
                          "search_funds",
                          "get_fund",
                          "get_changes",
                          "get_fund_signals",
                          "get_gp_profile",
                          "match_startup"
                        ]
                      },
                      "arguments": { "type": "object" }
                    }
                  }
                }
              },
              "example": {
                "jsonrpc": "2.0",
                "method": "tools/call",
                "params": {
                  "name": "get_fund_signals",
                  "arguments": { "slug": "100x100-fund-ii" }
                },
                "id": 1
              }
            }
          }
        },
        "responses": {
          "200": { "description": "Tool result. Usage and billing are in result._meta." },
          "402": { "description": "Payment required. Carries a standard WWW-Authenticate: Payment challenge (MPP, Tempo USDC)." },
          "429": { "description": "Rate or spend limit reached. Carries resets_at." }
        }
      }
    },
    "/_api/v1/funds": {
      "get": {
        "operationId": "searchFunds",
        "summary": "Search funds (REST equivalent of the MCP tool search_funds). Keyless allowed; 1 credit per fund returned on an agent key, default limit 10.",
        "parameters": [
          { "$ref": "#/components/parameters/stage" },
          { "$ref": "#/components/parameters/country" },
          { "$ref": "#/components/parameters/industry" },
          { "name": "closed_since", "in": "query", "description": "Only funds whose latest covered close is on or after this date (YYYY-MM-DD).", "schema": { "type": "string", "format": "date" } },
          { "name": "limit", "in": "query", "description": "Results to return. Default 10; the maximum depends on the plan and an over-cap value is rejected, not clamped.", "schema": { "type": "integer", "minimum": 1 } },
          { "name": "offset", "in": "query", "description": "Paging offset. Pro and paying agents only.", "schema": { "type": "integer", "minimum": 0 } }
        ],
        "security": [{}, { "ApiKey": [] }, { "Bearer": [] }],
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/_api/v1/funds/detail": {
      "get": {
        "operationId": "getFund",
        "summary": "Fund profile (get_fund). Keyless allowed. The slug is a query parameter because the platform has no path parameters.",
        "parameters": [{ "$ref": "#/components/parameters/slug" }],
        "security": [{}, { "ApiKey": [] }, { "Bearer": [] }],
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/_api/v1/funds/signals": {
      "get": {
        "operationId": "getFundSignals",
        "summary": "Investor signals (get_fund_signals). Pro, or EUR 0.10 per call: without a key the response is a real 402 with a payment challenge.",
        "x-payment-info": {
          "offers": [
            { "amount": "120000", "currency": "0x20c000000000000000000000b9537d11c60e8b50", "method": "tempo", "intent": "charge", "description": "get_fund_signals - EUR 0.10 per call. Indicative; the 402 challenge is authoritative." }
          ]
        },
        "parameters": [{ "$ref": "#/components/parameters/slug" }],
        "security": [{ "ApiKey": [] }, { "Bearer": [] }, {}],
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/_api/v1/funds/gps": {
      "get": {
        "operationId": "getGpProfile",
        "summary": "General Partner profiles (get_gp_profile). Pro, or EUR 0.10 per call: without a key the response is a real 402 with a payment challenge.",
        "x-payment-info": {
          "offers": [
            { "amount": "120000", "currency": "0x20c000000000000000000000b9537d11c60e8b50", "method": "tempo", "intent": "charge", "description": "get_gp_profile - EUR 0.10 per call. Indicative; the 402 challenge is authoritative." }
          ]
        },
        "parameters": [
          { "$ref": "#/components/parameters/slug" },
          { "name": "gp_name", "in": "query", "description": "Optional GP name to filter by.", "schema": { "type": "string" } }
        ],
        "security": [{ "ApiKey": [] }, { "Bearer": [] }, {}],
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/_api/v1/match": {
      "post": {
        "operationId": "matchStartup",
        "summary": "Match a startup to funds (match_startup). Pro, or EUR 0.25 per call. Takes 3 to 10 seconds.",
        "x-payment-info": {
          "offers": [
            { "amount": "290000", "currency": "0x20c000000000000000000000b9537d11c60e8b50", "method": "tempo", "intent": "charge", "description": "match_startup - EUR 0.25 per call. Indicative; the 402 challenge is authoritative." }
          ]
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["description"],
                "properties": {
                  "description": { "type": "string", "maxLength": 500, "description": "Startup description." },
                  "country": { "type": "string" },
                  "stage": { "type": "string", "description": "Accepted values: GET /_api/v1/enums." }
                }
              }
            }
          }
        },
        "security": [{ "ApiKey": [] }, { "Bearer": [] }, {}],
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/PaymentRequired" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/_api/v1/lps/coverage": {
      "get": {
        "operationId": "checkLpCoverage",
        "summary": "LP coverage counts (check_lp_coverage). Free, keyless allowed, counts only.",
        "parameters": [
          { "$ref": "#/components/parameters/country" },
          { "$ref": "#/components/parameters/lpType" }
        ],
        "security": [{}, { "ApiKey": [] }, { "Bearer": [] }],
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/_api/v1/lps": {
      "get": {
        "operationId": "searchLps",
        "summary": "LP records (search_lps). LP Radar subscribers only; never sold per call. Max 25 per call.",
        "parameters": [
          { "$ref": "#/components/parameters/country" },
          { "$ref": "#/components/parameters/lpType" },
          { "name": "backs_emerging_managers", "in": "query", "description": "true: only LPs that have backed an emerging manager.", "schema": { "type": "boolean" } },
          { "name": "limit", "in": "query", "description": "1 to 25. Above 25 is rejected, not clamped.", "schema": { "type": "integer", "minimum": 1, "maximum": 25, "default": 10 } }
        ],
        "security": [{ "ApiKey": [] }, { "Bearer": [] }],
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/_api/v1/changes": {
      "get": {
        "operationId": "getChanges",
        "summary": "Funds changed since a timestamp (get_changes). Keyless allowed. Send the previous ETag as If-None-Match, with the same since, for a 304 when unchanged. A 304 and an empty poll are both free: polls that return no changes do not count against quotas or credits (the per-minute pace limit still applies). _meta.billed says whether a call was charged. REST v1 is frozen: fields are only added, never renamed or removed; breaking changes ship as /v2.",
        "parameters": [
          { "name": "since", "in": "query", "description": "ISO 8601 timestamp or unix epoch in ms. Omit for the last 7 days.", "schema": { "type": "string" } },
          { "name": "limit", "in": "query", "description": "1 to 200.", "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 } },
          { "name": "If-None-Match", "in": "header", "description": "ETag from the previous response.", "schema": { "type": "string" } }
        ],
        "security": [{}, { "ApiKey": [] }, { "Bearer": [] }],
        "responses": {
          "200": { "description": "Changes, with an ETag header and _meta.", "headers": { "ETag": { "schema": { "type": "string" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ToolResult" } } } },
          "304": { "description": "Nothing changed in this window. Empty body." },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "429": { "$ref": "#/components/responses/TooMany" }
        }
      }
    },
    "/_api/v1/usage": {
      "get": {
        "operationId": "getUsage",
        "summary": "Tier, calls or credits left and reset time (get_usage). Free, keyless allowed, never charges.",
        "security": [{}, { "ApiKey": [] }, { "Bearer": [] }],
        "responses": {
          "200": { "$ref": "#/components/responses/Ok" },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/_api/v1/enums": {
      "get": {
        "operationId": "getEnums",
        "summary": "Accepted values for stage, industry and lp_type, for dropdowns. Keyless, unmetered.",
        "responses": {
          "200": {
            "description": "Each enum as values[] and options[{value,label}].",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "stage": { "type": "object" },
                    "industry": { "type": "object" },
                    "lp_type": { "type": "object" },
                    "limits": { "type": "object" }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/_api/webhooks/subscribe": {
      "post": {
        "operationId": "webhookSubscribe",
        "summary": "Subscribe an HTTPS URL to close_updated events. Available on every plan; the per-plan subscription and monthly event allowances are stated in https://fundmomentum.vc/_api/mcp-tools and returned by GET /_api/webhooks/list. Returns a stable subscription_id and the signing secret once. A second subscription on Free or Starter returns 403 with an upgrade next_step.",
        "security": [{ "ApiKey": [] }, { "Bearer": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["url"],
                "properties": {
                  "url": { "type": "string", "format": "uri" },
                  "filters": { "type": "object", "properties": { "stage": { "type": "string" }, "country": { "type": "string" }, "industry": { "type": "string" } } },
                  "description": { "type": "string", "maxLength": 200 }
                }
              }
            }
          }
        },
        "responses": {
          "201": { "description": "Created. Contains subscription_id, subscription, secret (shown once)." },
          "400": { "description": "Invalid url, filters or event." },
          "401": { "description": "Missing or invalid key." },
          "403": { "description": "subscription_limit on Free or Starter, with next_step pointing to /pricing." },
          "409": { "description": "subscription_limit on Pro or Enterprise." }
        }
      }
    },
    "/_api/webhooks/list": {
      "get": {
        "operationId": "webhookList",
        "summary": "Subscriptions (subscription_id, url, filters, created_at, events_this_month), the plan allowance, delivery_paused_until and next_step when the monthly allowance is used up, and the last 20 deliveries.",
        "security": [{ "ApiKey": [] }, { "Bearer": [] }],
        "responses": { "200": { "description": "Subscriptions and allowance." }, "401": { "description": "Missing or invalid key." } }
      }
    },
    "/_api/webhooks/test": {
      "post": {
        "operationId": "webhookTest",
        "summary": "Send a signed ping to a subscription now.",
        "security": [{ "ApiKey": [] }, { "Bearer": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["subscription_id"], "properties": { "subscription_id": { "type": "string", "example": "sub_12" } } } } } },
        "responses": { "200": { "description": "Delivered." }, "404": { "description": "No such subscription on this key." }, "502": { "description": "The receiver did not answer 2xx." } }
      }
    },
    "/_api/webhooks/delete": {
      "post": {
        "operationId": "webhookDelete",
        "summary": "Delete a subscription by subscription_id (preferred, for Make detach) or by url.",
        "security": [{ "ApiKey": [] }, { "Bearer": [] }],
        "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "properties": { "subscription_id": { "type": "string", "example": "sub_12" }, "url": { "type": "string", "format": "uri" } } } } } },
        "responses": { "200": { "description": "Deleted." }, "404": { "description": "No such subscription on this key." } }
      }
    }
  },
  "x-fund-momentum": {
    "maintenance_note": "Hand-maintained static file, because MPP discovery requires a document at the web root and Floot serves root paths from static/ only. If MCP_TOOLS or helpers/agentPricing change, update the tool enum and the offers here to match. When an endpoint under endpoints/v1 is added or changed, update its path here too. Webhook allowances live in helpers/webhookPlans and are deliberately NOT written as numbers in this file. Amounts are smallest-unit integer strings (6 decimals for USDC) and currency is the Tempo USDC token CONTRACT ADDRESS, not a currency code - the validator rejects both otherwise. That address is NETWORK-SPECIFIC and changes when you switch networks: Tempo testnet USDC is 0x20c0...0000, mainnet USDC is 0x20c0...8b50. Do not type it from memory - read supported_tokens[].token_contract_address off the crypto.deposit_address object for the address actually configured in TEMPO_DEPOSIT_ADDRESS. A leftover testnet address here advertises a token that does not exist on mainnet.",
    "free_tools": ["search_funds", "get_fund", "get_changes"],
    "free_tool_price_eur": 0.01,
    "rest_contract": "REST v1 contract: fields are only ever added, never renamed or removed. Breaking changes ship as /v2. Changelog: https://fundmomentum.vc/_api/api-changelog",
    "rest_api": "Every tool is also available as plain REST under /_api/v1 (no JSON-RPC envelope). Floot endpoints cannot take path parameters, so a fund slug is the query parameter slug.",
    "closes": "get_fund returns last_close {type, date, date_basis, date_basis_note, amount_m, currency, size_display, target_m, source_url, primary_url}. date is the day Fund Momentum reported the close (date_basis \"reported\"), not necessarily the signing day. search_funds closed_since (YYYY-MM-DD) compares against that date, lists newest close first and skips launch and target_only entries. A post applies only to the fund whose vintage it names (no numeral means Fund I); posts covering several funds never change a fund's size; closes found only on the open web are not written into fund records. A fund without last_close has not been covered, which does not mean it has not closed.",
    "keyless": "search_funds, get_fund, get_changes (7-day window) and check_lp_coverage answer with no credential at all, 10 calls per caller per UTC day.",
    "register": "POST {\"agent_name\":\"...\"} to /_api/agent/register for a key and 25 free credits. Email is optional.",
    "topup": "/_api/agent/credits/topup",
    "server_card": "https://fundmomentum.vc/_api/well-known/mcp/server-card",
    "mcp_endpoint": "https://fundmomentum.vc/_api/mcp"
  }
}
