Core Resources
View as Markdown

Topic

A meeting agenda item — either a decision (needs a call) or an inform (needs to be known). One shape, two kinds, with the state that matters for each.

A topic is a Droplet wearing a Type that carries a topic config block. Two kinds ship:

  • a decision needs a call — it carries a RAPID roster and collects sign-off (drive it through the Sign-off resource);
  • an inform needs to be known — it carries a summary, presenters, and passive read receipts. Nobody is blocked on it and there is no acknowledgement step.

A Droplet wears exactly one Type, so a Droplet is one kind or the other, never both. That is enforced by App.typeId being a single foreign key rather than by any rule here.

Why one resource for two kinds. Both share a lifecycle spine — draft → active → closed (plus blocked, decision-only) — because both Types declare the same status values. They diverge only in the progress they report: a decision reports its sign-off tally, an inform reports how much of its audience has opened it. Exactly one of signoff and reads is non-null on any item.

The two boards are separate on purpose, and so is the question each answers. A decision is durable and gets QUERIED — "has this been decided, and does what I am about to decide conflict with it?" An inform is temporal and gets CAUGHT UP ON — "what happened this week that I should know?" Filter list by kind when you want one or the other; an agent asking about conflicting decisions should pass kind: 'decision', or an inform that merely mentions the same subject comes back looking like a ruling.

What you can do:

  • list — every topic you can see, filtered by kind, phase, your role, or tag. The query behind both boards.
  • mine — what is waiting on YOU: decisions in flight where you hold a role and have not signed, and informs circulating that you have not opened.
  • inform.get / inform.set-phase — read an inform's state including who has opened it, and circulate it or mark it presented. Editor-gated; an inform has no roster to check, so the people who can edit the Droplet are the people who can move it.
  • inform.set-attendance — record that someone has the information without having opened the Droplet: the room an inform was presented to, laptops down. Stored on the Droplet, NOT as a view record, so Droplet view counts and admin metrics stay untouched.

Read receipts are passive throughout — opening the Droplet is what clears the flag. The roster on inform.get is EDITOR-ONLY: a supplemental access grant can make a non-member of an invite-only Space a reader, so naming the audience to every reader would disclose membership they cannot otherwise see. The aggregate count is always present.

The Topic object

Every Topic carries these attributes.

Attributes

slug
string
The Droplet's slug — a topic IS a Droplet, so this addresses it everywhere else in the API too.
name
string
The Droplet's name.
kind
string
decisioninform
Which kind, read from the Type's topic config.
summary
string· nullable
One-line plain-text flattening of the proposition (decision) or the summary (inform). Markdown is stripped so a list row cannot leak ## or **.
phase
string
draftactiveclosedblocked
Shared lifecycle spine. blocked is decision-only. An unrecognised or absent status reads as draft, never as closed.
status
string· nullable
The raw status value the phase was derived from, e.g. in-flight.
leadName
string· nullable
Who is driving: the decider on a decision, the first presenter on an inform.
awaitingYou
bool
Decision: in flight and you hold a role but have not signed. Inform: circulating and you have not opened it. This is what the sidebar badges count.
role
string
approverdeciderpresenternone
The role you hold on this topic, if any.
signoff
json· nullable
Decision progress: { signedCount, totalApprovers, tally }. Null on an inform.
reads
json· nullable
Inform read receipts: { readers, audience }. audience is null when the access mode gives no bounded roster — render a bare count, not a fraction. Null on a decision.
meeting
json· nullable
The recurring meeting this topic is queued for: { name, slug }.
More attributes+ Show 6
space
json· nullable
The Space this topic lives in: { name, slug }.
tags
string[]
The Droplet's tags.
openedAt
string· nullable
When sign-off opened (decision) or it was circulated (inform).
closedAt
string· nullable
When it was decided (decision) or presented (inform).
due
string· nullable
The date it is wanted by, when the Type declares one.
priority
string· nullable
Declared priority, when the Type carries the primitive.
THE TOPIC OBJECT
{
  "slug": "rds-proxy-ssr",
  "name": "Adopt RDS Proxy for the SSR fleet",
  "kind": "decision",
  "tags": [
    "infra",
    "database"
  ],
  "summary": "Put RDS Proxy in front of the SSR Lambdas to multiplex connections and end burst-time connection storms.",
  "phase": "active",
  "status": "in-flight",
  "leadName": "Carol Diaz",
  "awaitingYou": true,
  "role": "approver",
  "openedAt": "2026-08-06T14:00:00.000Z",
  "closedAt": null,
  "due": "2026-09-04",
  "priority": "high",
  "space": {
    "name": "Platform",
    "slug": "platform"
  },
  "meeting": {
    "name": "Ops Core",
    "slug": "ops-core"
  },
  "signoff": {
    "signedCount": 1,
    "totalApprovers": 4,
    "tally": {
      "agree": 1,
      "condition": 0,
      "disagree": 0,
      "pending": 3
    }
  },
  "reads": null
}

Methods

Endpoints to operate on a Topic. The example switches between curl, drop CLI, and MCP via the dropdown — your choice persists across the page.

GET

List topics

/api/topics

List every meeting topic you can see — decisions and informs — each with the state that matters for its kind: sign-off progress for a decision, read receipts for an inform. Filter by kind, phase, your role, or tag. This is the query behind the Decisions board, and the answer to "what is on deck", "what is in flight", or "which informs has nobody read".

GET/api/topics
drop topics list --kind inform --phase active
Response200
{
  "items": []
}
GET

Topics awaiting you

/api/topics/mine

The topics waiting on you specifically: decisions in flight where you hold a role and have not signed, and informs circulating that you have not opened. Lets an agent answer "what is blocked on me?" across both kinds without knowing which is which.

GET/api/topics/mine
drop topics mine
Response200
{
  "items": []
}