Helium API

Create rooms and add or remove the people in them from your own code.

Base URLhttps://heliumrooms.com/api/v1

Introduction

The API is part of every paid plan. Every request and response body is JSON. Rooms are identified by their id and people by their email address.

Rooms are created by copying a room you set up in Helium. Build a template room once in the editor, then copy it for each client. A typical onboarding flow does that with Create a room, then adds the client’s contacts with Add a person. Each of them gets the same invite email as when you add them in Helium.

Authentication

Create a key in Settings → API. The full key is shown once, so store it somewhere safe. Revoke a key in the same place and anything using it stops working right away.

Send the key as a bearer token on every request.

Request
curl https://heliumrooms.com/api/v1/rooms \
  -H "Authorization: Bearer hr_live_..."

Responses

A successful response wraps its result in data. A failed one returns an error with a stable code to branch on and a message for people. Every code is listed under Errors.

Lists return their items in data, in a fixed order, with hasMore. Today every list fits in one response, so hasMore is always false.

Success
{ "data": { "id": "6f1c...", "name": "Acme" } }
List
{ "data": [{ "id": "6f1c..." }], "hasMore": false }
Error
{
  "error": {
    "code": "room_not_found",
    "message": "No room with this id in your workspace."
  }
}

Changes to the API

We add to this API without breaking it. New fields, new error codes and new values for existing fields can appear at any time, so ignore what you don't recognise. Removing or renaming anything would only happen in a new version, announced in advance.

Idempotency

Send an Idempotency-Key header with any write, for example the id of the customer in your own system. If the request is retried with the same key, Helium returns the first response instead of creating a second room or sending a second invite.

Successful responses are kept for 24 hours. Errors are not kept, so once you fix the cause, a retry with the same key runs again. Reusing a key for a different request returns 422 idempotency_key_reused. A retry while the first request is still running returns 409 request_in_progress.

Request
curl -X POST https://heliumrooms.com/api/v1/rooms \
  -H "Authorization: Bearer hr_live_..." \
  -H "Idempotency-Key: customer-4821" \
  -H "Content-Type: application/json" \
  -d '{"templateRoomId": "...", "name": "Acme", "linearCustomerId": "..."}'

Rate limits

60 requests per minute per workspace. Past that, requests return 429 rate_limited with a Retry-After header.

The room object

Room
{
  "id": "6f1c2a9e-...",
  "name": "Acme",
  "access": "invite_only",
  "url": "https://heliumrooms.com/room/xY12ab",
  "createdAt": "2026-10-31T12:00:00.000Z"
}

Fields

idRequired
The room’s id.
accessRequired
public (anyone with the link), invite_only (people you add sign in) or closed.
urlRequired
The link to send. Uses your custom domain once it is verified.

List rooms

GET/rooms

Every room in your workspace, newest first.

Request
curl https://heliumrooms.com/api/v1/rooms \
  -H "Authorization: Bearer hr_live_..."
Response 200
{
  "data": [
    {
      "id": "6f1c2a9e-...",
      "name": "Acme",
      "access": "invite_only",
      "url": "https://heliumrooms.com/room/xY12ab",
      "createdAt": "2026-10-31T12:00:00.000Z"
    }
  ],
  "hasMore": false
}

Get a room

GET/rooms/{roomId}

One room by its id.

Request
curl https://heliumrooms.com/api/v1/rooms/6f1c2a9e-... \
  -H "Authorization: Bearer hr_live_..."
Response 200
{
  "data": {
    "id": "6f1c2a9e-...",
    "name": "Acme",
    "access": "invite_only",
    "url": "https://heliumrooms.com/room/xY12ab",
    "createdAt": "2026-10-31T12:00:00.000Z"
  }
}

Create a room from a template

POST/rooms

Rooms are always created by copying an existing room, the template. Set it up once in Helium the way every new room should look, then call this for each new room. Issue Boards fill from Linear while the request runs; a very large board finishes within a few minutes.

Body

templateRoomIdRequired
The id of the room to copy: any room in your workspace that you set up the way new rooms should look. Find it with List rooms or at the end of the room editor's URL (/app/room/{id}). The short code in a room's link is not its id.
nameRequired
The new room’s name, up to 100 characters.
linearCustomerIdRequired if the template room has a customer
The Linear customer this room is for: its id, or the last part of the customer's URL in Linear (for example acme-8dde29bc80d6). To copy the id in Linear, open the customer, press Cmd+K (Ctrl+K on Windows) and choose Copy model UUID. The new room's Issue Boards then show only this customer's issues, on top of their other filters; see the notes below for which boards. Required when a module in the template room has a customer.
accessOptional
public, invite_only or closed. Defaults to the template’s access, and to invite_only when the template is closed.

What gets copied

Copied: pages, their order and icons, and the Issue Board, Request Issue, Changelog and Embed Website modules with their settings. Not copied: people, issues (they load from Linear for the new room) and changelog releases.

Setting up the template room

Build the template room once in Helium, the way every client room should look. Set its access to closed: clients can't open it, and it doesn't count toward your room limit.

Teams, projects, labels and embedded URLs are copied exactly as they are; only the customer changes per room. So filter the template room by what every client may see, never by one client's project or label, or each new room will show that client's issues.

Setting up the customer

What a copy does with linearCustomerId depends on whether the module has a customer in the template room:

