VendorStacks
On this section

For Agents

Everything needed to wire VendorStacks into an agent — skill install, tool definitions, framework wrappers, and the spending rules. Mirrored as markdown at /docs/agents.md and inside /llms-full.txt. The lighter marketing overview lives at /skills.

Overview

VendorStacks is built to be operated by agents, not just called by code a human wrote. Four properties make that work:

  • Instant keysPOST /v1/keys with an email returns the key in the same response. No verification click, so an agent can self-provision mid-task.
  • Deterministic output — no LLM in the extraction path. The same disclosure always parses to the same stack, every claim carries a verbatim quote, and the API never invents a vendor. Your agent's stochastic layer stays the only one to audit.
  • 402 handoff — running out of credits returns a machine-readable paywall with the exact shortfall and a topup_url. Nothing is charged; the retry is idempotent.
  • Self-documenting responses — error messages carry their own fix (401 tells you how to get a key; 402 tells you what it costs), so an agent can recover without a docs lookup.

Discovery surface

ResourceURLWhat it is
Site llms.txtvendorstacks.com/llms.txtOrientation + link map for this site
Everything, one filevendorstacks.com/llms-full.txtAll docs, guides, and API reference as one markdown document
Markdown mirrors/docs.md · /docs/api.md · /docs/guides.md · /docs/guides/{slug}.md · /docs/agents.mdEvery docs page as plain markdown
OpenAPI 3.1[vendorradar-production.up.railway.app /openapi.json](https://vendorradar-production.up.railway.app /openapi.json)Spec for codegen and tool generation
Agent guide[vendorradar-production.up.railway.app /agents](https://vendorradar-production.up.railway.app /agents)The API's own agent integration guide
Skill file[vendorradar-production.up.railway.app /skill](https://vendorradar-production.up.railway.app /skill)Installable SKILL.md (see below)

Claude Code / Claude skills

The API serves an installable skill file — a markdown document written for a model, teaching the endpoints, the credit economics, and the cost-discipline rules in one fetch.

Install (Claude Code)
mkdir -p .claude/skills/vendorstacks
curl -s https://vendorradar-production.up.railway.app
/skill \
  -o .claude/skills/vendorstacks/SKILL.md

Claude Code discovers skills in .claude/skills/ automatically and loads this one when a task involves vendor stacks, subprocessor disclosures, entity due diligence, or prospecting by vendor — the skill's frontmatter description is what triggers it.

  • What the skill teaches: endpoint map with the flat 1-credit pricing, self-provisioning via /v1/keys, timeout discipline for live scans, found:false semantics, and the 402 relay pattern.
  • For other skill-compatible harnesses (or plain system prompts), fetch the same file and paste it into context — it is self-contained markdown.

OpenAI function calling

Function schema
{
  "type": "function",
  "function": {
    "name": "vendorstacks_check_entity",
    "description": "Look up whether an entity (by domain) publishes a subprocessor/DPA disclosure and which vendors it names, as a structured vendor stack across 13 categories with quoted evidence. Every lookup costs 1 credit flat: indexed answers return <1s, unindexed domains live-scan automatically (15-90s, same price) — use a 120s timeout. fresh=true forces a re-scan of an indexed domain at the same price. found=false means no disclosure was located, NOT that the entity uses no vendors.",
    "parameters": {
      "type": "object",
      "properties": {
        "url": { "type": "string", "description": "Domain, e.g. acme.com" },
        "fresh": { "type": "boolean", "description": "Force live re-scan. Default false." }
      },
      "required": ["url"]
    }
  }
}
Usage (Responses API)
import OpenAI from "openai";
const client = new OpenAI();

const response = await client.responses.create({
  model: "gpt-5",
  input: "What vendors does notify-hq.com disclose?",
  tools: [vendorstacksTool], // the schema above
});

for (const item of response.output) {
  if (item.type === "function_call" && item.name === "vendorstacks_check_entity") {
    const { url, fresh } = JSON.parse(item.arguments);
    const data = await fetch(
      `https://vendorradar-production.up.railway.app
/v1/check?url=${encodeURIComponent(url)}` + (fresh ? "&fresh=true" : ""),
      { headers: { Authorization: `Bearer ${process.env.VENDORSTACKS_KEY}` } },
    ).then(r => r.json());
    // feed `data` back as the function_call_output item
  }
}

The same schema works in the Assistants API and Chat Completions tools array unchanged.

Anthropic tool use

Tool definition (Anthropic format)
{
  "name": "vendorstacks_check_entity",
  "description": "Look up whether an entity (by domain) publishes a subprocessor/DPA disclosure and which vendors it names, as a structured vendor stack across 13 categories with quoted evidence. Every lookup costs 1 credit flat: indexed answers return <1s, unindexed domains live-scan automatically (15-90s, same price) — use a 120s timeout. fresh=true forces a re-scan of an indexed domain at the same price. found=false means no disclosure was located, NOT that the entity uses no vendors.",
  "input_schema": {
    "type": "object",
    "properties": {
      "url": { "type": "string", "description": "Domain, e.g. acme.com" },
      "fresh": { "type": "boolean", "description": "Force live re-scan. Default false." }
    },
    "required": ["url"]
  }
}
Messages API example
import Anthropic from "@anthropic-ai/sdk";
const anthropic = new Anthropic();

const msg = await anthropic.messages.create({
  model: "claude-sonnet-4-5",
  max_tokens: 1024,
  tools: [vendorstacksTool], // the definition above
  messages: [{ role: "user", content: "Vet notify-hq.com's subprocessors." }],
});

const call = msg.content.find(b => b.type === "tool_use");
if (call) {
  const { url, fresh } = call.input;
  const data = await fetch(/* same fetch as the OpenAI example */);
  // return as { type: "tool_result", tool_use_id: call.id, content: JSON.stringify(data) }
}

MCP

Status: hosted MCP server — coming. Not shipped yet; this section describes what is planned, not what exists today.

The hosted server will speak the Model Context Protocol over streamable HTTP and expose the core operations as MCP tools:

  • check_entity — wraps GET /v1/check (1 credit flat, with the timeout guardrails baked into the tool description)
  • prospect — wraps GET /v1/prospect reverse lookup with pagination
  • balance — wraps GET /v1/balance so agents can check spend headroom for free

Until it ships, the practical equivalents are the skill file for Claude-family harnesses and the function-calling definitions above for everything else — they cover the same three operations.

LangChain / LlamaIndex

LangChain (python)
import os, requests
from langchain_core.tools import tool

@tool
def vendorstacks_check_entity(url: str, fresh: bool = False) -> dict:
    """Look up whether an entity (by domain) publishes a subprocessor/DPA disclosure and which vendors it names, as a structured vendor stack across 13 categories with quoted evidence. Every lookup costs 1 credit flat: indexed answers return <1s, unindexed domains live-scan automatically (15-90s, same price) — use a 120s timeout. fresh=true forces a re-scan of an indexed domain at the same price. found=false means no disclosure was located, NOT that the entity uses no vendors."""
    r = requests.get(
        "https://vendorradar-production.up.railway.app
/v1/check",
        params={"url": url, **({"fresh": "true"} if fresh else {})},
        headers={"Authorization": f"Bearer {os.environ['VENDORSTACKS_KEY']}"},
        timeout=120 if fresh else 15,
    )
    if r.status_code == 402:
        body = r.json()
        return {"needs_human": True, "topup_url": body["topup_url"],
                "credits_needed": body["credits_needed"]}
    return r.json()
LlamaIndex (python)
import os, requests
from llama_index.core.tools import FunctionTool

def check_entity(url: str, fresh: bool = False) -> dict:
    """Look up whether an entity (by domain) publishes a subprocessor/DPA disclosure and which vendors it names, as a structured vendor stack across 13 categories with quoted evidence. Every lookup costs 1 credit flat: indexed answers return <1s, unindexed domains live-scan automatically (15-90s, same price) — use a 120s timeout. fresh=true forces a re-scan of an indexed domain at the same price. found=false means no disclosure was located, NOT that the entity uses no vendors."""
    r = requests.get(
        "https://vendorradar-production.up.railway.app
/v1/check",
        params={"url": url, **({"fresh": "true"} if fresh else {})},
        headers={"Authorization": f"Bearer {os.environ['VENDORSTACKS_KEY']}"},
        timeout=120 if fresh else 15,
    )
    return r.json()

vendorstacks_tool = FunctionTool.from_defaults(fn=check_entity,
                                              name="vendorstacks_check_entity")

Cost discipline for agents

Agents spend real credits. These rules belong in the tool description or system prompt — the model, not your wrapper code, makes the spending decisions:

  • Flat pricing, variable latency. Every lookup is 1 credit — scans cost nothing extra. The thing to budget is wall-clock time: unindexed domains take 15–90s. Pass fresh=true only when the user explicitly asked for a re-scan (same price, always slow).
  • 120-second timeouts. Any lookup can trigger a live scan; a short timeout burns the wait and re-spends on retry.
  • Bulk lookups cost 1 credit each — budget time, not just credits. Before looping a large list, quote the credit total (count × 1) and warn that unindexed domains add 15–90s each of wall-clock time. On the free plan, live scans are capped at 5/day (429 daily_scan_limit); any pack removes the cap.
  • On 402, relay `topup_url` verbatim with credits_needed and stop. Don't retry, don't paraphrase the URL, don't switch keys.
  • Warn below 20 credits. GET /v1/balance is free — check it at the start of long runs and surface a warning before the balance can strand a task mid-list.

Worked examples: Handle 402s gracefully and the agent guide.

Machine payments (Tempo / MPP)

Status: in development. Nothing described here is live; no dates are promised. This section exists so agent builders know the direction.

Today, a 402 needs a human with a card. The plan is to remove that hop for autonomous workloads: VendorStacks intends to accept stablecoin micropayments from agents via the Machine Payments Protocol (MPP) on Tempo, a payments-focused L1. The intended flow:

  • The 402 response carries machine-readable payment details (amount, asset, destination) alongside the existing topup_url.
  • The agent settles the micropayment on-chain from its own wallet — no checkout page, no card, no human.
  • The agent retries the same request with proof of settlement; the API verifies and serves it.

The existing 402 contract stays unchanged for humans — machine payment details will be additive fields. When this ships it will be announced on the blog and documented here first.