# Manage members

> Add, change, and remove staff members and team placements across the organizations on your account with an account API key, so an HR or identity system keeps BloomText in step.

Source: https://www.bloomtext.com/developers/api/members-api/

An account API key can keep BloomText's staff in step with your HR or identity system: add a new hire to the right organizations and teams, change their role, and remove them when they leave. Your account is the subscription your organizations share, and the key reaches all of them or the ones picked when it was made.

### Get an account key

BloomText turns accounts on per account, on request; in an account without them, these endpoints return `403` with `code: accounts_not_enabled`. An Owner or IT person creates the key under **Settings → Account → API keys**, with the scopes it needs:

- `members.read` to list organizations, members, teams, and member events.
- `members.write` to add, change, and remove members.
- `teams.write` to put members on teams and take them off.

See [API keys](https://www.bloomtext.com/developers/api/api-keys/) for expiry, rotation, and storage.

### Find the organization

List the organizations the key reaches. Pass one's `id` as `org_id` on the other calls. A key that reaches only one organization can leave `org_id` out; a key that reaches several gets `400 org_id_required` without it.

```bash filename="Request"
curl https://api.bloomtext.com/v1/orgs \
  -H "Authorization: Bearer $BLOOMTEXT_API_KEY"
```

```json filename="Response · 200 OK"
{
  "data": [
    { "id": "6db1e3f5-9b7f-4f2b-8be1-0f1e1d7d7d8c", "name": "Northside Clinic", "is_default": true },
    { "id": "0c5a7e2d-3f41-4b8e-9d26-5a1c8e7f9b30", "name": "Eastside Clinic", "is_default": false }
  ],
  "pagination": { "next_cursor": null, "has_more": false }
}
```

### Add a member

Add a person by email. A new email creates their BloomText user, owned by your account. They get an invite email only with `send_invite_email: true`; leave it `false` when they sign in another way or you'll invite them later.

```bash filename="Request"
curl -X POST "https://api.bloomtext.com/v1/members?org_id=$ORG_ID" \
  -H "Authorization: Bearer $BLOOMTEXT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jordan.lee@clinic.example",
    "first_name": "Jordan",
    "last_name": "Lee",
    "role": "user",
    "title": "Front desk",
    "send_invite_email": false
  }'
```

```json filename="Response · 201 Created"
{
  "id": "9f2c4b1a-7d3e-4a5b-8c6d-1e2f3a4b5c6d",
  "org_id": "6db1e3f5-9b7f-4f2b-8be1-0f1e1d7d7d8c",
  "email": "jordan.lee@clinic.example",
  "first_name": "Jordan",
  "last_name": "Lee",
  "role": "user",
  "account_role": null,
  "managed_by": "api",
  "managed_by_name": "HR sync"
}
```

The member's `id` is their BloomText user ID, the same in every organization. To add them to a second organization, repeat the call with that organization's `org_id`.

### Put them on a team

List the organization's teams with [`GET /teams`](https://www.bloomtext.com/developers/api/reference/list-teams/), then add the member by `member_id`:

```bash filename="Request"
curl -X POST "https://api.bloomtext.com/v1/teams/$TEAM_ID/members" \
  -H "Authorization: Bearer $BLOOMTEXT_API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{ "member_id": "9f2c4b1a-7d3e-4a5b-8c6d-1e2f3a4b5c6d" }'
```

The member must already be in the team's organization. It returns `201` with the team, also when they were already on it. Team calls take no `org_id`: the team names its organization.

## Who manages a membership

Each membership has one manager, in `managed_by`:

| `managed_by` | Who changes it | In BloomText it shows |
| --- | --- | --- |
| `api` | Only API keys. `managed_by_name` is the key that added it. | **🔒 Managed by API: HR sync**, with no Manage or Remove |
| `admin` | People in BloomText. The API answers `409 managed_by_admin`. | A normal member row |
| `account` | An Owner or IT person's membership, in every organization, set on the Account page. The API answers `409 managed_by_account`. | **🔒 Set on the Account page** |
| `okta` | The account's Okta rules, through Directory Sync. The API answers `409 managed_by_identity_provider`. | **🔒 Managed by Okta** |

`account_role` is `owner` or `it` for Owners and IT, and `null` for everyone else. Account roles are set only in BloomText: no API call, and no Okta rule, makes someone an Owner or gives them the IT role.

`admin` in the API is what BloomText's People list calls **Manual**. Everyone who joined before your sync started is `admin`, so a sync's first run should read the members first and leave those rows to people in BloomText; the API manages only the members it adds.

The same rule covers teams: [`DELETE /teams/{teamId}/members/{memberId}`](https://www.bloomtext.com/developers/api/reference/remove-team-member/) takes off only people the API put on the team.

### With Okta Directory Sync

When your account connects Okta, its rules decide which organizations, role and teams each person in Okta gets, and those memberships show `managed_by: "okta"`. See [SSO and Directory Sync](https://www.bloomtext.com/developers/api/identity-and-sso/). For the API that means:

- The API can't change or remove a membership or team placement Okta manages. It answers `409 managed_by_identity_provider`; change the person in Okta or in the account's rules.
- When an Owner or IT person saves a rule, Okta takes over the matching memberships that people in BloomText or an API key made. Their `managed_by` becomes `okta`, and from then on your sync gets `409` for them.
- Deactivating, suspending or deleting someone in Okta removes every membership they have on the account, including the ones an API key made.
- The account's **Allow Admins to invite people outside Okta** switch limits organization Admins in BloomText. It doesn't limit API keys.

Pick one source of truth per person. Use Directory Sync when Okta is where your staff live; use the API for people Okta doesn't cover, such as contractors, or when your HR system is the source.

## Run a sync

A sync that keeps BloomText in step with an HR system usually runs on a schedule, once per organization:

1. Read the organization's members with [`GET /members?org_id=…`](https://www.bloomtext.com/developers/api/reference/list-members/), following `next_cursor` to the end. Match them to your records by email.
2. Leave alone everyone whose `managed_by` isn't `api`: people in BloomText, the Account page or Okta manage them.
3. Add the people who are missing with `POST /members`. Derive the `Idempotency-Key` from something stable, such as the employee ID and the organization, so a retried run never adds anyone twice.
4. Change role and title with `PATCH /members/{memberId}`. For a line-of-business lead who should run their organization, set `role` to `admin` in that organization.
5. Remove leavers with `DELETE /members/{memberId}?confirm_delete_user=true`.
6. Put people on their teams with `POST /teams/{teamId}/members`; list the teams with [`GET /teams`](https://www.bloomtext.com/developers/api/reference/list-teams/).
7. Check the result with [`GET /member-events`](https://www.bloomtext.com/developers/api/reference/list-member-events/).

Every call counts toward the organization's limit of 300 requests a minute; see [Rate limits](https://www.bloomtext.com/developers/api/rate-limits/).

## Change a member

[`PATCH /members/{memberId}`](https://www.bloomtext.com/developers/api/reference/update-member/) changes the `role` (`admin` or `user`) or `title` of a membership the API manages.

```bash filename="Request"
curl -X PATCH "https://api.bloomtext.com/v1/members/$MEMBER_ID?org_id=$ORG_ID" \
  -H "Authorization: Bearer $BLOOMTEXT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "role": "admin" }'
```

## Remove a member

[`DELETE /members/{memberId}`](https://www.bloomtext.com/developers/api/reference/delete-member/) removes a membership the API manages, and answers `204`.

When this is the person's only organization and your account owns their user, removing them deletes their BloomText user, and they can no longer sign in. The API asks you to confirm that. Without `confirm_delete_user=true` the call changes nothing and answers:

```json filename="Response · 409 Conflict"
{
  "type": "https://www.bloomtext.com/developers/api/errors/#confirmation_required",
  "title": "Conflict",
  "status": 409,
  "code": "confirmation_required",
  "reason": "would_delete_user",
  "detail": "This is the person's only organization, so removing them deletes their BloomText user and they can't sign in. Repeat the call with confirm_delete_user=true to do that.",
  "request_id": "0b6c2d1e-5f4a-4c3b-9a8d-7e6f5a4b3c2d",
  "field_errors": []
}
```

Repeat it with the confirmation to remove and delete them:

```bash filename="Request"
curl -X DELETE "https://api.bloomtext.com/v1/members/$MEMBER_ID?org_id=$ORG_ID&confirm_delete_user=true" \
  -H "Authorization: Bearer $BLOOMTEXT_API_KEY"
```

Someone who is also in another organization is only removed from this one, with or without the flag. A sync that offboards people can always send `confirm_delete_user=true` when it means "this person has left".

## See what changed

[`GET /member-events`](https://www.bloomtext.com/developers/api/reference/list-member-events/) lists who was added, approved, or removed in an organization, newest first, and who did it. A change an API key made shows `actor_name` as `API key: HR sync`, and a change Okta made shows `Okta directory`. BloomText's member history export names them the same way, and Owners and IT see the API's changes in the account's [Audit log](https://www.bloomtext.com/how-to/security/audit-log/) as **API key: HR sync**.

## Errors

| Code | Status | Meaning |
| --- | --- | --- |
| `org_id_required` | 400 | The key reaches several organizations. Pass `org_id`. |
| `org_not_reachable` | 403 | The key can't reach the organization in `org_id`. |
| `accounts_not_enabled` | 403 | Accounts are off for this account. Contact BloomText to turn them on. |
| `already_member` | 409 | The person is already in that organization. |
| `patient_account` | 409 | The email belongs to a patient's BloomText account. |
| `managed_by_admin` | 409 | People in BloomText manage this membership or team placement. |
| `managed_by_account` | 409 | This is an Owner or IT person's membership. |
| `managed_by_identity_provider` | 409 | The account's Okta rules manage this membership or team placement. Change it in Okta or in the rules. |
| `confirmation_required` | 409 | Removing them deletes their user. Add `confirm_delete_user=true`. |

See [Errors](https://www.bloomtext.com/developers/api/errors/) for the problem object and every other code.
