> ## Documentation Index
> Fetch the complete documentation index at: https://docs.answeringagent.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Users

> Manage users within customer organizations through the Partner API.

## Overview

Users are identities owned by your partner account. One user can belong to your provisioned organizations and can also be attached to independently owned, linked organizations as a read-only dashboard viewer.

<Tip>
  Users are identified by your system's `external_id` - this makes it easy to map Answering Agent users to your own database without exposing internal IDs.
</Tip>

***

## Base URL

**Playground**: `https://playground.answeringagent.com/api/v1`
**Production**: `https://answeringagent.com/api/v1`

All endpoints require the `X-API-KEY` header containing a valid partner API key.

***

## Understanding External IDs

The `external_id` is **your** unique identifier for a user from your system:

* Use your existing user IDs (e.g., `"user_12345"`, `"customer_abc"`)
* Makes API calls simple: `GET /users/user_12345/embed-token`
* Maintains clean mapping between your database and Answering Agent
* Must be unique across all users for your partner account

**Example Flow:**

1. User signs up in your system → assigned ID `"cust_789"`
2. Create organization in Answering Agent → use `external_id: "cust_789"`
3. Later: Get embed token → `GET /users/cust_789/embed-token`

***

## Key Concepts

<AccordionGroup>
  <Accordion title="Organization Owners vs Additional Users">
    * **Owner**: Created automatically when you create an organization. Has full permissions.
    * **Additional Users**: Team members you add to the organization after creation.

    Both types are managed through the same `/users` endpoints.
  </Accordion>

  <Accordion title="When to Use External ID">
    Always! The `external_id` is how you reference users in the API:

    * `GET /users/{external_id}` - Look up user details
    * `GET /users/{external_id}/embed-token` - Get embed token
    * All other user operations

    You rarely need to use Answering Agent's internal user IDs.
  </Accordion>

  <Accordion title="Can users belong to multiple organizations?">
    Yes. Create the identity once, then attach it to each approved organization. Provisioned organizations can assign normal roles. Owner-linked organizations always attach the identity as a read-only `user` viewer and require a separate owner/admin grant for each organization.
  </Accordion>

  <Accordion title="Provisioned organizations vs owner-linked organizations">
    * **Provisioned**: Created and owned by your partner account. Existing user and organization write endpoints apply.
    * **Owner-linked**: Owned by an existing Answering Agent customer and visible through an accepted partner link. The organization remains read-only except for the narrow dashboard-viewer attach/detach endpoint, and only after its owner or admin enables viewer management.
  </Accordion>
</AccordionGroup>

## 1. List Users

| Property    | Value               |
| ----------- | ------------------- |
| **Method**  | `GET /users`        |
| **Returns** | `200 OK` on success |

List all users for the authenticated reseller, with optional organization filtering.

### Query Parameters

<ParamField query="organization_id" type="integer" optional>
  Filter users by organization ID
</ParamField>

### Example

```bash theme={null}
# List all users
curl -H "X-API-KEY: <your-api-key>" \
     https://answeringagent.com/api/v1/users

# List users in specific organization
curl -H "X-API-KEY: <your-api-key>" \
     https://answeringagent.com/api/v1/users?organization_id=123
```

```json theme={null}
200 OK
[
  {
    "id": 2144,
    "external_id": "user_123",
    "name": "John Doe",
    "email": "customer@example.com",
    "created_at": "2025-05-14T12:00:00Z",
    "updated_at": "2025-05-14T12:00:00Z"
  }
]
```

## 2. Create User

| Property    | Value                    |
| ----------- | ------------------------ |
| **Method**  | `POST /users`            |
| **Returns** | `201 Created` on success |

Create one identity, optionally assigning it to an organization. When `organization_id` selects an owner-linked organization with an active viewer-management grant, this call creates the identity and attaches it as a read-only dashboard viewer. Do not send `team_name`, `locations`, `phone_number_ids`, or an elevated `role` for that flow.

