dist0
API

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

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.com

Authenticate

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

FieldTypeRequiredWhat it does
capabilitystringyesThe capability name, e.g. briefs.list.
inputobjectnoThe capability's input. Defaults to {}.
projectstringnoA substring of the project's URL to select it (dist0.com matches https://dist0.com). Defaults to your most recent project.
renderstringnoSet 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" } }
codeStatusWhen
invalid400Missing capability, malformed body, or input fails its schema.
forbidden403Bad/absent token, or the project isn't yours.
not_found404Unknown capability, or the thing you asked for doesn't exist.
limit409A 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" } — omit field for 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" }pageSize 1–100 (default 25), sort default recent.
{ "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 from pains.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-kind list.
{ "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.