[GH-ISSUE #8740] Organization Plugin: listMembers requires organizationId, preventing cross-organization member search #28499

Open
opened 2026-04-17 19:57:05 -05:00 by GiteaMirror · 3 comments
Owner

Originally created by @anmol-fzr on GitHub (Mar 23, 2026).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/8740

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Minimal Steps to Reproduce

  1. Initialize the Better Auth client with the organization plugin enabled.

  2. Call listMembers without providing organizationId:

await authClient.organization.listMembers({
    query: {
        limit: 10,
        offset: 0,
    },
});
  1. Observe the response:

    • Request fails with 400 Bad Request
    • Error indicates missing required organizationId
  2. Attempt to pass undefined or null:

await authClient.organization.listMembers({
    query: {
        organizationId: undefined as any,
        limit: 10,
    },
});
  1. Observe:

    • Same 400 error or validation failure

Result:
There is no way to call listMembers without a single organizationId, preventing cross-organization queries.

Current vs. Expected behavior

Current Behavior:
The authClient.organization.listMembers method enforces organizationId as a required field in the query. When attempting to omit it (to perform a global or cross-organization member search), the server responds with a 400 Bad Request.

Example:

const { data, error } = await authClient.organization.listMembers({
    query: {
        // organizationId intentionally omitted
        limit: 100,
        offset: 0,
        sortBy: "createdAt",
        sortDirection: "desc",
    },
});

This results in:

Request rejection with 400
No way to query members across multiple organizations

Expected Behavior:
There should be a supported way to query members across multiple organizations, such as:

Making organizationId optional

Allowing an array of organization IDs:

organizationIds: string[]
Or providing a separate API (e.g., listMembersGlobal or searchMembers) for cross-organization queries

This would enable use cases like:

Searching users across all organizations a user belongs to
Admin dashboards spanning multiple organizations
Global member lookup by email or identifier

What version of Better Auth are you using?

1.5.2

System info

{
  "system": {
    "platform": "linux",
    "arch": "x64",
    "version": "#1 SMP PREEMPT_DYNAMIC Mon, 24 Nov 2025 15:21:09 +0000",
    "release": "6.17.9-arch1-1",
    "cpuCount": 16,
    "cpuModel": "12th Gen Intel(R) Core(TM) i5-1240P",
    "totalMemory": "23.17 GB",
    "freeMemory": "4.00 GB"
  },
  "node": {
    "version": "v22.21.1",
    "env": "development"
  },
  "packageManager": {
    "name": "pnpm",
    "version": "10.30.3"
  },
  "frameworks": null,
  "databases": null,
  "betterAuth": {
    "version": "1.5.2",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  emailAndPassword: {  
    enabled: true
  },
  plugins: [organization()]
});

Additional context

This limitation creates friction in multi-tenant applications where users may belong to multiple organizations and need to be searched globally (e.g., searching by email before inviting or assigning roles).

Currently, the only workaround is to:

Fetch all organizations
Iterate and call listMembers per organization
Aggregate results manually

This approach is:

Inefficient (N API calls)
Harder to paginate and sort globally
Adds unnecessary client-side complexity

A first-class API or optional parameter support would significantly improve developer experience and scalability for multi-org use cases.

Originally created by @anmol-fzr on GitHub (Mar 23, 2026). Original GitHub issue: https://github.com/better-auth/better-auth/issues/8740 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce **Minimal Steps to Reproduce** 1. Initialize the Better Auth client with the organization plugin enabled. 2. Call `listMembers` **without** providing `organizationId`: ```ts id="9kz1lp" await authClient.organization.listMembers({ query: { limit: 10, offset: 0, }, }); ``` 3. Observe the response: * Request fails with `400 Bad Request` * Error indicates missing required `organizationId` 4. Attempt to pass `undefined` or `null`: ```ts id="wq3m8x" await authClient.organization.listMembers({ query: { organizationId: undefined as any, limit: 10, }, }); ``` 5. Observe: * Same `400` error or validation failure **Result:** There is no way to call `listMembers` without a single `organizationId`, preventing cross-organization queries. ### Current vs. Expected behavior Current Behavior: The authClient.organization.listMembers method enforces organizationId as a required field in the query. When attempting to omit it (to perform a global or cross-organization member search), the server responds with a 400 Bad Request. Example: ``` const { data, error } = await authClient.organization.listMembers({ query: { // organizationId intentionally omitted limit: 100, offset: 0, sortBy: "createdAt", sortDirection: "desc", }, }); ``` This results in: Request rejection with 400 No way to query members across multiple organizations Expected Behavior: There should be a supported way to query members across multiple organizations, such as: Making organizationId optional Allowing an array of organization IDs: organizationIds: string[] Or providing a separate API (e.g., listMembersGlobal or searchMembers) for cross-organization queries This would enable use cases like: Searching users across all organizations a user belongs to Admin dashboards spanning multiple organizations Global member lookup by email or identifier ### What version of Better Auth are you using? 1.5.2 ### System info ```bash { "system": { "platform": "linux", "arch": "x64", "version": "#1 SMP PREEMPT_DYNAMIC Mon, 24 Nov 2025 15:21:09 +0000", "release": "6.17.9-arch1-1", "cpuCount": 16, "cpuModel": "12th Gen Intel(R) Core(TM) i5-1240P", "totalMemory": "23.17 GB", "freeMemory": "4.00 GB" }, "node": { "version": "v22.21.1", "env": "development" }, "packageManager": { "name": "pnpm", "version": "10.30.3" }, "frameworks": null, "databases": null, "betterAuth": { "version": "1.5.2", "config": null } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ emailAndPassword: { enabled: true }, plugins: [organization()] }); ``` ### Additional context This limitation creates friction in multi-tenant applications where users may belong to multiple organizations and need to be searched globally (e.g., searching by email before inviting or assigning roles). Currently, the only workaround is to: Fetch all organizations Iterate and call listMembers per organization Aggregate results manually This approach is: Inefficient (N API calls) Harder to paginate and sort globally Adds unnecessary client-side complexity A first-class API or optional parameter support would significantly improve developer experience and scalability for multi-org use cases.
GiteaMirror added the organization label 2026-04-17 19:57:05 -05:00
Author
Owner

@WilsonnnTan commented on GitHub (Mar 25, 2026):

Hi @anmol-fzr , from what I can see in the codebase, the /organization/list-members API falls back to the user's active organizationId from their session when no organizationId is provided in the query. So regardless of whether you pass an org ID or not, it always resolves to a single org ID either from the query or from the session. and as you mentioned, this makes cross-organization queries inefficient since it requires N separate API calls.

I'd like to work on this issue by introducing a new API endpoint that accepts either an optional organizationId or an array of organization IDs, intended for querying members across multiple organizations the user belongs to.
The reason I'm proposing a new API rather than modifying the existing one is for backward compatibility so that existing behavior of listMembers and the session structure will remain untouched, so there are no breaking changes.

<!-- gh-comment-id:4123601804 --> @WilsonnnTan commented on GitHub (Mar 25, 2026): Hi @anmol-fzr , from what I can see in the codebase, the `/organization/list-members` API falls back to the user's active organizationId from their session when no organizationId is provided in the query. So regardless of whether you pass an org ID or not, it always resolves to a single org ID either from the query or from the session. and as you mentioned, this makes cross-organization queries inefficient since it requires N separate API calls. I'd like to work on this issue by introducing a new API endpoint that accepts either an optional organizationId or an array of organization IDs, intended for querying members across multiple organizations the user belongs to. The reason I'm proposing a new API rather than modifying the existing one is for backward compatibility so that existing behavior of listMembers and the session structure will remain untouched, so there are no breaking changes.
Author
Owner

@WilsonnnTan commented on GitHub (Mar 25, 2026):

Hi, below is the new API that i proposed

Proposed API: listMembersByOrganizations

Route

GET /organization/list-members-by-organizations

Description

Lists members grouped by organization. Accepts an optional array of organization IDs. If no IDs are provided, the API resolves all organizations the authenticated user belongs to and returns members for each of them.

Query Parameters

Parameter Type Required Description
organizationIds string[] No Array of organization IDs to query. If omitted, resolves all organizations the user belongs to
limit number No Max number of members per organization (default: 10)
offset number No Pagination offset
sortBy string No Field to sort members by
sortDirection "asc" or "desc" No Sort direction

Usage

// Pass specific organization IDs
await authClient.organization.listMembersByOrganizations({
  query: {
    organizationIds: ["org-id-1", "org-id-2"],
    limit: 10,
    offset: 0,
  }
})
 
// Omit organizationIds to resolve all organizations the user belongs to
await authClient.organization.listMembersByOrganizations({
  query: {
    limit: 10,
    offset: 0,
  }
})

Response Body

{
  "organizations": [
    {
      "id": "org-1",
      "slug": "org-slug-1",
      "members": [
        {
          "id": "member-1",
          "userId": "user-1",
          "organizationId": "org-1",
          "role": "admin",
          "createdAt": "2024-01-15T10:30:00Z"
        },
        {
          "id": "member-2",
          "userId": "user-2",
          "organizationId": "org-1",
          "role": "member",
          "createdAt": "2024-01-16T14:45:00Z"
        }
      ],
      "total": 2
    },
    {
      "id": "org-2",
      "slug": "org-slug-2",
      "members": [
        {
          "id": "member-3",
          "userId": "user-3",
          "organizationId": "org-2",
          "role": "owner",
          "createdAt": "2024-02-10T09:00:00Z"
        }
      ],
      "total": 1
    }
  ]
}

Response Fields

Field Type Description
organizations array List of organizations with their members
organizations[].id string Organization ID
organizations[].slug string Organization slug
organizations[].members array List of members belonging to this organization
organizations[].total number Total number of members in this organization

Comparison with Existing API

API Route Scope
listMembers /organization/list-members Single org (one ID or active session org)
listMembersByOrganizations /organization/list-members-by-organizations Multiple orgs (array of IDs or all user's orgs)

Notes

  • This is a new endpoint and does not modify or replace the existing listMembers API.
  • When organizationIds is omitted, the server resolves the user's organization memberships from the database, avoiding N client-side calls.
  • Pagination via limit and offset applies per organization, not globally across all organizations.
<!-- gh-comment-id:4123987982 --> @WilsonnnTan commented on GitHub (Mar 25, 2026): Hi, below is the new API that i proposed # Proposed API: listMembersByOrganizations ## Route ``` GET /organization/list-members-by-organizations ``` ## Description Lists members grouped by organization. Accepts an optional array of organization IDs. If no IDs are provided, the API resolves all organizations the authenticated user belongs to and returns members for each of them. ## Query Parameters | Parameter | Type | Required | Description | |---|---|---|---| | `organizationIds` | `string[]` | No | Array of organization IDs to query. If omitted, resolves all organizations the user belongs to | | `limit` | `number` | No | Max number of members per organization (default: 10) | | `offset` | `number` | No | Pagination offset | | `sortBy` | `string` | No | Field to sort members by | | `sortDirection` | `"asc" or "desc"` | No | Sort direction | ## Usage ```ts // Pass specific organization IDs await authClient.organization.listMembersByOrganizations({ query: { organizationIds: ["org-id-1", "org-id-2"], limit: 10, offset: 0, } }) // Omit organizationIds to resolve all organizations the user belongs to await authClient.organization.listMembersByOrganizations({ query: { limit: 10, offset: 0, } }) ``` ## Response Body ```json { "organizations": [ { "id": "org-1", "slug": "org-slug-1", "members": [ { "id": "member-1", "userId": "user-1", "organizationId": "org-1", "role": "admin", "createdAt": "2024-01-15T10:30:00Z" }, { "id": "member-2", "userId": "user-2", "organizationId": "org-1", "role": "member", "createdAt": "2024-01-16T14:45:00Z" } ], "total": 2 }, { "id": "org-2", "slug": "org-slug-2", "members": [ { "id": "member-3", "userId": "user-3", "organizationId": "org-2", "role": "owner", "createdAt": "2024-02-10T09:00:00Z" } ], "total": 1 } ] } ``` ## Response Fields | Field | Type | Description | |---|---|---| | `organizations` | `array` | List of organizations with their members | | `organizations[].id` | `string` | Organization ID | | `organizations[].slug` | `string` | Organization slug | | `organizations[].members` | `array` | List of members belonging to this organization | | `organizations[].total` | `number` | Total number of members in this organization | ## Comparison with Existing API | API | Route | Scope | |---|---|---| | `listMembers` | `/organization/list-members` | Single org (one ID or active session org) | | `listMembersByOrganizations` | `/organization/list-members-by-organizations` | Multiple orgs (array of IDs or all user's orgs) | ## Notes - This is a new endpoint and does not modify or replace the existing `listMembers` API. - When `organizationIds` is omitted, the server resolves the user's organization memberships from the database, avoiding N client-side calls. - Pagination via `limit` and `offset` applies per organization, not globally across all organizations.
Author
Owner

@WilsonnnTan commented on GitHub (Mar 25, 2026):

hi @ping-maxwell @bytaesu, can you review my proposed API for this issue?
Thanks ❤️

<!-- gh-comment-id:4124041134 --> @WilsonnnTan commented on GitHub (Mar 25, 2026): hi @ping-maxwell @bytaesu, can you review my proposed API for this issue? Thanks ❤️
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#28499