Skip to content

Announcements

An announcement tells people something has changed, and makes the knowledge base agree with what was said. It has words (the author’s own, and anything drawn from the library), a set of people who are told, and a plan: the changes to topics and items that sending it would make.

Reading announcements needs the read scope. Writing them needs a token granted the announcement:write scope, which is an OAuth path today, and the person behind the token must hold the matching permission. Admins, owners and billing admins hold them.

These are two separate settings, and both have to be chosen before a send.

Setting Field What it decides
Visibility everyone Whether everyone in the organization may read the announcement, or admins only.
Audience audience Who is told. A person, a team, a company role, a kind of role, or the whole organization.

An audience wider than who may read the announcement is refused, because it would send people a message they cannot open.

status Meaning
DRAFT Being written. The only state in which the words, the title and everyone can change.
PLANNING The plan is being worked out in the background.
PLANNED The plan is ready to read.
IN_REVIEW, READY Reserved for a reviewer’s confirmation. READY is reached by confirming the plan. Sending does not require it.
SENDING Delivery is under way.
SENT Delivered. Can be withdrawn for 48 hours.
FAILED Some people were not reached. Sending again finishes the job.
RETRACTED Withdrawn.

GET /v1/announcements

The organization’s announcements, most recent first, in every state. Filter with ?status=. Requires announcement.read. The list is not paginated.

GET /v1/announcements/{id}

One announcement as its author sees it, with the same shape the write routes return.

{
"id": "…",
"title": "Hard hats on every site",
"everyone": true,
"status": "DRAFT",
"authorName": "Ada Admin",
"selections": [
{ "id": "…", "kind": "TOPIC", "topicId": "…", "title": "Head protection", "isAuthored": false, "excludedItemIds": [], "sequence": 1 }
],
"audience": [
{ "id": "…", "actorType": "TEAM", "actorId": "…", "displayName": "Site crews" }
],
"composition": [
{ "kind": "TEXT", "text": "From Monday, hard hats are required on every site." }
],
"teaser": null,
"requiresPopQuiz": false,
"popQuizDueDate": null,
"sentAt": null,
"retractedAt": null,
"createdAt": "2026-09-01T09:00:00.000Z",
"updatedAt": "2026-09-01T09:05:00.000Z",
"url": "https://…"
}

composition is what recipients will read, resolved from the selections as they stand now. It carries no thumbnails or internal ids.

GET /v1/announcements/{id}/plan

The changes sending would make, and which the author has excluded. plan is null until planning has started.

GET /v1/announcements/{id}/delivery

How many people were addressed, reached, could not be told, are outstanding, or failed. delivery is null until sent.

GET /v1/announcements/{id}/pop-quiz

Who has taken the pop quiz, and how they did. popQuiz is null when the announcement did not ask for one.

GET /v1/announcements/{id}/retraction-preview

What withdrawing it would undo, what cannot be undone, and how many people would be told. canRetract is false once the window has closed.

GET /v1/announcements/{id}/read

The announcement as a recipient reads it: the title, who wrote it, when it was sent and what it says. Needs no permission beyond being someone it was visible to. A draft, or one you cannot see, is a 404.

Every write route needs announcement:write on the token, and asks for the person’s permission live on each request. Write routes return the announcement in the shape above, except where noted.

POST /v1/announcements

Start a draft. Nothing is required. Needs announcement.create.

PATCH /v1/announcements/{id}

Change title, everyone, requiresPopQuiz or popQuizDueDate. The title and everyone only change while the announcement is a draft. The due date must be in the future and may carry any UTC offset, and null returns to the default window. A field the route does not know is refused with a 400, not ignored.

DELETE /v1/announcements/{id}

Delete a draft. Returns 204. Needs announcement.delete.

POST /v1/announcements/{id}/text

Set the author’s own words. Repeating it replaces them.

POST /v1/announcements/{id}/selections

Draw on a topic, item or resource.

DELETE /v1/announcements/{id}/selections/{selectionId}

Remove a selection, or the author’s own words.

DELETE /v1/announcements/{id}/selections/{selectionId}/items/{itemId}

Keep the selection and leave one item out of it.

POST /v1/announcements/{id}/audience

Add someone to who is told.

DELETE /v1/announcements/{id}/audience/{audienceId}

Remove an entry from who is told.

POST /v1/announcements/{id}/plan

Start working out what sending would change. Returns 202 at once with the announcement in PLANNING.