ModuleHas a customerHas no customer
Issue BoardReplaced by linearCustomerIdGets linearCustomerId, unless another module in the template room has a customer. Then it stays without one.
Request IssueReplaced by linearCustomerIdStays without one ("Each company its own list")

So a customer in the template room is a placeholder: set one on every module that should follow the client, and each copy fills in the right one.

Checks and limits

Two checks keep a room from showing the wrong customer. A template room with a customer anywhere needs linearCustomerId (422 linear_customer_required). A template room with no Issue Board and no Request Issue with a customer refuses one (422 template_has_no_customer_modules).

Rooms created through the API count toward your plan’s room limit like any other.

Request
curl -X POST https://heliumrooms.com/api/v1/rooms \
  -H "Authorization: Bearer hr_live_..." \
  -H "Idempotency-Key: customer-4821" \
  -H "Content-Type: application/json" \
  -d '{
    "templateRoomId": "1d0b7c3e-...",
    "name": "Acme",
    "linearCustomerId": "b7e2c1d4-..."
  }'
Response 201
{
  "data": {
    "id": "6f1c2a9e-...",
    "name": "Acme",
    "access": "invite_only",
    "url": "https://heliumrooms.com/room/xY12ab",
    "createdAt": "2026-10-31T12:00:00.000Z"
  }
}

The person object

Person
{
  "email": "jane@acme.com",
  "name": "Jane Doe",
  "hasAccess": true,
  "invitedAt": "2026-10-31T12:00:00.000Z",
  "firstViewedAt": null
}

Fields

hasAccessRequired
Whether they can open the room today. Everyone on a public room; on an invite-only room, the people you added.
invitedAtRequired
When the invite email went out, or null if it never did.
firstViewedAtRequired
When they first opened the room, or null if they haven’t yet.

Your own team is never listed; they see every room already.

List people

GET/rooms/{roomId}/people

Everyone in a room, most recently added first.

Request
curl https://heliumrooms.com/api/v1/rooms/6f1c2a9e-.../people \
  -H "Authorization: Bearer hr_live_..."
Response 200
{
  "data": [
    {
      "email": "jane@acme.com",
      "name": "Jane Doe",
      "hasAccess": true,
      "invitedAt": "2026-10-31T12:00:00.000Z",
      "firstViewedAt": null
    }
  ],
  "hasMore": false
}

Add a person

POST/rooms/{roomId}/people

Adds a person and emails them the room invite, the same invite the People list in Helium sends. On an invite_only room this creates their account.

Adding someone who was already invited changes nothing, sends nothing, and returns 200 with "invited": false.

If the invite email could not be sent, the person is still added and you get 201 with "invited": false. Adding them again retries the email. If you send an Idempotency-Key, use a new one for the retry.

Body

emailRequired
Their address.
firstNameOptional
Used for new accounts on invite-only rooms.
lastNameOptional
Used for new accounts on invite-only rooms.
Request
curl -X POST https://heliumrooms.com/api/v1/rooms/6f1c2a9e-.../people \
  -H "Authorization: Bearer hr_live_..." \
  -H "Content-Type: application/json" \
  -d '{"email": "jane@acme.com", "firstName": "Jane", "lastName": "Doe"}'
Response 201
{
  "data": {
    "person": {
      "email": "jane@acme.com",
      "name": "Jane Doe",
      "hasAccess": true,
      "invitedAt": "2026-10-31T12:00:00.000Z",
      "firstViewedAt": null
    },
    "invited": true
  }
}

Remove a person

DELETE/rooms/{roomId}/people/{email}

Removes their access to the room and stops its emails to them. URL-encode the address. Their account stays, so access to other rooms is untouched.

Removing someone who is not in the room returns 404 person_not_found. After a retry, that means they are already removed.

Request
curl -X DELETE https://heliumrooms.com/api/v1/rooms/6f1c2a9e-.../people/jane%40acme.com \
  -H "Authorization: Bearer hr_live_..."
Response 200
{ "data": { "removed": true } }

Errors

Branch on code; the message may change.

StatusCodeMeaning
400invalid_requestA field is missing, invalid or unknown.
400invalid_jsonThe body is not valid JSON.
401missing_api_keyNo bearer token was sent.
401invalid_api_keyThe key is unknown or revoked.
403plan_requiredThe workspace is not on a paid plan.
403room_limit_reachedThe plan has no room left. Close a room or contact us.
404room_not_foundNo room with this id in your workspace.
404template_not_foundNo room with this templateRoomId in your workspace.
404person_not_foundThe address is not in the room.
409room_closedPeople cannot be added to a closed room.
409email_unsubscribedThe address unsubscribed from this room.
409email_has_other_accountThe address has an account in another Helium workspace.
409email_is_team_memberThe address belongs to your own team.
409linear_not_connectedLinear is not connected to the workspace.
409linear_customers_disabledCustomer requests are off in your Linear workspace.
409request_in_progressA request with this Idempotency-Key is still running.
422invalid_linear_customerNo Linear customer with this id.
422linear_customer_requiredThe template room has customer modules but no linearCustomerId was sent.
422template_has_no_customer_moduleslinearCustomerId was sent but the template room has nothing to apply it to.
422idempotency_key_reusedThe Idempotency-Key was used for a different request.
429rate_limitedToo many requests.
429invite_limit_reachedYour workspace sent 100 invites to public rooms in the last 24 hours.
500internal_errorSomething went wrong on our side.

Questions or missing something? Email support@heliumrooms.com.