Skip to main content
Use Roam HQ through Corsair: one client, typed API calls. Create an API client in Roam Administration → Developer to get your credentials. Send the key as Authorization: Bearer <key>. Things to know before you call:
  • The version header matters. Every response is rendered against a dated API version, and the plugin pins Roam-Version: 2026-08-25 on every request so a body’s shape cannot change under it. Omitting the header would use the integration’s default, which Roam moves between releases.
  • Errors are labelled text/plain. Roam’s error envelope is JSON but the response’s content type makes generic clients hand it over as a raw string. The plugin parses it, so RoamAPIError.code is the catalog code such as invalid_token — not a string containing the whole body.
  • Branch on the code, not the status. A 401 can be not_authed, invalid_token or token_revoked, and a 403 can be missing_scope or approval_pending. A revoked token is permanent: discard it rather than retrying. A missing_scope body names the scopes that would work in needed, any one of which is enough.
  • chat.post content rules. text, items and asset_ids combine; blocks and poll each stand alone. A body with no content, or blocks/poll mixed with anything else, is refused before the request.
  • Pass sync: true to get the message timestamp. Without it Roam posts in the background and the answer has only chatId, so there is nothing to hand to chat.update, reactions.add or a thread reply.
  • A post needs one destination. Exactly one of chat_id, group_id or user_ids: the last one opens a direct conversation.
  • Scheduling a post returns a different shape. Passing send_at returns scheduledMessageId instead of a timestamp. The time must be in the future, within 30 days and on a 15-minute UTC boundary, and cannot be combined with sync, poll, thread_key or reply_timestamp.
  • chat.history needs one target. Exactly one of chat_id, group_id or user_ids (the direct chat with those people); Roam refuses none or more than one.
  • Messages are edited, not replaced. chat.update takes the chat and the message timestamp, and needs text (with optional items or asset_ids) or blocks. A bot can edit only its own messages.
  • Paging is a cursor. users.list, chat.list, chat.history, groups.list and meetings.list answer Roam’s nextCursor, which the plugin returns as next_cursor with has_more. Pass it back as cursor for the next page; next_cursor: null is the last page, and a call without a cursor starts at the first. reactions.list is not paged, and users.list with ids returns every match in one answer.
What you get:
  • 12 typed API operations

Setup

1

Install

2

Add the plugin

corsair.ts
With multiTenancy: true, scope calls with corsair.withTenant(id). See Quick start for KEK + Hub keys, and Multi-tenancy for account isolation.
3

Choose authentication

4

Connect a tenant

Mint a connect link and send the tenant to it. Hub hosts the page and delivers the result to your app — see Connect / OAuth.

Example API calls

Read the last messages in a chat
Post a message to a chat
See the full list on the API page.

What’s next

API reference

Every roam.api.* operation with input and output types.

Connect / OAuth

createLink, Hub delivery, and tenant connect flows.

Use with agents

Expose this plugin’s operations as MCP tools.