### Query Parameters

<ParamField query="organization_id" type="integer" optional>
  Organization to assign the user to
</ParamField>

### Request Body

```json theme={null}
{
  "external_id": "your-system-id",        // required – This is the user's ID in YOUR (the reseller's) system
  "email":       "customer@example.com",  // required – customer e‑mail
  "name":        "John Doe",              // optional – customer name
  "team_name":   "John's Team",           // optional – team name (creates new team if not assigned to organization)
  "organization_id": 123,                 // optional – organization ID (alternative to query parameter)
  "locations": [                          // DEPRECATED – use /locations endpoint instead
    { "name": "Main St", "area_code": "303" },
    { "name": "Elm Ave", "area_code": "720" }
  ]
}
```

<Note>
  The `locations` array is deprecated. Use the dedicated [Locations API](/v1/locations) for better location management.
</Note>

### Success Response

```json theme={null}
201 Created
{
  "embed_token": "MTIzNDV8ZDgyZDk3Mzg1OTAxNTkzNA",
  "user": {
    "id": 2144,
    "name": "John Doe",
    "email": "customer@example.com",
    "external_id": "your-system-id",
    "parent_user_id": 7,
    "created_at": "2025-05-14T12:00:00Z",
    "updated_at": "2025-05-14T12:00:00Z"
  },
  "team": {
    "id": 3109,
    "name": "Default",
    "created_at": "2025-05-14T12:00:00Z",
    "updated_at": "2025-05-14T12:00:00Z"
  },
  "locations": [
    { "location": "Main St", "phone_id": 889 },
    { "location": "Elm Ave", "phone_id": 890 }
  ]
}
```

<Note>For a provisioned organization, `embed_token` is returned as usual. For an owner-linked organization, it is `null`; request an organization-scoped token after the viewer relationship is attached.</Note>

**About External IDs:**

* The `external_id` is the user's ID in **your** system (not Answering Agent's internal ID)
* Must be unique across all of your users (your partner account)
* Use it for all API operations: looking up users, generating embed tokens, etc.
* Provides clean separation between your system and Answering Agent

### Error Codes

| Code | Meaning               | Typical Cause                           |
| ---- | --------------------- | --------------------------------------- |
| 401  | Unauthorized          | Missing or incorrect `X-API-KEY`.       |
| 422  | Validation Error      | Required field absent or malformed.     |
| 500  | Internal Server Error | Unexpected failure during provisioning. |

## 3. Get User by External ID

| Property    | Value                      |
| ----------- | -------------------------- |
| **Method**  | `GET /users/{external_id}` |
| **Returns** | `200 OK` on success        |

### Example

```bash theme={null}
curl -H "X-API-KEY: <your-api-key>" \
     https://answeringagent.com/api/v1/users/your-system-id
```

```json theme={null}
200 OK
{
  "id": 2144,
  "name": "John Doe",
  "email": "customer@example.com",
  "external_id": "your-system-id",
  "created_at": "2025-05-14T12:00:00Z",
}
```

