---
title: Topic
kind: resource
slug: topic
summary: "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."
---

# 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](/docs/resources/signoff) 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

### 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` (enum: `decision` / `inform`) — 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` (enum: `draft` / `active` / `closed` / `blocked`) — 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` (enum: `approver` / `decider` / `presenter` / `none`) — 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 }`.
- **`space`** `json · nullable · less-common` — The Space this topic lives in: `{ name, slug }`.
- **`tags`** `string[] · less-common` — The Droplet's tags.
- **`openedAt`** `string · nullable · less-common` — When sign-off opened (decision) or it was circulated (inform).
- **`closedAt`** `string · nullable · less-common` — When it was decided (decision) or presented (inform).
- **`due`** `string · nullable · less-common` — The date it is wanted by, when the Type declares one.
- **`priority`** `string · nullable · less-common` — Declared priority, when the Type carries the primitive.

### JSON example

```json
{
  "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

### List topics

`GET /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".

**CLI**

```bash
drop topics list --kind inform --phase active
```

**MCP**

```json
{
  "tool": "drop_topic_list",
  "arguments": {
    "kind": "inform",
    "phase": "active"
  }
}
```

**HTTP**

```bash
curl -X GET 'https://drop.himarley.ai/api/topics' \
  -H "Authorization: Bearer $DROP_TOKEN"
```

**Response** (200)

```json
{
  "items": []
}
```

### Topics awaiting you

`GET /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.

**CLI**

```bash
drop topics mine
```

**MCP**

```json
{
  "tool": "drop_topic_mine",
  "arguments": {}
}
```

**HTTP**

```bash
curl -X GET 'https://drop.himarley.ai/api/topics/mine' \
  -H "Authorization: Bearer $DROP_TOKEN"
```

**Response** (200)

```json
{
  "items": []
}
```
