# Post to the ticker

`POST /ticker.post`

## Description

Add a post to the ticker. The post is created as your integration's bot
and appears to people in the Roam immediately. Posts don't expire; a post
stays until someone deletes it.

The new post goes at the end of the ticker. To place it next to an existing
post, pass that post's ID as `beforeId` or `afterId` (not both). The anchor
can be any post on the ticker, whoever created it. People can reorder posts
in the Roam app and there is no endpoint to reorder them, so a post may not
stay where you put it.

The ticker holds at most 50 posts, shared by members and integrations. When
it is full, this call returns `409` `ticker_full`; delete a post with
[`ticker.delete`](/docs/api/ticker-delete) first. There is no edit
endpoint either: to change a post, delete it and post it again.

This call is not idempotent. If a request times out and you can't tell
whether it succeeded, call [`ticker.list`](/docs/api/ticker-list) and check
for the post before retrying, or you may create a duplicate.

**Access:** Organization only (API Key or OAuth with admin consent).
Personal tokens, including Personal Access Tokens, get `403`
`access_mode_not_supported`.

**Required scope:** `ticker:write`

---

**OpenAPI Spec:** [chat-v1.json](https://developer.ro.am/chat-v1.json)

## Authentication

```
Authorization: Bearer YOUR_API_KEY
```

## Request Body

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `category` | string | Yes | Lowercase label shown with the post, 1–16 characters after trimming whitespace. Uppercase `A`–`Z` is rejected, not converted. `announcement`, `goal`, and `news` are suggestions, not a fixed list.  |
| `text` | string | Yes | Post text, as plain text. Leading and trailing whitespace is trimmed, and the result must be 1–300 characters.  |
| `url` | string | No | Link the post opens: an `http` or `https` URL whose host is a domain name, not an IP address or a single-label host such as `localhost`. Omit it for a post with no link; an empty string is rejected.  |
| `beforeId` | string | No | ID of a post on the ticker. The new post is inserted immediately before it. Cannot be combined with `afterId`.  |
| `afterId` | string | No | ID of a post on the ticker. The new post is inserted immediately after it. Cannot be combined with `beforeId`.  |

## Responses

### 200 - Post created

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `ok` | "true" | Yes | Always `true` on success. See [Responses and Errors](/docs/guides/responses-and-errors). |
| `message` | TickerMessage | Yes |  |

**TickerMessage**:

| Property | Type | Required | Description |
|----------|------|----------|-------------|
| `id` | string | Yes | Post ID. |
| `category` | string | Yes | Lowercase label shown with the post, such as `announcement`, `goal`, or `news`. |
| `text` | string | Yes | Post text, as plain text. |
| `url` | string | No | HTTP(S) link the post opens. Omitted when the post has no link. |
| `authorId` | string | Yes | Who created the post: the member's user ID (resolve it with `user.info`) for a post created in the Roam app, or the integration's bot ID for a post created through the API. To find your own posts, com |
| `createdAt` | string | Yes | When the post was created (RFC-3339, UTC). |


#### Example Response

```json
{
  "ok": true,
  "message": {
    "id": "019a0000-0000-7000-8000-0000000000a2",
    "category": "announcement",
    "text": "v4.2 is live for every customer",
    "url": "https://example.com/changelog/4-2",
    "authorId": "b893c426-6d54-4d9a-8e71-6bd53b26124e",
    "createdAt": "2026-01-21T18:30:00.127Z"
  }
}
```

### 400 - Bad request. Common causes:
- Missing, empty, or too-long `category` or `text`, or a `category` with uppercase letters (`invalid_arguments`)
- A `url` that is not an `http` or `https` URL with a domain-name host, or is longer than 2048 characters (`invalid_arguments`)
- An empty `url` (`invalid_parameter`, `param` is `url`)
- A `beforeId` or `afterId` that is not a UUID (`invalid_parameter`, `param` names the field)
- Both `beforeId` and `afterId` (`invalid_arguments`)
- Malformed JSON body or an unknown field (`invalid_json`)


### 401 - Presented invalid authentication credentials.

### 403 - Forbidden. Common causes:
- Personal token (`access_mode_not_supported`)
- Token is missing `ticker:write` (`missing_scope`)
- The ticker is not available in this Roam (`ticker_not_available`)
- An admin has turned the ticker off (`ticker_disabled`)


### 404 - Not found. Common causes:
- `beforeId` or `afterId` is not a post on the ticker in this Roam (`ticker_message_not_found`)


### 405 - An unsupported method was requested.

### 409 - The ticker already has 50 posts (`ticker_full`). Delete a post first.

### 500 - An internal error occurred.

---

*Machine-readable API documentation.*
*Full documentation: https://developer.ro.am/docs/api/ticker-post*