<Note>The embed token is not returned in user lookup responses for security reasons. Use the dedicated [embed token endpoints](#embed-token-management) to retrieve or manage tokens.</Note>

## 4. Get User by Email

| Property    | Value                                 |
| ----------- | ------------------------------------- |
| **Method**  | `GET /users/by-email?email={address}` |
| **Returns** | `200 OK` on success                   |

### Example

```bash theme={null}
curl -H "X-API-KEY: <your-api-key>" \
     "https://answeringagent.com/api/v1/users/by-email?email=customer@example.com"
```

```json theme={null}
200 OK
{
  "id": 2144,
  "name": "John Doe",
  "email": "customer@example.com",
  "external_id": "your-system-id",
  "created_at": "2025-05-14T12:00:00Z"
}
```

## 5. Update User

| Property    | Value                        |
| ----------- | ---------------------------- |
| **Method**  | `PATCH /users/{external_id}` |
| **Returns** | `200 OK` on success          |

### Request Body

```json theme={null}
{
  "name": "John Smith",              // optional – updated name
  "email": "newemail@example.com",   // optional – updated email
  "team_name": "Updated Team Name"   // optional – updated team name
}
```

### Success Response

```json theme={null}
200 OK
{
  "user": {
    "id": 2144,
    "name": "John Smith",
    "email": "newemail@example.com",
    "external_id": "your-system-id",
    "created_at": "2025-05-14T12:00:00Z",
    "updated_at": "2025-05-15T10:30:00Z"
  }
}
```

## 6. Delete User

| Property    | Value                         |
| ----------- | ----------------------------- |
| **Method**  | `DELETE /users/{external_id}` |
| **Returns** | `200 OK` on success           |

### Example

```bash theme={null}
curl -X DELETE \
     -H "X-API-KEY: <your-api-key>" \
     https://answeringagent.com/api/v1/users/your-system-id
```

```json theme={null}
200 OK
{
  "message": "User deleted successfully"
}
```

## Account Linking

You can link existing Answering Agent accounts to your partner account, giving you embed-only access to their dashboard. The user receives an email invite and must accept before the link is active.

<Info>
  Linked users give you **embed-only access** — you can generate embed tokens, but you cannot modify their account, organization, or locations.
</Info>

This existing account-link flow maps the linked account owner to your `external_id`. It is separate from attaching one of your own provisioned identities as a multi-organization dashboard viewer.

### Send Linking Invite

| Property    | Value                    |
| ----------- | ------------------------ |
| **Method**  | `POST /partner-links`    |
| **Returns** | `201 Created` on success |

```bash theme={null}
curl -X POST \
     -H "X-API-KEY: <your-api-key>" \
     -H "Content-Type: application/json" \
     -d '{"email": "customer@example.com", "external_id": "customer-456"}' \
     https://answeringagent.com/api/v1/partner-links
```

The user will receive an email with an accept link. Once accepted, the user appears in your user list and you can generate embed tokens using the `external_id` you provided.

### Check Invite Status

| Property    | Value                |
| ----------- | -------------------- |
| **Method**  | `GET /partner-links` |
| **Returns** | `200 OK` on success  |

Returns all your pending and active linked accounts:

```json theme={null}
200 OK
{
  "data": [
    {
      "id": 1,
      "external_id": "customer-456",
      "email": "customer@example.com",
      "name": "Jane Smith",
      "status": "active",
      "accepted_at": "2026-04-01T12:00:00Z",
      "created_at": "2026-04-01T10:00:00Z"
    }
  ]
}
```

### Working with Linked Users

Once a link is active, the user appears in `GET /users` with `type: "linked"` and you can use all embed token endpoints with their `external_id`. Write operations (`PATCH`, `DELETE`) return `403` for linked users.

<Warning>
  Users can revoke the link at any time from their account settings. If revoked, all embed token endpoints will return `404` for that user.
</Warning>

***

## Embed Token Management

For a multi-organization viewer, include `organization_id` on every token request. It is required when the user has more than one eligible organization context. A scoped response includes the selected `organization_id`:

```bash theme={null}
curl -H "X-API-KEY: <your-api-key>" \
  "https://answeringagent.com/api/v1/users/your-system-id/embed-token?organization_id=123"
```

```json theme={null}
{
  "embed_token": "SCOPED_COMPACT_TOKEN",
  "user_id": "your-system-id",
  "organization_id": 123
}
```

### Get Current Embed Token

| Property    | Value                                  |
| ----------- | -------------------------------------- |
| **Method**  | `GET /users/{external_id}/embed-token` |
| **Returns** | `200 OK` on success                    |

Retrieve the current compact embed token for a user. If the user is a scoped viewer, select the organization with `organization_id`.

### Example

```bash theme={null}
curl -H "X-API-KEY: <your-api-key>" \
     https://answeringagent.com/api/v1/users/your-system-id/embed-token
```

```json theme={null}
200 OK
{
  "embed_token": "MTIzNDV8ZDgyZDk3Mzg1OTAxNTkzNA",
  "user_id": "your-system-id"
}
```

### Generate New Embed Token

| Property    | Value                                   |
| ----------- | --------------------------------------- |
| **Method**  | `POST /users/{external_id}/embed-token` |
| **Returns** | `200 OK` on success                     |

Generate a new embed token. For a scoped viewer, include `organization_id`; rotation invalidates only that organization relationship and preserves the user's other organization access. Partner-provisioned users retain one user-wide signing authority, including when `organization_id` selects the returned token's team.

<Warning>
  For a scoped viewer, update the iframe for the selected organization. A token rotation does not affect the user's other organization relationships.

  For a partner-provisioned user, a scoped response includes `"rotation_scope": "user"`. Rotation invalidates compact tokens for all of that user's provisioned organizations, so replace every affected iframe token.
</Warning>

### Example

```bash theme={null}
curl -X POST \
     -H "X-API-KEY: <your-api-key>" \
     https://answeringagent.com/api/v1/users/your-system-id/embed-token
```

```json theme={null}
200 OK
{
  "embed_token": "OTg3NjV8YWJjZGVmZ2hpams1Njc4OTA",
  "user_id": "your-system-id",
  "message": "New embed token generated successfully"
}
```

### Security Features

**Compact & Secure**: Embed tokens use URL-safe base64 encoding and HMAC-SHA256 for cryptographic integrity.

**User-Specific**: Each token is tied to a specific user and cannot be transferred or reused for other users.

**Invalidation**: Scoped tokens are bound to one organization relationship and generation. Detach, grant disable/revoke, partner-link revoke, membership removal, or user deletion invalidates that authority. Re-enabling a grant alone does not restore access; attach the viewer again and request a fresh token.

**Token Lifetime**: The compact token has no built-in expiry, but it can be invalidated immediately. The iframe exchanges it for a bearer that expires after 15 minutes and is revalidated against the current viewer authority on each request.

### Using Embed Tokens

Once you have an embed token, use it with the [embed system](/advanced/embed) to integrate the Answering Agent dashboard into your application.

***

## Multi-Organization Dashboard Viewers

This workflow is for a partner account manager who should use one identity across multiple independently owned customer organizations.

### Prerequisites

1. The organization owner accepts the partner link.
2. The owner or an organization admin enables dashboard-viewer management for your partner account.
3. `GET /organizations/{organization_id}` shows `partner_type: "linked"` and `permissions.can_manage_dashboard_viewers: true`.

### Create or reuse the identity

For the first approved linked organization, you can create and attach in one request:

```bash theme={null}
curl -X POST -H "X-API-KEY: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "account-manager-42",
    "email": "manager@example.com",
    "organization_id": 123
  }' \
  https://answeringagent.com/api/v1/users
```

For each additional approved linked organization, reuse the same `external_id`:

```bash theme={null}
curl -X PUT -H "X-API-KEY: <your-api-key>" \
  https://answeringagent.com/api/v1/organizations/456/dashboard-viewers/account-manager-42
```

The response is the user object. Its `organizations` array contains all currently visible memberships. Repeating the `PUT` is safe.

### Remove one organization without deleting the identity

```bash theme={null}
curl -X DELETE -H "X-API-KEY: <your-api-key>" \
  https://answeringagent.com/api/v1/organizations/123/dashboard-viewers/account-manager-42
```

This returns `204 No Content`, revokes stale compact and bearer authority for organization `123`, and preserves organization `456` plus the user identity. Repeating the `DELETE` is safe.

<Warning>
  Scoped viewers are organization-wide but read-only. They can read current and future locations in the approved organization, but cannot change settings, manage members or roles, use write endpoints, subscribe to operator realtime channels, or trigger recurring AI/model work.
</Warning>
