Read your data over the HTTP API
One endpoint reads every part of a project's data. Point an agent or a script at it and pull briefs, pains, and source posts as JSON.
Last updated July 3, 2026
On this page
- Base URL
- Authenticate
- The one endpoint: POST /api/v1/invoke
- One example call
- Projects: which one you're reading
- Errors
- Discover capabilities: GET /api/v1/capabilities
- Capability reference
- project.memory
- project.list
- briefs.list
- briefs.get
- pains.list
- pains.get
- pains.sources
- posts.get
- runs.get
- competitors.list
- Write capabilities
- memory.edit
- A common flow: ground on a pain's source posts
The dist0 HTTP API lets an agent or a script read a project's data as JSON — the same briefs, pains, and source posts you see in your daily email and Slack. There is one endpoint to call and one endpoint to list what you can ask for.
Base URL
https://dist0.comAuthenticate
Every request carries your API key as a bearer token in the Authorization
header:
Authorization: Bearer <your-key>You make a key in the dashboard — see Authentication. A request
with no key, or a key the server doesn't recognize, comes back as 403 forbidden.
The one endpoint: POST /api/v1/invoke
You name a capability (e.g. pains.list), pass its input, and get that
capability's data back. Adding capabilities never changes how you call — the
shape of the request is always the same.
Request body
| Field | Type | Required | What it does |
|---|---|---|---|
capability | string | yes | The capability name, e.g. briefs.list. |
input | object | no | The capability's input. Defaults to {}. |
project | string | no | A substring of the project's URL to select it (dist0.com matches https://dist0.com). Defaults to your most recent project. |
render | string | no | Set to "text" to also get a plain-text rendering of the data (only for capabilities that have one). |
Response
{ "data": { /* the capability's data */ } }With "render": "text", the response also carries a text field:
{ "data": { /* ... */ }, "text": "• Slow onboarding — 12 posts\n• ..." }One example call
curl -s https://dist0.com/api/v1/invoke \
-H "Authorization: Bearer $DIST0_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"capability": "pains.list",
"input": { "type": "pain", "sort": "posts", "pageSize": 5 }
}'Projects: which one you're reading
Most capabilities are project-scoped — they read one project. Pick the
project with the project selector; leave it out and the API uses your most
recent project.
If your key was tied to one project when you made it, it always reads that
project — the project selector is ignored. Use a key with no project set to
switch between projects with the selector.
{ "capability": "briefs.list", "project": "acme.com", "input": { "limit": 1 } }A few capabilities are user-scoped and span your whole account
(project.list, runs.get); for those the project field is ignored.
Errors
Errors come back as a typed envelope with a matching HTTP status:
{ "error": { "code": "not_found", "message": "pain theme not found" } }code | Status | When |
|---|---|---|
invalid | 400 | Missing capability, malformed body, or input fails its schema. |
forbidden | 403 | Bad/absent token, or the project isn't yours. |
not_found | 404 | Unknown capability, or the thing you asked for doesn't exist. |
limit | 409 | A usage or spend limit was hit. |
Discover capabilities: GET /api/v1/capabilities
Lists every capability — reads and writes — with its kind and JSON Schema,
enough for an agent or a CLI to build its own tool list without hard-coding
anything.
curl -s https://dist0.com/api/v1/capabilities \
-H "Authorization: Bearer $DIST0_TOKEN"{
"capabilities": [
{
"name": "pains.list",
"kind": "read",
"scope": "project",
"agentInvokable": true,
"description": "The project's buyer pain themes or competitors, chosen by `type` ('pain' or 'competitor'), ranked by recent activity or post count.",
"inputSchema": {
"type": "object",
"required": ["type"],
"properties": {
"type": { "type": "string", "enum": ["pain", "competitor"] },
"page": { "type": "integer", "minimum": 1 },
"pageSize": { "type": "integer", "minimum": 1, "maximum": 100 },
"sort": { "type": "string", "enum": ["recent", "posts"] }
}
}
}
]
}Capability reference
Every capability below is a value for capability on POST /api/v1/invoke. The
"Input" is what goes in the input field.
project.memory
What dist0 knows about the project: product, ideal customer, watched subreddits, competitors, offerings, and filter rules.
- Scope: project · supports
"render": "text" - Input:
{ "field"?: "product" | "icp" | "subreddits" | "competitors" | "offerings" | "filter_rules" }— omitfieldfor everything; pass one to narrow it.
{ "capability": "project.memory", "input": {} }{
"data": {
"memory": {
"id": "prj_1a2b",
"name": "Acme",
"url": "https://acme.com",
"category": "project management",
"description": "Acme is a lightweight task tracker for small teams.",
"idealCustomer": "Founders and ops leads at 2–20 person startups.",
"competitors": [
{ "name": "Linear", "url": "https://linear.app", "description": "Issue tracking for software teams." }
],
"offerings": [
{ "name": "Free plan", "description": "Up to 3 projects." }
],
"subreddits": [
{ "name": "r/startups", "description": "Startup founders and operators." }
],
"filterRules": ["Skip posts about enterprise sales"],
"memoryStatus": "ready",
"memoryRefreshRequestedAt": null
},
"field": null
}
}project.list
Every project on your account.
- Scope: user · supports
"render": "text" - Input:
{}
{ "capability": "project.list", "input": {} }{
"data": {
"projects": [
{ "id": "prj_1a2b", "name": "Acme", "url": "https://acme.com", "memoryStatus": "ready" }
]
}
}briefs.list
The project's most recent market briefs, newest first. Each brief is a set of
sections (content, offers) holding numbered signal items.
- Scope: project
- Input:
{ "limit"?: number }— 1–5, default 3.
{ "capability": "briefs.list", "input": { "limit": 1 } }{
"data": {
"briefs": [
{
"window": "Jul 1–2",
"brief": {
"sections": [
{
"kind": "content",
"title": "Content",
"items": [
{
"origin": "pain",
"surfaced": {
"summary": "Teams keep losing track of tasks across tools.",
"quote": { "text": "I have tasks in three apps and forget half of them.", "who": "u/founder_kate", "sub": "r/startups" }
},
"move": { "title": "Write: one place for every task", "body": "A guide on consolidating task tracking." },
"sources": [
{ "title": "How do you keep track of everything?", "url": "https://www.reddit.com/r/startups/comments/abc123/...", "sub": "r/startups" }
]
}
]
}
]
}
}
]
}
}briefs.get
One signal from the recent briefs in full, by its sequence number.
- Scope: project · supports
"render": "text" - Input:
{ "n": number }— the signal's 1-based number.
{ "capability": "briefs.get", "input": { "n": 3 } }{
"data": {
"n": 3,
"when": "Jul 1–2",
"kind": "content",
"item": {
"origin": "pain",
"surfaced": { "summary": "Teams keep losing track of tasks across tools." },
"move": { "title": "Write: one place for every task", "body": "A guide on consolidating task tracking." },
"sources": [
{ "title": "How do you keep track of everything?", "url": "https://www.reddit.com/r/startups/comments/abc123/...", "sub": "r/startups" }
]
}
}
}pains.list
The project's buyer pain themes or competitors — chosen by type — ranked by
recent activity or by post count.
- Scope: project · supports
"render": "text" - Input:
{ "type": "pain" | "competitor", "page"?: number, "pageSize"?: number, "sort"?: "recent" | "posts" }—typeis required;pageSize1–100 (default 25),sortdefaultrecent.
{ "capability": "pains.list", "input": { "type": "pain", "sort": "posts", "pageSize": 5 } }{
"data": {
"rows": [
{ "id": "sov_77", "title": "Slow onboarding", "summary": "New users churn before finishing setup.", "postCount": 12, "lastUpdatedAt": "2026-07-01T09:14:00.000Z" }
],
"totalCount": 8,
"totalPostCount": 41,
"page": 1,
"pageCount": 2,
"rangeStart": 1,
"rangeEnd": 5,
"pageSize": 5,
"sort": "posts"
}
}pains.get
One pain theme in full: its quotes, authors, and source posts, newest first.
- Scope: project
- Input:
{ "sovId": string }— a pain theme id frompains.list.
{ "capability": "pains.get", "input": { "sovId": "sov_77" } }{
"data": {
"id": "sov_77",
"title": "Slow onboarding",
"summary": "New users churn before finishing setup.",
"postCount": 12,
"firstSeenAt": "2026-06-14T00:00:00.000Z",
"lastSeenAt": "2026-07-01T09:14:00.000Z",
"pains": [
{
"signalId": "sig_501",
"quote": "It took me 40 minutes just to connect my first tool.",
"author": "u/founder_kate",
"postUrl": "https://www.reddit.com/r/startups/comments/abc123/...",
"postTitle": "Onboarding is exhausting",
"subreddit": "r/startups",
"seenAt": "2026-07-01T09:14:00.000Z",
"moves": {
"content": { "title": "Write: cut setup to 5 minutes", "body": "A walkthrough of a faster first-run." }
}
}
]
}
}pains.sources
Source grounding for one or more pain themes: each pain plus its Reddit source
posts and full threads. Omit sovIds to instead get the project's pains ranked
by post count, so you can pick which to ground.
- Scope: project
- Input:
{ "sovIds"?: string[] }
List mode — no sovIds:
{ "capability": "pains.sources", "input": {} }{
"data": {
"mode": "list",
"pains": [
{ "id": "sov_77", "title": "Slow onboarding", "summary": "New users churn before finishing setup.", "painCount": 12 }
]
}
}Sources mode — with sovIds:
{ "capability": "pains.sources", "input": { "sovIds": ["sov_77"] } }{
"data": {
"mode": "sources",
"sovs": [
{
"id": "sov_77",
"title": "Slow onboarding",
"summary": "New users churn before finishing setup.",
"postCount": 12,
"firstSeenAt": "2026-06-14T00:00:00.000Z",
"lastSeenAt": "2026-07-01T09:14:00.000Z",
"pains": [
{ "signalId": "sig_501", "quote": "It took me 40 minutes just to connect my first tool.", "author": "u/founder_kate", "postUrl": "https://www.reddit.com/r/startups/comments/abc123/...", "postTitle": "Onboarding is exhausting", "subreddit": "r/startups", "seenAt": "2026-07-01T09:14:00.000Z", "moves": {} }
]
}
],
"threads": [
{
"id": "post_abc123",
"permalink": "/r/startups/comments/abc123/onboarding_is_exhausting/",
"title": "Onboarding is exhausting",
"selftext": "I have tasks in three apps...",
"author": "u/founder_kate",
"score": 84,
"postCreatedAt": "2026-07-01T08:00:00.000Z",
"content": "# Onboarding is exhausting\n\nI have tasks in three apps...\n\n## Comments\n...",
"subreddit": "r/startups"
}
]
}
}posts.get
A Reddit post and its full thread, plus the signals the pipeline extracted from it. Look it up by permalink (an absolute URL or a bare path) or by raw post id.
- Scope: project · supports
"render": "text" - Input:
{ "permalink"?: string, "id"?: string }— one of the two is required.
{ "capability": "posts.get", "input": { "permalink": "https://www.reddit.com/r/startups/comments/abc123/onboarding_is_exhausting/" } }{
"data": {
"post": {
"id": "post_abc123",
"permalink": "/r/startups/comments/abc123/onboarding_is_exhausting/",
"title": "Onboarding is exhausting",
"selftext": "I have tasks in three apps...",
"author": "u/founder_kate",
"score": 84,
"postCreatedAt": "2026-07-01T08:00:00.000Z",
"content": "# Onboarding is exhausting\n\n...\n\n## Comments\n...",
"subreddit": "r/startups"
},
"signals": [
{ "kind": "pain", "quote": "It took me 40 minutes just to connect my first tool.", "author": "u/founder_kate", "sovId": "sov_77" }
]
}
}runs.get
One digest run's status, window, and signal counts, by run id.
- Scope: user
- Input:
{ "runId": string }
{ "capability": "runs.get", "input": { "runId": "run_555" } }{
"data": {
"id": "run_555",
"projectId": "prj_1a2b",
"projectName": "Acme",
"windowStart": "2026-07-01T00:00:00.000Z",
"windowEnd": "2026-07-02T00:00:00.000Z",
"status": "complete",
"signalCounts": { "pain": 9, "competitor": 2, "content": 4 },
"postsAnalyzed": 137
}
}competitors.list
Competitors the pipeline flagged — one entry per product — each with its name,
link, source posts, and how often it recurred, ranked most-mentioned first. The
entry's name is the product; url carries the competitor's link when the
product is in your project memory, and is null otherwise.
- Scope: project · supports
"render": "text" - Input:
{}
{ "capability": "competitors.list", "input": {} }{
"data": {
"competitors": [
{
"name": "Linear",
"url": "https://linear.app",
"mentions": 3,
"sources": [
{ "title": "What do you use to track issues?", "url": "https://www.reddit.com/r/startups/comments/def456/...", "quote": "We moved to Linear and never looked back." }
]
}
]
}
}Write capabilities
Write capabilities change a project's data. They call the same
POST /api/v1/invoke endpoint and enforce the same ownership check as reads —
you can only write to a project on your own account.
memory.edit
Apply structured edits to the active project's memory: add or remove filter rules, competitors, offerings, or watched subreddits, and set the product or ideal customer. Pass a list of edit operations; they apply in order, and either all of them commit or none do.
- Scope: project · supports
"render": "text" - Input:
{ "edits": Edit[] }— 1–64 operations.
Each edit is one of:
op | Fields | What it does |
|---|---|---|
set_product | text | Replace the product description. |
set_icp | text | Replace the ideal-customer description. |
add_competitor | name, url, description | Add a tracked competitor. |
remove_competitor | name | Remove a tracked competitor by name. |
add_offering | name, description | Add an offering. |
remove_offering | name | Remove an offering by name. |
add_subreddit | name, description | Watch a subreddit (up to 5). |
remove_subreddit | name | Stop watching a subreddit. |
add_filter_rule | rule | Add a rule that steers what counts as a signal. |
remove_filter_rule | rule | Remove a filter rule. |
{
"capability": "memory.edit",
"input": {
"edits": [
{ "op": "add_filter_rule", "rule": "Don't treat note-taking apps as competitors" },
{ "op": "remove_competitor", "name": "Obsidian" }
]
}
}{
"data": {
"applied": [
"Added filter rule: Don't treat note-taking apps as competitors",
"Removed competitor: Obsidian"
]
}
}A bad edit comes back as the typed error envelope — invalid for a malformed
edit or one that changes nothing, limit if it would push you past the
5-subreddit cap.
A common flow: ground on a pain's source posts
To read a pain theme and the full Reddit threads behind it in one call, use
pains.sources with the theme's id:
# 1. Find the pain you care about.
curl -s https://dist0.com/api/v1/invoke \
-H "Authorization: Bearer $DIST0_TOKEN" -H "Content-Type: application/json" \
-d '{ "capability": "pains.list", "input": { "type": "pain", "sort": "posts" } }'
# 2. Pull that pain plus its source posts and full threads.
curl -s https://dist0.com/api/v1/invoke \
-H "Authorization: Bearer $DIST0_TOKEN" -H "Content-Type: application/json" \
-d '{ "capability": "pains.sources", "input": { "sovIds": ["sov_77"] } }'The sovs array carries each pain and its signals; the threads array carries
the full rendered Reddit threads (content) for every source post, deduped by
URL.
