Atlas Roster

Invite people to share with an Atlas organization and automate their roster lifecycle.

6 min read Updated Sep 19, 2026

Roster operations manage people invited through Atlas. They are separate from /v1/users, which manages API-created users and their stored health data. Roster operations require a live API key; sandbox keys return 409 live_environment_required.

Invite a Roster Member

POST /v1/atlas/roster/invitations

Creates and sends a 14-day invitation. The person chooses whether to share their Sonar data with the organization.

json
{
  "email": "jordan@example.com",
  "full_name": "Jordan Kim"
}

A successful request returns 201 Created:

json
{
  "id": "8ab89a…",
  "email": "jordan@example.com",
  "full_name": "Jordan Kim",
  "status": "pending",
  "expires_at": "2026-10-03T10:00:00.000Z",
  "accept_url": "https://app.sonarhealth.co/trainee-invite?token=…"
}

Creating another invitation for the same email revokes the prior pending invitation and sends a new one. This endpoint does not accept an idempotency key; retain the returned invitation ID before retrying.

Invitations return 403 roster_invitations_disabled when the organization’s product configuration does not allow invitations, or 402 billing_add_trainee_blocked when another active roster seat cannot be added.

Read Invitation Status

GET /v1/atlas/roster/invitations/{id}

Returns pending, accepted, revoked, or expired. subject_id is populated after acceptance.

json
{
  "id": "8ab89a…",
  "status": "accepted",
  "subject_id": "7e4e91a5-1e4f-4fc2-903c-251046d2a4d3"
}

Revoke an Invitation

DELETE /v1/atlas/roster/invitations/{id}

Revokes a pending invitation and returns 204 No Content.

Repeating a revoke, or revoking an expired invitation, also returns 204. An accepted invitation returns 409 invitation_already_accepted; manage the accepted roster member instead.

Archive or Reactivate

PATCH /v1/atlas/roster/{id}

Changes whether an invited person is active in the Atlas roster.

json
{ "status": "archived" }

Archiving removes the person from the active roster and active-seat count without deleting their Sonar account or health data. The sharing relationship is retained so the member can be restored. Reactivating may return 402 billing_add_trainee_blocked when no active seat is available.

The response is the current roster member:

json
{
  "id": "7e4e91a5-1e4f-4fc2-903c-251046d2a4d3",
  "display_name": "Jordan Kim",
  "email": "jordan@example.com",
  "source": "b2c",
  "status": "archived",
  "created_at": "2026-09-19T10:00:00.000Z",
  "archived_at": "2026-09-19T12:00:00.000Z"
}

Setting the current status again is a no-op and returns 200 with the same member.

Remove from the Roster

DELETE /v1/atlas/roster/{id}

Permanently removes the invited person from this organization’s roster and returns 204 No Content.

Removal revokes the organization’s sharing grant and removes the active billing seat. It does not delete the person’s Sonar account or underlying health data. Repeating the same deletion returns 204.

API-created users use the Users API

These lifecycle routes reject API-created users with 409 api_user_lifecycle_managed_by_users_api. Use /v1/users/{id} for those users; deleting an API user has broader data-deletion effects.

Example

python
invitation = requests.post(
    f"{BASE}/atlas/roster/invitations",
    headers=HEADERS,
    json={"email": "jordan@example.com", "full_name": "Jordan Kim"},
).json()
typescript
const invitation = await fetch(`${base}/atlas/roster/invitations`, {
  method: "POST",
  headers,
  body: JSON.stringify({ email: "jordan@example.com", full_name: "Jordan Kim" }),
}).then((response) => response.json());