# Find selectable team members

Use `selectableTeamMembers` to find people an integration can offer in a member picker, such as the @-mention picker in [Notes](https://developers.buffer.com/guides/notes.md). The query is experimental. It returns member profiles, not team-management roles or channel permissions.

## Before you start

Your personal API key or OAuth token needs `notes:read`. Creating or editing a Note with a mention also needs `notes:write`. Scopes do not replace the connected person's organization and channel permissions. See [Authentication](https://developers.buffer.com/guides/authentication.md) for requesting scopes.

You need the ID of the organization or channel you want to query. For a Post, use its `channelId` field. Looking up a Post with `post` or `posts` requires `posts:read`; listing channels requires `account:read`. See [Data Model](https://developers.buffer.com/guides/data-model.md) for finding organization and channel IDs; those lookups require their own scopes.

## Choose a scope

Provide exactly one non-null field in `input.scope`:

| Scope | Who is returned | When to use it |
| --- | --- | --- |
| `{ "channelId": "YOUR_CHANNEL_ID" }` | Members who can read Notes on that channel. Buffer derives the organization from the channel. | Mentions on a channel-specific Post. |
| `{ "organizationId": "YOUR_ORGANIZATION_ID" }` | Selectable members of the organization. | Mentions on a channel-less draft (`ContentItem`). |

The connected person must have access to the selected scope. The organization list can include members who cannot read Notes on a particular Post or channel-less draft. Use the Post's channel scope when choosing recipients for that Post. If the scope cannot be found, or the connected person lacks permission or feature access, the query reports a top-level GraphQL error rather than a typed error payload. See [Error Handling](https://developers.buffer.com/guides/error-handling.md).

## Query members

Run this query with the variables below:

```graphql
query SelectableTeamMembers(
  $input: SelectableTeamMembersInput!
  $first: Int = 20
  $after: String
) {
  selectableTeamMembers(input: $input, first: $first, after: $after) {
    edges {
      cursor
      node {
        id
        urn
        name
        email
        avatar
        isLocked
      }
    }
    pageInfo { hasNextPage endCursor }
  }
}
```

```json
{
  "input": {
    "scope": { "channelId": "YOUR_CHANNEL_ID" },
    "search": "alex"
  },
  "first": 20,
  "after": null
}
```

For an organization-wide picker, replace `scope` with `{ "organizationId": "YOUR_ORGANIZATION_ID" }`. Omit `search` to list everyone eligible in that scope.

`id` is a Buffer Account ID. `urn` is the complete account URN, in the form `urn:buffer:account:<account-id>`; use the returned value as a mention annotation's `serviceEntityId`. It is not a social-platform account ID or username. See [Add a mention to a Note](https://developers.buffer.com/guides/notes.md#add-a-mention) for the next request.

`name` can be null, so a picker can fall back to `email`. Request only the profile fields your app needs. On organizations with Roles, locked members with an assigned role keep viewer-level access and can be returned with `isLocked: true`. Locked members without a role, and those in organizations using older permissions, are excluded. Returned locked members can read Notes and be mentioned, but receive no Notes notifications while locked. Consider indicating this in your picker.

## Search and pagination

`search` matches a substring of a member's name or email, without case sensitivity. Omitted, null, empty or whitespace-only search returns every eligible member in scope.

Results are ordered by display name, with email used when no name is set, then by Account ID. The default page size is 10; `first` accepts integers from 1 through 100. When `pageInfo.hasNextPage` is true, pass `pageInfo.endCursor` as `after`. Treat cursors as opaque strings. See [Pagination](https://developers.buffer.com/guides/pagination.md).

Reset `after` to null when the scope or search changes. The list is live, so a member renamed between pages can appear twice or be missed. Merge pages by `node.id` if you keep a combined list, and refresh the search when current results matter.

## Handle errors and permission changes

Check the response's top-level GraphQL `errors`. Missing `notes:read` can return `INSUFFICIENT_SCOPE`; request the scope and authorize again. A scope with both IDs, neither ID, or a null selected ID is invalid. A page size outside 1–100 is also rejected. See [Error Handling](https://developers.buffer.com/guides/error-handling.md).

A selection is not a permission grant. Buffer checks every mentioned account against the Note's subject again on create or update. If any recipient is ineligible, the entire write is rejected with `InvalidInputError`; no partial Note is saved. Ask the person to choose a different recipient or remove the rejected mention. Refreshing the picker may return the same ineligible member, especially with organization scope; it does not change that member's access to the specific subject.
