diff --git a/docs/content/docs/plugins/organization.mdx b/docs/content/docs/plugins/organization.mdx
index b2d43f8936..5c4df8d821 100644
--- a/docs/content/docs/plugins/organization.mdx
+++ b/docs/content/docs/plugins/organization.mdx
@@ -560,7 +560,7 @@ await authClient.organization.getInvitation({
### List Invitations
-To list all invitations you can use the `listInvitations` function provided by the client.
+To list all invitations for a given organization you can use the `listInvitations` function provided by the client.
```ts title="auth-client.ts"
const invitations = await authClient.organization.listInvitations({
@@ -570,6 +570,30 @@ const invitations = await authClient.organization.listInvitations({
})
```
+### List user invitations
+
+To list all invitations for a given user you can use the `listUserInvitations` function provided by the client.
+
+```ts title="auth-client.ts"
+const invitations = await authClient.organization.listUserInvitations()
+```
+
+On the server, you can pass the user ID as a query parameter.
+
+```ts title="api.ts"
+const invitations = await auth.api.listUserInvitations({
+ query: {
+ email: "user@example.com"
+ }
+})
+```
+
+The `email` query parameter is only available on the server to query for invitations for a specific user.
+
+
+
+
+
## Members
### Remove Member
diff --git a/packages/better-auth/src/plugins/organization/adapter.ts b/packages/better-auth/src/plugins/organization/adapter.ts
index 9f26219d7d..2761d36be0 100644
--- a/packages/better-auth/src/plugins/organization/adapter.ts
+++ b/packages/better-auth/src/plugins/organization/adapter.ts
@@ -561,7 +561,13 @@ export const getOrgAdapter = (
});
return invitations;
},
-
+ listUserInvitations: async (email: string) => {
+ const invitations = await adapter.findMany({
+ model: "invitation",
+ where: [{ field: "email", value: email }],
+ });
+ return invitations;
+ },
createInvitation: async ({
invitation,
user,
diff --git a/packages/better-auth/src/plugins/organization/organization.test.ts b/packages/better-auth/src/plugins/organization/organization.test.ts
index a450891c0d..ae921fdfec 100644
--- a/packages/better-auth/src/plugins/organization/organization.test.ts
+++ b/packages/better-auth/src/plugins/organization/organization.test.ts
@@ -703,10 +703,11 @@ describe("organization", async (it) => {
password: userOverLimit.password,
name: userOverLimit.name,
});
- const { res, headers: headers2 } = await signInWithUser(
+ const { headers: headers2 } = await signInWithUser(
userOverLimit2.email,
userOverLimit2.password,
);
+
await client.signUp.email(
{
email: userOverLimit2.email,
@@ -738,6 +739,92 @@ describe("organization", async (it) => {
});
expect(getFullOrganization.data?.members.length).toBe(6);
});
+
+ it("should allow listing invitations for an org", async () => {
+ const invitations = await client.organization.listInvitations({
+ query: {
+ organizationId: organizationId,
+ },
+ fetchOptions: {
+ headers: headers,
+ },
+ });
+ expect(invitations.data?.length).toBe(4);
+ });
+
+ it("should allow listing invitations for a user using authClient", async () => {
+ const rng = crypto.randomUUID();
+ const user = {
+ email: `${rng}@email.com`,
+ password: rng,
+ name: rng,
+ };
+ const rng2 = crypto.randomUUID();
+ const orgAdminUser = {
+ email: `${rng2}@email.com`,
+ password: rng2,
+ name: rng2,
+ };
+ await auth.api.signUpEmail({
+ body: user,
+ });
+ await auth.api.signUpEmail({
+ body: orgAdminUser,
+ });
+ const { headers: headers2, res: session } = await signInWithUser(
+ user.email,
+ user.password,
+ );
+ const { headers: adminHeaders, res: adminSession } = await signInWithUser(
+ orgAdminUser.email,
+ orgAdminUser.password,
+ );
+ const orgRng = crypto.randomUUID();
+ const org = await auth.api.createOrganization({
+ body: {
+ name: orgRng,
+ slug: orgRng,
+ },
+ headers: adminHeaders,
+ });
+ const invitation = await client.organization.inviteMember({
+ organizationId: org?.id,
+ email: user.email,
+ role: "member",
+ fetchOptions: {
+ headers: adminHeaders,
+ },
+ });
+ const userInvitations = await client.organization.listUserInvitations({
+ fetchOptions: {
+ headers: headers2,
+ },
+ });
+ expect(userInvitations.data?.[0].id).toBe(invitation.data?.id);
+ expect(userInvitations.data?.length).toBe(1);
+ });
+
+ it("should allow listing invitations for a user using server", async () => {
+ const orgInvitations = await client.organization.listInvitations({
+ fetchOptions: {
+ headers,
+ },
+ });
+
+ if (!orgInvitations.data?.[0].email) throw new Error("No email found");
+
+ const invitations = await auth.api.listUserInvitations({
+ query: {
+ email: orgInvitations.data?.[0].email,
+ },
+ });
+
+ expect(invitations?.length).toBe(
+ orgInvitations.data.filter(
+ (x) => x.email === orgInvitations.data?.[0].email,
+ ).length,
+ );
+ });
});
describe("access control", async (it) => {
diff --git a/packages/better-auth/src/plugins/organization/organization.ts b/packages/better-auth/src/plugins/organization/organization.ts
index 4197d9c633..dbbb1f825f 100644
--- a/packages/better-auth/src/plugins/organization/organization.ts
+++ b/packages/better-auth/src/plugins/organization/organization.ts
@@ -16,6 +16,7 @@ import {
getInvitation,
listInvitations,
rejectInvitation,
+ listUserInvitations,
} from "./routes/crud-invites";
import {
addMember,
@@ -386,6 +387,7 @@ export const organization = (options?: O) => {
acceptInvitation,
getInvitation,
rejectInvitation,
+ listUserInvitations,
checkOrganizationSlug,
addMember: addMember(),
removeMember,
diff --git a/packages/better-auth/src/plugins/organization/routes/crud-invites.ts b/packages/better-auth/src/plugins/organization/routes/crud-invites.ts
index 8f51d4e58f..25697bd2bc 100644
--- a/packages/better-auth/src/plugins/organization/routes/crud-invites.ts
+++ b/packages/better-auth/src/plugins/organization/routes/crud-invites.ts
@@ -745,3 +745,44 @@ export const listInvitations = createAuthEndpoint(
return ctx.json(invitations);
},
);
+
+/**
+ * List all invitations recieved for a user
+ */
+export const listUserInvitations = createAuthEndpoint(
+ "/organization/list-user-invitations",
+ {
+ method: "GET",
+ use: [orgMiddleware],
+ query: z
+ .object({
+ email: z
+ .string({
+ description:
+ "The email of the user to list invitations for. This only works for server side API calls.",
+ })
+ .optional(),
+ })
+ .optional(),
+ },
+ async (ctx) => {
+ const session = await getSessionFromCtx(ctx);
+
+ if (ctx.request && ctx.query?.email) {
+ throw new APIError("BAD_REQUEST", {
+ message: "User email cannot be passed for client side API calls.",
+ });
+ }
+
+ const userEmail = session?.user.email || ctx.query?.email;
+ if (!userEmail) {
+ throw new APIError("BAD_REQUEST", {
+ message: "Missing session headers, or email query parameter.",
+ });
+ }
+ const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
+
+ const invitations = await adapter.listUserInvitations(userEmail);
+ return ctx.json(invitations);
+ },
+);