API
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, source posts, content briefs, and next moves 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
- contentBriefs.list
- contentBriefs.get
- nextMoves.list
- nextMoves.get
- posts.get
- runs.get
- 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, source posts, content briefs, and next moves 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": { "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 read capability with its 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 pain themes (SOVs) ranked by recent activity or post count.",
"inputSchema": {
"type": "object",
"properties": {
"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 pain themes, ranked by recent activity or by post count.
- Scope: project · supports
"render": "text" - Input:
{ "page"?: number, "pageSize"?: number, "sort"?: "recent" | "posts" }—pageSize1–100 (default 25),sortdefaultrecent.
{ "capability": "pains.list", "input": { "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"
}
]
}
}contentBriefs.list
The project's saved content briefs, newest-updated first.
- Scope: project · supports
"render": "text" - Input:
{}
{ "capability": "contentBriefs.list", "input": {} }{
"data": {
"briefs": [
{ "id": "cb_9", "subject": "Cut setup to 5 minutes", "title": "How to onboard your team in 5 minutes", "keyword": "fast team onboarding", "painTitle": "Slow onboarding", "sovId": "sov_77", "updatedAt": "2026-07-01T12:00:00.000Z" }
]
}
}contentBriefs.get
The saved content brief for one pain theme, with its full document.
- Scope: project · supports
"render": "text" - Input:
{ "sovId": string }
{ "capability": "contentBriefs.get", "input": { "sovId": "sov_77" } }{
"data": {
"id": "cb_9",
"projectId": "prj_1a2b",
"sovId": "sov_77",
"subject": "Cut setup to 5 minutes",
"keyword": "fast team onboarding",
"title": "How to onboard your team in 5 minutes",
"doc": { /* structured brief */ },
"markdown": "# How to onboard your team in 5 minutes\n\n..."
}
}nextMoves.list
The project's active content and offer opportunities, in a stable numbered order.
- Scope: project · supports
"render": "text" - Input:
{ "kind"?: "content" | "offers" }— omit for both.
{ "capability": "nextMoves.list", "input": { "kind": "content" } }{
"data": {
"moves": [
{
"id": "nm_12",
"kind": "content",
"title": "Write: cut setup to 5 minutes",
"body": "A walkthrough of a faster first-run.",
"sov_id": "sov_77",
"source_label": "Slow onboarding",
"post_permalink": null,
"post_title": null
}
]
}
}nextMoves.get
One opportunity in full by its list number, with the pain or competitor post it came from.
- Scope: project
- Input:
{ "n": number, "kind"?: "content" | "offers" }— the number is the position in the same-kindlist.
{ "capability": "nextMoves.get", "input": { "n": 1, "kind": "content" } }{
"data": {
"id": "nm_12",
"kind": "content",
"title": "Write: cut setup to 5 minutes",
"body": "A walkthrough of a faster first-run.",
"sov_id": "sov_77",
"source_label": "Slow onboarding",
"post_permalink": null,
"post_title": null
}
}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
}
}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": { "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.
