diff --git a/apps/shared/package.json b/apps/shared/package.json
index 8bd91aba98c275d2a099f75f9054d8647874cc54..e092ae552055278ec57d94efd8a8623dd2c7fccf 100644
--- a/apps/shared/package.json
+++ b/apps/shared/package.json
@@ -21,6 +21,7 @@
     "test:e2e": "jest --config ./test/jest.config.js"
   },
   "dependencies": {
+    "@aries-framework/anoncreds": "0.4.2",
     "@aries-framework/core": "0.4.2",
     "@aries-framework/tenants": "^0.4.2",
     "@elastic/ecs-winston-format": "^1.5.0",
diff --git a/apps/shared/src/events/schemaEvents.ts b/apps/shared/src/events/schemaEvents.ts
new file mode 100644
index 0000000000000000000000000000000000000000..29cb53b233bd9191134ecf5606a676d784714b78
--- /dev/null
+++ b/apps/shared/src/events/schemaEvents.ts
@@ -0,0 +1,70 @@
+import type { BaseEventInput } from './baseEvents.js';
+import type { AnonCredsSchema } from '@aries-framework/anoncreds';
+
+import { BaseEvent } from './baseEvents.js';
+
+export type EventAnonCredsSchemasGetAllInput = BaseEventInput;
+export class EventAnonCredsSchemasGetAll extends BaseEvent<
+  Array<AnonCredsSchema>
+> {
+  public static token = 'anoncreds.schemas.getAll';
+
+  public get instance() {
+    return this.data;
+  }
+
+  public static fromEvent(e: EventAnonCredsSchemasGetAll) {
+    return new EventAnonCredsSchemasGetAll(
+      e.data,
+      e.tenantId,
+      e.id,
+      e.type,
+      e.timestamp,
+    );
+  }
+}
+
+export type EventAnonCredsSchemasGetByIdInput = BaseEventInput<{
+  schemaId: string;
+}>;
+export class EventAnonCredsSchemasGetById extends BaseEvent<AnonCredsSchema | null> {
+  public static token = 'anoncreds.schemas.getById';
+
+  public get instance() {
+    return this.data;
+  }
+
+  public static fromEvent(e: EventAnonCredsSchemasGetById) {
+    return new EventAnonCredsSchemasGetById(
+      e.data,
+      e.tenantId,
+      e.id,
+      e.type,
+      e.timestamp,
+    );
+  }
+}
+
+export type EventAnonCredsSchemasRegisterInput = BaseEventInput<{
+  issuerDid: string;
+  name: string;
+  version: string;
+  attributeNames: Array<string>;
+}>;
+export class EventAnonCredsSchemasRegister extends BaseEvent<AnonCredsSchema> {
+  public static token = 'anoncreds.schemas.register';
+
+  public get instance() {
+    return this.data;
+  }
+
+  public static fromEvent(e: EventAnonCredsSchemasRegister) {
+    return new EventAnonCredsSchemasRegister(
+      e.data,
+      e.tenantId,
+      e.id,
+      e.type,
+      e.timestamp,
+    );
+  }
+}
diff --git a/apps/shared/src/index.ts b/apps/shared/src/index.ts
index 42fe80034abf82559f144a6549e28bfc314b6b55..7f8ae5efe78d58642c60d2b62c1cd718d0e501e5 100644
--- a/apps/shared/src/index.ts
+++ b/apps/shared/src/index.ts
@@ -7,3 +7,4 @@ export * from './logging/logAxiosError.js';
 export * from './events/connectionEvents.js';
 export * from './events/didEvents.js';
 export * from './events/tenantEvents.js';
+export * from './events/schemaEvents.js';
diff --git a/apps/ssi-abstraction/src/agent/schemas/__tests__/schemas.controller.spec.ts b/apps/ssi-abstraction/src/agent/schemas/__tests__/schemas.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d761a213358d7eb77c68c21053480efa68193867
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/schemas/__tests__/schemas.controller.spec.ts
@@ -0,0 +1,74 @@
+import type { AnonCredsSchema } from '@aries-framework/anoncreds';
+
+import { Test } from '@nestjs/testing';
+
+import { mockConfigModule } from '../../../config/__tests__/mockConfig.js';
+import { AgentModule } from '../../agent.module.js';
+import { SchemasController } from '../schemas.controller.js';
+import { SchemasService } from '../schemas.service.js';
+
+describe('ConnectionsController', () => {
+  let schemasController: SchemasController;
+  let schemasService: SchemasService;
+
+  beforeEach(async () => {
+    const moduleRef = await Test.createTestingModule({
+      imports: [mockConfigModule(), AgentModule],
+      controllers: [SchemasController],
+      providers: [SchemasService],
+    }).compile();
+
+    schemasService = moduleRef.get(SchemasService);
+    schemasController = moduleRef.get(SchemasController);
+  });
+
+  describe('get all', () => {
+    it('should get all the registered schemas of the agent', async () => {
+      const result: Array<AnonCredsSchema> = [];
+      jest.spyOn(schemasService, 'getAll').mockResolvedValue(result);
+
+      const event = await schemasController.getAll({
+        tenantId: 'some-id',
+      });
+
+      expect(event.data).toStrictEqual(result);
+    });
+  });
+
+  describe('get by id', () => {
+    it('should get a schema by id', async () => {
+      const result: AnonCredsSchema | null = null;
+      jest.spyOn(schemasService, 'getById').mockResolvedValue(result);
+
+      const event = await schemasController.getById({
+        schemaId: 'id',
+        tenantId: 'some-id',
+      });
+
+      expect(event.data).toStrictEqual(result);
+    });
+  });
+
+  describe('register schema', () => {
+    it('should register a schema on a ledger', async () => {
+      const result: AnonCredsSchema = {
+        name: 'schema-name',
+        version: '1.0',
+        issuerId: 'did:indy:123',
+        attrNames: ['name', 'age'],
+      };
+
+      jest.spyOn(schemasService, 'register').mockResolvedValue(result);
+
+      const event = await schemasController.register({
+        tenantId: 'some-id',
+        version: '1.0',
+        name: 'schema-name',
+        issuerDid: 'did:indy:123',
+        attributeNames: ['name', 'age'],
+      });
+
+      expect(event.data).toStrictEqual(result);
+    });
+  });
+});
diff --git a/apps/ssi-abstraction/src/agent/schemas/schemas.controller.ts b/apps/ssi-abstraction/src/agent/schemas/schemas.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..180fd98b3be90fd778cef061ae2c415acbf54f7e
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/schemas/schemas.controller.ts
@@ -0,0 +1,47 @@
+import { Controller } from '@nestjs/common';
+import { MessagePattern } from '@nestjs/microservices';
+import {
+  EventAnonCredsSchemasGetAll,
+  EventAnonCredsSchemasGetAllInput,
+  EventAnonCredsSchemasGetById,
+  EventAnonCredsSchemasGetByIdInput,
+  EventAnonCredsSchemasRegister,
+  EventAnonCredsSchemasRegisterInput,
+} from '@ocm/shared';
+
+import { SchemasService } from './schemas.service.js';
+
+@Controller('schemas')
+export class SchemasController {
+  public constructor(private schemasService: SchemasService) {}
+
+  @MessagePattern(EventAnonCredsSchemasGetAll.token)
+  public async getAll(
+    options: EventAnonCredsSchemasGetAllInput,
+  ): Promise<EventAnonCredsSchemasGetAll> {
+    return new EventAnonCredsSchemasGetAll(
+      await this.schemasService.getAll(options),
+      options.tenantId,
+    );
+  }
+
+  @MessagePattern(EventAnonCredsSchemasGetById.token)
+  public async getById(
+    options: EventAnonCredsSchemasGetByIdInput,
+  ): Promise<EventAnonCredsSchemasGetById> {
+    return new EventAnonCredsSchemasGetById(
+      await this.schemasService.getById(options),
+      options.tenantId,
+    );
+  }
+
+  @MessagePattern(EventAnonCredsSchemasRegister.token)
+  public async register(
+    options: EventAnonCredsSchemasRegisterInput,
+  ): Promise<EventAnonCredsSchemasRegister> {
+    return new EventAnonCredsSchemasRegister(
+      await this.schemasService.register(options),
+      options.tenantId,
+    );
+  }
+}
diff --git a/apps/ssi-abstraction/src/agent/schemas/schemas.module.ts b/apps/ssi-abstraction/src/agent/schemas/schemas.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b3faaad8d8902a6551cd0e3cab57313e5e925076
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/schemas/schemas.module.ts
@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common';
+
+import { AgentModule } from '../agent.module.js';
+
+import { SchemasController } from './schemas.controller.js';
+import { SchemasService } from './schemas.service.js';
+
+@Module({
+  imports: [AgentModule],
+  providers: [SchemasService],
+  controllers: [SchemasController],
+})
+export class SchemasModule {}
diff --git a/apps/ssi-abstraction/src/agent/schemas/schemas.service.ts b/apps/ssi-abstraction/src/agent/schemas/schemas.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5cb4222db53d420bde4ea769d3dc30866dda4ce8
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/schemas/schemas.service.ts
@@ -0,0 +1,72 @@
+import type { AnonCredsSchema } from '@aries-framework/anoncreds';
+import type { IndyVdrRegisterSchemaOptions } from '@aries-framework/indy-vdr';
+import type {
+  EventAnonCredsSchemasGetAllInput,
+  EventAnonCredsSchemasGetByIdInput,
+  EventAnonCredsSchemasRegisterInput,
+} from '@ocm/shared';
+
+import { Injectable } from '@nestjs/common';
+
+import { WithTenantService } from '../withTenantService.js';
+
+@Injectable()
+export class SchemasService {
+  public withTenantService: WithTenantService;
+
+  public constructor(withTenantService: WithTenantService) {
+    this.withTenantService = withTenantService;
+  }
+
+  public async getAll({
+    tenantId,
+  }: EventAnonCredsSchemasGetAllInput): Promise<Array<AnonCredsSchema>> {
+    return this.withTenantService.invoke(tenantId, async (t) =>
+      (await t.modules.anoncreds.getCreatedSchemas({})).map((r) => r.schema),
+    );
+  }
+
+  public async getById({
+    tenantId,
+    schemaId,
+  }: EventAnonCredsSchemasGetByIdInput): Promise<AnonCredsSchema | null> {
+    return this.withTenantService.invoke(tenantId, async (t) => {
+      const { schema } = await t.modules.anoncreds.getSchema(schemaId);
+      return schema ?? null;
+    });
+  }
+
+  public async register({
+    tenantId,
+    name,
+    version,
+    issuerDid,
+    attributeNames,
+  }: EventAnonCredsSchemasRegisterInput): Promise<AnonCredsSchema> {
+    return this.withTenantService.invoke(tenantId, async (t) => {
+      const { schemaState } =
+        await t.modules.anoncreds.registerSchema<IndyVdrRegisterSchemaOptions>({
+          schema: {
+            version,
+            name,
+            issuerId: issuerDid,
+            attrNames: attributeNames,
+          },
+          options: {
+            endorserMode: 'external',
+            endorserDid: issuerDid,
+          },
+        });
+
+      if (schemaState.state !== 'finished') {
+        throw new Error(
+          `Error registering schema: ${
+            schemaState.state === 'failed' ? schemaState.reason : 'Not Finished'
+          }`,
+        );
+      }
+
+      return schemaState.schema;
+    });
+  }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 93ebdf1a54006abba74a71cf365a0f0661a32edf..0829affa99021eff4ffd7d9c6979028c77224ae7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -682,6 +682,9 @@ importers:
 
   apps/shared:
     dependencies:
+      '@aries-framework/anoncreds':
+        specifier: 0.4.2
+        version: 0.4.2(expo@49.0.18)(react-native@0.72.7)
       '@aries-framework/core':
         specifier: 0.4.2
         version: 0.4.2(expo@49.0.18)(react-native@0.72.7)