diff --git a/packages/core/src/queries/organizations.ts b/packages/core/src/queries/organizations.ts
index 601ba3fa6..30ad9db0a 100644
--- a/packages/core/src/queries/organizations.ts
+++ b/packages/core/src/queries/organizations.ts
@@ -8,6 +8,7 @@ import {
   OrganizationRoleScopeRelations,
   Users,
   OrganizationUserRelations,
+  OrganizationRoleUserRelations,
 } from '@logto/schemas';
 import { type CommonQueryMethods } from 'slonik';
 
@@ -35,6 +36,14 @@ export default class OrganizationQueries extends SchemaQueries<
     ),
     /** Queries for organization - user relations. */
     users: new RelationQueries(this.pool, OrganizationUserRelations.table, Organizations, Users),
+    /** Queries for organization - organization role - user relations. */
+    rolesUsers: new RelationQueries(
+      this.pool,
+      OrganizationRoleUserRelations.table,
+      Organizations,
+      OrganizationRoles,
+      Users
+    ),
   };
 
   constructor(pool: CommonQueryMethods) {
diff --git a/packages/core/src/routes/organizations.ts b/packages/core/src/routes/organizations.ts
index 4ac17f014..e7ba28823 100644
--- a/packages/core/src/routes/organizations.ts
+++ b/packages/core/src/routes/organizations.ts
@@ -1,5 +1,8 @@
-import { Organizations } from '@logto/schemas';
+import { OrganizationRoles, Organizations } from '@logto/schemas';
+import { z } from 'zod';
 
+import RequestError from '#src/errors/RequestError/index.js';
+import koaGuard from '#src/middleware/koa-guard.js';
 import SchemaRouter, { SchemaActions } from '#src/utils/SchemaRouter.js';
 
 import { type AuthedRouter, type RouterInitArgs } from './types.js';
@@ -8,7 +11,7 @@ export default function organizationRoutes<T extends AuthedRouter>(
   ...[
     originalRouter,
     {
-      queries: { organizations },
+      queries: { organizations, users },
     },
   ]: RouterInitArgs<T>
 ) {
@@ -16,5 +19,77 @@ export default function organizationRoutes<T extends AuthedRouter>(
 
   router.addRelationRoutes(organizations.relations.users);
 
+  // Manually add these routes since I don't want to over-engineer the `SchemaRouter`
+  // MARK: Organization - user - organization role relation routes
+  const params = Object.freeze({ id: z.string().min(1), userId: z.string().min(1) } as const);
+  const pathname = '/:id/users/:userId/roles';
+
+  router.get(
+    pathname,
+    koaGuard({
+      params: z.object(params),
+      response: OrganizationRoles.guard.array(),
+      status: [200, 404],
+    }),
+    // TODO: Add pagination
+    async (ctx, next) => {
+      const { id, userId } = ctx.guard.params;
+
+      // Ensure both the organization and the role exist
+      await Promise.all([organizations.findById(id), users.findUserById(userId)]);
+
+      ctx.body = await organizations.relations.rolesUsers.getEntries(OrganizationRoles, {
+        organizationId: id,
+        userId,
+      });
+      return next();
+    }
+  );
+
+  router.post(
+    pathname,
+    koaGuard({
+      params: z.object(params),
+      body: z.object({ roleIds: z.string().min(1).array().nonempty() }),
+      status: [201, 404, 422],
+    }),
+    async (ctx, next) => {
+      const { id, userId } = ctx.guard.params;
+      const { roleIds } = ctx.guard.body;
+
+      // Ensure membership
+      if (!(await organizations.relations.users.exists(id, userId))) {
+        throw new RequestError({ code: 'organization.require_membership', status: 422 });
+      }
+
+      await organizations.relations.rolesUsers.insert(
+        ...roleIds.map<[string, string, string]>((roleId) => [id, roleId, userId])
+      );
+
+      ctx.status = 201;
+      return next();
+    }
+  );
+
+  router.delete(
+    `${pathname}/:roleId`,
+    koaGuard({
+      params: z.object({ ...params, roleId: z.string().min(1) }),
+      status: [204, 404],
+    }),
+    async (ctx, next) => {
+      const { id, roleId, userId } = ctx.guard.params;
+
+      await organizations.relations.rolesUsers.delete({
+        organizationId: id,
+        organizationRoleId: roleId,
+        userId,
+      });
+
+      ctx.status = 204;
+      return next();
+    }
+  );
+
   originalRouter.use(router.routes());
 }
diff --git a/packages/core/src/utils/RelationQueries.ts b/packages/core/src/utils/RelationQueries.ts
index 36f248ef0..b2dfa1232 100644
--- a/packages/core/src/utils/RelationQueries.ts
+++ b/packages/core/src/utils/RelationQueries.ts
@@ -80,6 +80,9 @@ export default class RelationQueries<
    * Each entry must contain the same number of ids as the number of relations, and
    * the order of the ids must match the order of the relations.
    *
+   * @param data Entries to insert.
+   * @returns A Promise that resolves to the query result.
+   *
    * @example
    * ```ts
    * const userGroupRelations = new RelationQueries(pool, 'user_group_relations', Users, Groups);
@@ -91,9 +94,6 @@ export default class RelationQueries<
    *   ['user-id-2', 'group-id-1']
    * );
    * ```
-   *
-   * @param data Entries to insert.
-   * @returns A Promise that resolves to the query result.
    */
   async insert(...data: ReadonlyArray<string[] & { length: Length }>) {
     return this.pool.query(sql`
@@ -179,4 +179,32 @@ export default class RelationQueries<
 
     return rows;
   }
+
+  /**
+   * Check if a relation exists.
+   *
+   * @param ids The ids of the entries to check. The order of the ids must match the order of the relations.
+   * @returns A Promise that resolves to `true` if the relation exists, otherwise `false`.
+   *
+   * @example
+   * ```ts
+   * const userGroupRelations = new RelationQueries(pool, 'user_group_relations', Users, Groups);
+   *
+   * userGroupRelations.exists('user-id-1', 'group-id-1');
+   * ```
+   */
+  async exists(...ids: readonly string[] & { length: Length }) {
+    return this.pool.exists(sql`
+      select
+      from ${this.table}
+      where ${sql.join(
+        this.schemas.map(
+          ({ tableSingular }, index) =>
+            sql`${sql.identifier([tableSingular + '_id'])} = ${ids[index] ?? sql`null`}`
+        ),
+        sql` and `
+      )}
+      limit 1
+    `);
+  }
 }
diff --git a/packages/core/src/utils/SchemaRouter.ts b/packages/core/src/utils/SchemaRouter.ts
index 760d78cbf..ca1c63a75 100644
--- a/packages/core/src/utils/SchemaRouter.ts
+++ b/packages/core/src/utils/SchemaRouter.ts
@@ -315,8 +315,7 @@ export default class SchemaRouter<
       koaGuard({
         params: z.object({ id: z.string().min(1) }),
         body: z.object({ [columns.relationSchemaIds]: z.string().min(1).array().nonempty() }),
-        response: relationSchema.guard.array(),
-        status: [200, 404, 422],
+        status: [201, 404, 422],
       }),
       async (ctx, next) => {
         const {
@@ -327,8 +326,7 @@ export default class SchemaRouter<
         await relationQueries.insert(
           ...(relationIds?.map<[string, string]>((relationId) => [id, relationId]) ?? [])
         );
-
-        ctx.body = await relationQueries.getEntries(relationSchema, { [columns.schemaId]: id });
+        ctx.status = 201;
         return next();
       }
     );
diff --git a/packages/integration-tests/src/api/organization.ts b/packages/integration-tests/src/api/organization.ts
index fceaeeaa4..d71a4cd55 100644
--- a/packages/integration-tests/src/api/organization.ts
+++ b/packages/integration-tests/src/api/organization.ts
@@ -1,4 +1,4 @@
-import { type Organization } from '@logto/schemas';
+import { type Role, type Organization } from '@logto/schemas';
 
 import { authedAdminApi } from './api.js';
 import { ApiFactory } from './factory.js';
@@ -19,6 +19,18 @@ class OrganizationApi extends ApiFactory<Organization, { name: string; descripti
   async deleteUser(id: string, userId: string): Promise<void> {
     await authedAdminApi.delete(`${this.path}/${id}/users/${userId}`);
   }
+
+  async addUserRoles(id: string, userId: string, roleIds: string[]): Promise<void> {
+    await authedAdminApi.post(`${this.path}/${id}/users/${userId}/roles`, { json: { roleIds } });
+  }
+
+  async getUserRoles(id: string, userId: string): Promise<Role[]> {
+    return authedAdminApi.get(`${this.path}/${id}/users/${userId}/roles`).json<Role[]>();
+  }
+
+  async deleteUserRole(id: string, userId: string, roleId: string): Promise<void> {
+    await authedAdminApi.delete(`${this.path}/${id}/users/${userId}/roles/${roleId}`);
+  }
 }
 
 /** API methods for operating organizations. */
diff --git a/packages/integration-tests/src/tests/api/organization.test.ts b/packages/integration-tests/src/tests/api/organization.test.ts
index d6b45e3f3..608469f33 100644
--- a/packages/integration-tests/src/tests/api/organization.test.ts
+++ b/packages/integration-tests/src/tests/api/organization.test.ts
@@ -1,7 +1,10 @@
+import assert from 'node:assert';
+
 import { generateStandardId } from '@logto/shared';
 import { HTTPError } from 'got';
 
 import { createUser, deleteUser } from '#src/api/admin-user.js';
+import { roleApi } from '#src/api/organization-role.js';
 import { organizationApi } from '#src/api/organization.js';
 
 const randomId = () => generateStandardId(4);
@@ -123,4 +126,37 @@ describe('organization APIs', () => {
       await Promise.all([organizationApi.delete(organization.id), deleteUser(user.id)]);
     });
   });
+
+  describe('organization - user - organization role relation routes', () => {
+    it("should be able to add and get user's organization roles", async () => {
+      const organization = await organizationApi.create({ name: 'test' });
+      const user = await createUser({ username: 'test' + randomId() });
+      const [role1, role2] = await Promise.all([
+        roleApi.create({ name: 'test' + randomId() }),
+        roleApi.create({ name: 'test' + randomId() }),
+      ]);
+
+      const response = await organizationApi
+        .addUserRoles(organization.id, user.id, [role1.id, role2.id])
+        .catch((error: unknown) => error);
+
+      assert(response instanceof HTTPError);
+      expect(response.response.statusCode).toBe(422);
+      expect(JSON.parse(String(response.response.body))).toMatchObject(
+        expect.objectContaining({ code: 'organization.require_membership' })
+      );
+
+      await organizationApi.addUsers(organization.id, [user.id]);
+      await organizationApi.addUserRoles(organization.id, user.id, [role1.id, role2.id]);
+      const roles = await organizationApi.getUserRoles(organization.id, user.id);
+      expect(roles).toContainEqual(expect.objectContaining({ id: role1.id }));
+      expect(roles).toContainEqual(expect.objectContaining({ id: role2.id }));
+      await Promise.all([
+        organizationApi.delete(organization.id),
+        deleteUser(user.id),
+        roleApi.delete(role1.id),
+        roleApi.delete(role2.id),
+      ]);
+    });
+  });
 });
diff --git a/packages/phrases/src/locales/de/errors/index.ts b/packages/phrases/src/locales/de/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/de/errors/index.ts
+++ b/packages/phrases/src/locales/de/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/de/errors/organization.ts b/packages/phrases/src/locales/de/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/de/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/en/errors/index.ts b/packages/phrases/src/locales/en/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/en/errors/index.ts
+++ b/packages/phrases/src/locales/en/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/en/errors/organization.ts b/packages/phrases/src/locales/en/errors/organization.ts
new file mode 100644
index 000000000..fa1558b0d
--- /dev/null
+++ b/packages/phrases/src/locales/en/errors/organization.ts
@@ -0,0 +1,5 @@
+const organization = {
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/es/errors/index.ts b/packages/phrases/src/locales/es/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/es/errors/index.ts
+++ b/packages/phrases/src/locales/es/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/es/errors/organization.ts b/packages/phrases/src/locales/es/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/es/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/fr/errors/index.ts b/packages/phrases/src/locales/fr/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/fr/errors/index.ts
+++ b/packages/phrases/src/locales/fr/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/fr/errors/organization.ts b/packages/phrases/src/locales/fr/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/fr/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/it/errors/index.ts b/packages/phrases/src/locales/it/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/it/errors/index.ts
+++ b/packages/phrases/src/locales/it/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/it/errors/organization.ts b/packages/phrases/src/locales/it/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/it/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/ja/errors/index.ts b/packages/phrases/src/locales/ja/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/ja/errors/index.ts
+++ b/packages/phrases/src/locales/ja/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/ja/errors/organization.ts b/packages/phrases/src/locales/ja/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/ja/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/ko/errors/index.ts b/packages/phrases/src/locales/ko/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/ko/errors/index.ts
+++ b/packages/phrases/src/locales/ko/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/ko/errors/organization.ts b/packages/phrases/src/locales/ko/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/ko/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/pl-pl/errors/index.ts b/packages/phrases/src/locales/pl-pl/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/pl-pl/errors/index.ts
+++ b/packages/phrases/src/locales/pl-pl/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/pl-pl/errors/organization.ts b/packages/phrases/src/locales/pl-pl/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/pl-pl/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/pt-br/errors/index.ts b/packages/phrases/src/locales/pt-br/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/pt-br/errors/index.ts
+++ b/packages/phrases/src/locales/pt-br/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/pt-br/errors/organization.ts b/packages/phrases/src/locales/pt-br/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/pt-br/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/pt-pt/errors/index.ts b/packages/phrases/src/locales/pt-pt/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/pt-pt/errors/index.ts
+++ b/packages/phrases/src/locales/pt-pt/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/pt-pt/errors/organization.ts b/packages/phrases/src/locales/pt-pt/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/pt-pt/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/ru/errors/index.ts b/packages/phrases/src/locales/ru/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/ru/errors/index.ts
+++ b/packages/phrases/src/locales/ru/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/ru/errors/organization.ts b/packages/phrases/src/locales/ru/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/ru/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/tr-tr/errors/index.ts b/packages/phrases/src/locales/tr-tr/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/tr-tr/errors/index.ts
+++ b/packages/phrases/src/locales/tr-tr/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/tr-tr/errors/organization.ts b/packages/phrases/src/locales/tr-tr/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/tr-tr/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/zh-cn/errors/index.ts b/packages/phrases/src/locales/zh-cn/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/zh-cn/errors/index.ts
+++ b/packages/phrases/src/locales/zh-cn/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/zh-cn/errors/organization.ts b/packages/phrases/src/locales/zh-cn/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/zh-cn/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/zh-hk/errors/index.ts b/packages/phrases/src/locales/zh-hk/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/zh-hk/errors/index.ts
+++ b/packages/phrases/src/locales/zh-hk/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/zh-hk/errors/organization.ts b/packages/phrases/src/locales/zh-hk/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/zh-hk/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);
diff --git a/packages/phrases/src/locales/zh-tw/errors/index.ts b/packages/phrases/src/locales/zh-tw/errors/index.ts
index f4c8f1ed2..acf9eda5f 100644
--- a/packages/phrases/src/locales/zh-tw/errors/index.ts
+++ b/packages/phrases/src/locales/zh-tw/errors/index.ts
@@ -8,6 +8,7 @@ import hook from './hook.js';
 import localization from './localization.js';
 import log from './log.js';
 import oidc from './oidc.js';
+import organization from './organization.js';
 import password from './password.js';
 import request from './request.js';
 import resource from './resource.js';
@@ -44,6 +45,7 @@ const errors = {
   domain,
   subscription,
   application,
+  organization,
 };
 
 export default Object.freeze(errors);
diff --git a/packages/phrases/src/locales/zh-tw/errors/organization.ts b/packages/phrases/src/locales/zh-tw/errors/organization.ts
new file mode 100644
index 000000000..2752f71f9
--- /dev/null
+++ b/packages/phrases/src/locales/zh-tw/errors/organization.ts
@@ -0,0 +1,6 @@
+const organization = {
+  /** UNTRANSLATED */
+  require_membership: 'The user must be a member of the organization to proceed.',
+};
+
+export default Object.freeze(organization);