Users

A Sonar user maps one person in your system to their device connections and health data.

7 min read Updated Sep 18, 2026

User IDs are UUIDs generated by Sonar. external_ref is your stable identifier and is unique within the API key’s environment. display_name is the editable label shown in Atlas; it defaults to external_ref when omitted.

User Object

json
{
  "id": "7e4e91a5-1e4f-4fc2-903c-251046d2a4d3",
  "external_ref": "member_123",
  "display_name": "Jordan Kim",
  "created_at": "2026-09-09T09:30:00.000Z"
}

Profile values are accepted when creating or updating a user, but they are not returned by the public API.

Create a User

POST /v1/users

Creates a user and returns it with 201 Created.

json
{
  "external_ref": "member_123",
  "display_name": "Jordan Kim",
  "profile": {
    "date_of_birth": "1990-01-02",
    "birth_sex": "Female",
    "timezone": "Europe/Madrid"
  }
}
FieldRequiredRules
external_refYesNon-empty string; unique in the selected environment
display_nameNoNon-empty string up to 120 characters; defaults to external_ref
profile.date_of_birthNoReal calendar date in YYYY-MM-DD, or null
profile.birth_sexNoMale, Female, or null
profile.timezoneNoIANA timezone name up to 64 characters, or null

If external_ref is already in use, the API returns 409 user_conflict.

The timezone defines the calendar and clock used by health-data endpoints: daily dates, default date windows, workout and sleep times, and time-series buckets. Supply the person’s current IANA timezone when known and retain it in your system because profile values are not returned by the public API. A missing or null timezone uses UTC.

List Users

GET /v1/users

Lists active users in the key’s environment, oldest first.

Query parameterDefaultDescription
external_refExact lookup by your reference
limit50Page size from 1 to 100
cursorOpaque next_cursor from the prior response

Copy a non-null next_cursor unchanged into the next request’s cursor. Keep external_ref and limit unchanged between pages.

json
{
  "users": [
    {
      "id": "7e4e91a5-1e4f-4fc2-903c-251046d2a4d3",
      "external_ref": "member_123",
      "display_name": "Jordan Kim",
      "created_at": "2026-09-09T09:30:00.000Z"
    }
  ],
  "next_cursor": "WyIyMDI2LTA5LTA5VDA5OjMwOjAwLjAwMFoiLCI3ZTRlOTFhNS0xZTRmLTRmYzItOTAzYy0yNTEwNDZkMmE0ZDMiXQ"
}

next_cursor is null on the final page.

Retrieve a User

GET /v1/users/{id}

Returns one user or 404 not_found.

json
{
  "id": "7e4e91a5-1e4f-4fc2-903c-251046d2a4d3",
  "external_ref": "member_123",
  "display_name": "Jordan Kim",
  "created_at": "2026-09-09T09:30:00.000Z",
  "connections": [
    {
      "provider": "garmin",
      "status": "connected",
      "connected_at": "2026-09-09T09:35:00.000Z",
      "last_sync_at": "2026-09-15T08:42:10.000Z"
    }
  ]
}

The retrieve response includes one entry per connected provider. last_sync_at is null until Sonar processes the provider’s first data and advances whenever newer data is processed. A connection through the Sonar SDK (Apple Health, Health Connect) reports stale once it has sent nothing for 14 days: the app was likely removed from the phone or its health permission withdrawn. Sonar keeps the connection and its data; disconnect it to remove both. Create, list, and update responses remain compact and omit connections.

Update a User

PATCH /v1/users/{id}

Updates the display name, supplied profile fields, or both, and returns the public user object. external_ref remains immutable.

python
requests.patch(
    f"{BASE}/users/{user_id}",
    headers=HEADERS,
    json={
        "display_name": "Jordan Lee",
        "profile": {"timezone": "America/New_York", "birth_sex": None},
    },
).raise_for_status()
typescript
await fetch(`${base}/users/${userId}`, {
  method: "PATCH",
  headers,
  body: JSON.stringify({
    display_name: "Jordan Lee",
    profile: { timezone: "America/New_York", birth_sex: null },
  }),
});

Delete a User

DELETE /v1/users/{id}

Queues deletion and returns 202 Accepted.

json
{
  "id": "7e4e91a5-1e4f-4fc2-903c-251046d2a4d3",
  "status": "deleting"
}

Deletion disconnects providers and removes the subject’s stored data asynchronously. Repeating the request while deletion is in progress returns the same accepted shape.

Deletion is permanent

When deletion completes, the user disappears from list and retrieve operations and Sonar emits subject.deleted to subscribed webhooks.