POST /v1/announcements/{id}/plan/exclusion

Leave a planned change out, or put it back. Returns 204. The body names the change by its sequence and states the outcome, so repeating it is safe:

{ "sequence": 2, "excluded": true }

POST /v1/announcements/{id}/plan/confirm

Accept the plan and move the announcement to READY. Optional.

POST /v1/announcements/{id}/notification-preview

The email and text as they would arrive, and how many people they would go to, without sending anything. resolvedRecipients is everyone the audience resolves to, exclusions says who would be left out and why, and willBeTold is the number a send would actually tell. It is a POST because it writes the opening line if there is not one yet, so the sentence you preview is the sentence delivered. Returns the notification rather than the announcement.

POST /v1/announcements/{id}/send

Send. Returns 202 at once with the announcement in SENDING. See Sending.

POST /v1/announcements/{id}/retract

Withdraw within 48 hours of sending. See Withdrawing.

POST /v1/announcements/{id}/reopen

Return to draft, from PLANNING, PLANNED, IN_REVIEW or READY. The plan is kept until you change what the announcement says or draws on, and a plan that survived can be sent as it is. After such a change, plan again.

{ "kind": "TOPIC", "topicId": "…" }

kind is RESOURCE, TOPIC or ITEM, and the body carries exactly the one id that matches it: contentId, topicId or itemId.

{ "actorType": "TEAM", "actorId": "…" }

actorType is INDIVIDUAL, TEAM, ROLE, ORGANIZATION or ROLE_KIND. For ORGANIZATION, actorId is the organization id, and for INDIVIDUAL it is a user id of an active member, which GET /v1/people finds from a name or an email. A response reports a person’s actorId as the organization’s own record of them, which is a different id, so identify people by displayName; it is accepted back when adding. Third parties cannot be added, because they may have no address and no way to take a pop quiz.

{ "body": "From Monday, hard hats are required on every site.", "title": "Hard hats" }

title is optional and defaults to the announcement’s. An empty body clears the words.

Sending is not a single call. The order is:

  1. POST /v1/announcements to start a draft.
  2. POST …/text and POST …/selections to say what it says.
  3. PATCH … to choose everyone and requiresPopQuiz. Each has to be chosen, true or false, before a send.
  4. POST …/audience for everyone who should be told.
  5. POST …/plan, which returns at once with the announcement in PLANNING. Poll GET …/plan until it appears. The announcement needs its own words first, because every change the plan proposes cites them.
  6. Read the plan, and optionally POST …/plan/exclusion to leave changes out. Then POST …/notification-preview to see the email and text and how many people it would tell (willBeTold).
  7. POST …/send with that number as expectedRecipientCount. It returns at once with the announcement in SENDING. Follow it with GET …/delivery.

Having a plan does not mean anyone has read it, and sending is itself agreement to the plan as it stands. POST …/plan/confirm is available, and moves the announcement to READY, but is not required.

Audiences change. Someone joins a team, an address is removed, a role gains a member. expectedRecipientCount is how a send says which audience it was meant for:

{ "expectedRecipientCount": 39 }

If the send would now tell a different number of people, nothing goes out and the response is a 409 that says what the number is now. Read the preview again and send when it is the number you mean. Leaving the body out sends without the check.

The count is checked when the request is accepted. The audience is resolved once more a moment later, when delivery starts, so this guards against a stale preview and not against someone joining a team in those few seconds. The number is willBeTold from the preview, which is the audience less the people who cannot be told, and for a send that is finishing after a failure it is the people still outstanding.

Calling send again on a FAILED announcement finishes it: it reaches only the people still outstanding.

POST /v1/announcements/{id}/retract works for 48 hours after a send. It undoes what the announcement changed in the knowledge base and tells everyone the send attempted that it was withdrawn. A change that cannot be undone is left in place, and an issue is filed for each. Call GET …/retraction-preview first to see what it would do.

Status Meaning
400 The body is invalid, or the announcement is in a state that does not allow this (for example editing one that has been planned, or sending one with no audience). A field the route does not take is refused rather than ignored, so a misspelt one is caught. The message says which.
403 The token lacks the scope, or the person lacks the permission.
404 The announcement does not exist, or is in another organization.
409 A send with expectedRecipientCount would now tell a different number of people. Nothing was sent, and the message says the number.
429 Rate limited. Send and retract allow only a few calls a minute, and plan and preview a few more. See Rate limits.