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 keys —
POST /v1/keyswith 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
| Resource | URL | What it is |
|---|---|---|
| Site llms.txt | vendorstacks.com/llms.txt | Orientation + link map for this site |
| Everything, one file | vendorstacks.com/llms-full.txt | All 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.md | Every 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.
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:falsesemantics, 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
{
"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"]
}
}
}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
{
"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"]
}
}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— wrapsGET /v1/check(1 credit flat, with the timeout guardrails baked into the tool description) - ›
prospect— wrapsGET /v1/prospectreverse lookup with pagination - ›
balance— wrapsGET /v1/balanceso 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
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()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=trueonly 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 (429daily_scan_limit); any pack removes the cap. - ›On 402, relay `topup_url` verbatim with
credits_neededand stop. Don't retry, don't paraphrase the URL, don't switch keys. - ›Warn below 20 credits.
GET /v1/balanceis 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.