diff --git a/apps/shared/src/events/credentialDefinitionEvents.ts b/apps/shared/src/events/credentialDefinitionEvents.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cc5741abb187c43dd9351f1941caf10367e1f5de
--- /dev/null
+++ b/apps/shared/src/events/credentialDefinitionEvents.ts
@@ -0,0 +1,74 @@
+import type { BaseEventInput } from './baseEvents.js';
+import type { AnonCredsCredentialDefinition } from '@aries-framework/anoncreds';
+
+import { BaseEvent } from './baseEvents.js';
+
+export type CredentialDefinitionWithId = AnonCredsCredentialDefinition & {
+  credentialDefinitionId: string;
+};
+
+export type EventAnonCredsCredentialDefinitionsGetAllInput = BaseEventInput;
+export class EventAnonCredsCredentialDefinitionsGetAll extends BaseEvent<
+  Array<CredentialDefinitionWithId>
+> {
+  public static token = 'anoncreds.credentialDefinitions.getAll';
+
+  public get instance() {
+    return this.data;
+  }
+
+  public static fromEvent(e: EventAnonCredsCredentialDefinitionsGetAll) {
+    return new EventAnonCredsCredentialDefinitionsGetAll(
+      e.data,
+      e.tenantId,
+      e.id,
+      e.type,
+      e.timestamp,
+    );
+  }
+}
+
+export type EventAnonCredsCredentialDefinitionsGetByIdInput = BaseEventInput<{
+  credentialDefinitionId: string;
+}>;
+export class EventAnonCredsCredentialDefinitionsGetById extends BaseEvent<CredentialDefinitionWithId | null> {
+  public static token = 'anoncreds.credentialDefinitions.getById';
+
+  public get instance() {
+    return this.data;
+  }
+
+  public static fromEvent(e: EventAnonCredsCredentialDefinitionsGetById) {
+    return new EventAnonCredsCredentialDefinitionsGetById(
+      e.data,
+      e.tenantId,
+      e.id,
+      e.type,
+      e.timestamp,
+    );
+  }
+}
+
+export type EventAnonCredsCredentialDefinitionsRegisterInput = BaseEventInput<{
+  schemaId: string;
+  tag: string;
+  issuerDid: string;
+}>;
+
+export class EventAnonCredsCredentialDefinitionsRegister extends BaseEvent<CredentialDefinitionWithId> {
+  public static token = 'anoncreds.credentialDefinitions.register';
+
+  public get instance() {
+    return this.data;
+  }
+
+  public static fromEvent(e: EventAnonCredsCredentialDefinitionsRegister) {
+    return new EventAnonCredsCredentialDefinitionsRegister(
+      e.data,
+      e.tenantId,
+      e.id,
+      e.type,
+      e.timestamp,
+    );
+  }
+}
diff --git a/apps/shared/src/events/didEvents.ts b/apps/shared/src/events/didEvents.ts
index 60f5c6854f6343c4e0c0832901044ed155c0a650..e300545be74213c4172a34a216ecdd7eaf50c21a 100644
--- a/apps/shared/src/events/didEvents.ts
+++ b/apps/shared/src/events/didEvents.ts
@@ -4,44 +4,36 @@ import { DidDocument, JsonTransformer } from '@aries-framework/core';
 
 import { BaseEvent } from './baseEvents.js';
 
-/**
- *
- * @todo: this should be removed as it is a weird event that should not be needed
- *
- */
-export type EventDidsPublicDidInput = BaseEventInput;
-/**
- *
- * @todo: this should be removed as it is a weird event that should not be needed
- *
- */
-export class EventDidsPublicDid extends BaseEvent<DidDocument> {
-  public static token = 'dids.publicDid';
+export type EventDidsResolveInput = BaseEventInput<{ did: string }>;
+export class EventDidsResolve extends BaseEvent<DidDocument> {
+  public static token = 'dids.resolve';
 
   public get instance() {
     return JsonTransformer.fromJSON(this.data, DidDocument);
   }
 
-  public static fromEvent(e: EventDidsPublicDid) {
-    return new EventDidsPublicDid(
-      e.data,
-      e.tenantId,
-      e.id,
-      e.type,
-      e.timestamp,
-    );
+  public static fromEvent(e: EventDidsResolve) {
+    return new EventDidsResolve(e.data, e.tenantId, e.id, e.type, e.timestamp);
   }
 }
 
-export type EventDidsResolveInput = BaseEventInput<{ did: string }>;
-export class EventDidsResolve extends BaseEvent<DidDocument> {
-  public static token = 'dids.resolve';
+export type EventDidsRegisterIndyFromSeedInput = BaseEventInput<{
+  seed: string;
+}>;
+export class EventDidsRegisterIndyFromSeed extends BaseEvent<Array<string>> {
+  public static token = 'dids.register.indy.fromSeed';
 
   public get instance() {
-    return JsonTransformer.fromJSON(this.data, DidDocument);
+    return this.data;
   }
 
-  public static fromEvent(e: EventDidsResolve) {
-    return new EventDidsResolve(e.data, e.tenantId, e.id, e.type, e.timestamp);
+  public static fromEvent(e: EventDidsRegisterIndyFromSeed) {
+    return new EventDidsRegisterIndyFromSeed(
+      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 7f8ae5efe78d58642c60d2b62c1cd718d0e501e5..8fa24d341f31ec9e1d424ae5b860438608c4d807 100644
--- a/apps/shared/src/index.ts
+++ b/apps/shared/src/index.ts
@@ -8,3 +8,4 @@ export * from './events/connectionEvents.js';
 export * from './events/didEvents.js';
 export * from './events/tenantEvents.js';
 export * from './events/schemaEvents.js';
+export * from './events/credentialDefinitionEvents.js';
diff --git a/apps/ssi-abstraction/src/agent/agent.service.ts b/apps/ssi-abstraction/src/agent/agent.service.ts
index 76b0bad51df5ce8abd267e2f08d56bf83f86de1c..9df1a7034eb1dec7eb9352566ea01366f26e7786 100644
--- a/apps/ssi-abstraction/src/agent/agent.service.ts
+++ b/apps/ssi-abstraction/src/agent/agent.service.ts
@@ -16,11 +16,9 @@ import {
   JwkDidResolver,
   KeyDidRegistrar,
   KeyDidResolver,
-  KeyType,
   LogLevel,
   PeerDidRegistrar,
   PeerDidResolver,
-  TypedArrayEncoder,
   WebDidResolver,
 } from '@aries-framework/core';
 import {
@@ -40,7 +38,6 @@ import { logger } from '@ocm/shared';
 
 import { LEDGERS } from '../config/ledger.js';
 
-import { registerPublicDids } from './ledger/register.js';
 import { AgentLogger } from './logger.js';
 
 export type AppAgent = Agent<AgentService['modules']>;
@@ -127,7 +124,7 @@ export class AgentService implements OnApplicationShutdown {
     };
   }
 
-  public get ledgers() {
+  private get ledgers() {
     const ledgerIds = this.configService.get('agent.ledgerIds');
 
     if (!ledgerIds || ledgerIds.length < 1 || ledgerIds[0] === '') {
@@ -153,41 +150,8 @@ export class AgentService implements OnApplicationShutdown {
     });
   }
 
-  private async registerPublicDid() {
-    const { publicDidSeed, ledgerIds } = this.configService.get('agent');
-
-    if (!publicDidSeed) {
-      logger.info('No public did seed provided, skipping registration');
-      return;
-    }
-
-    if (!ledgerIds || ledgerIds.length < 1 || ledgerIds[0] === '') {
-      return;
-    }
-
-    const registeredPublicDidResponses = await registerPublicDids({
-      alias: this.config.label,
-      ledgerIds,
-      seed: publicDidSeed,
-    });
-
-    for (const publicDidResponse of registeredPublicDidResponses) {
-      await this.agent.dids.import({
-        overwrite: true,
-        did: publicDidResponse.did,
-        privateKeys: [
-          {
-            keyType: KeyType.Ed25519,
-            privateKey: TypedArrayEncoder.fromString(publicDidSeed),
-          },
-        ],
-      });
-    }
-  }
-
   public async onModuleInit() {
     await this.agent.initialize();
-    await this.registerPublicDid();
     logger.info('Agent initialized');
   }
 
diff --git a/apps/ssi-abstraction/src/agent/credentialDefinitions/__tests__/credentialDefinitions.controller.spec.ts b/apps/ssi-abstraction/src/agent/credentialDefinitions/__tests__/credentialDefinitions.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b90afbccd45bbd629db0a107460ccaef2ce33c91
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/credentialDefinitions/__tests__/credentialDefinitions.controller.spec.ts
@@ -0,0 +1,84 @@
+import type { AnonCredsCredentialDefinition } from '@aries-framework/anoncreds';
+
+import { Test } from '@nestjs/testing';
+
+import { mockConfigModule } from '../../../config/__tests__/mockConfig.js';
+import { AgentModule } from '../../agent.module.js';
+import { CredentialDefinitionsController } from '../credentialDefinitions.controller.js';
+import { CredentialDefinitionsService } from '../credentialDefinitions.service.js';
+
+describe('CredentialDefinitionsController', () => {
+  let credentialDefinitionsController: CredentialDefinitionsController;
+  let credentialDefinitionsService: CredentialDefinitionsService;
+
+  beforeEach(async () => {
+    const moduleRef = await Test.createTestingModule({
+      imports: [mockConfigModule(), AgentModule],
+      controllers: [CredentialDefinitionsController],
+      providers: [CredentialDefinitionsService],
+    }).compile();
+
+    credentialDefinitionsService = moduleRef.get(CredentialDefinitionsService);
+    credentialDefinitionsController = moduleRef.get(
+      CredentialDefinitionsController,
+    );
+  });
+
+  describe('get all', () => {
+    it('should get all the registered credentialDefinitions of the agent', async () => {
+      const result: Array<AnonCredsCredentialDefinition> = [];
+      jest
+        .spyOn(credentialDefinitionsService, 'getAll')
+        .mockResolvedValue(result);
+
+      const event = await credentialDefinitionsController.getAll({
+        tenantId: 'some-id',
+      });
+
+      expect(event.data).toStrictEqual(result);
+    });
+  });
+
+  describe('get by id', () => {
+    it('should get a credentialDefinition by id', async () => {
+      const result: AnonCredsCredentialDefinition | null = null;
+      jest
+        .spyOn(credentialDefinitionsService, 'getById')
+        .mockResolvedValue(result);
+
+      const event = await credentialDefinitionsController.getById({
+        credentialDefinitionId: 'id',
+        tenantId: 'some-id',
+      });
+
+      expect(event.data).toStrictEqual(result);
+    });
+  });
+
+  describe('register credentialDefinition', () => {
+    it('should register a credentialDefinition on a ledger', async () => {
+      const result: AnonCredsCredentialDefinition = {
+        tag: 'some-tag',
+        type: 'CL',
+        issuerId: 'did:indy:issuer',
+        schemaId: 'schemaid:123:default',
+        value: {
+          primary: {},
+        },
+      };
+
+      jest
+        .spyOn(credentialDefinitionsService, 'register')
+        .mockResolvedValue(result);
+
+      const event = await credentialDefinitionsController.register({
+        tenantId: 'some-tenant-id',
+        tag: 'some-tag',
+        issuerDid: 'did:indy:issuer',
+        schemaId: 'schemaid:123:default',
+      });
+
+      expect(event.data).toStrictEqual(result);
+    });
+  });
+});
diff --git a/apps/ssi-abstraction/src/agent/credentialDefinitions/credentialDefinitions.controller.ts b/apps/ssi-abstraction/src/agent/credentialDefinitions/credentialDefinitions.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2e75b94722c15e629dd8549c60f1d7f08f0a723c
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/credentialDefinitions/credentialDefinitions.controller.ts
@@ -0,0 +1,49 @@
+import { Controller } from '@nestjs/common';
+import { MessagePattern } from '@nestjs/microservices';
+import {
+  EventAnonCredsCredentialDefinitionsGetAll,
+  EventAnonCredsCredentialDefinitionsGetAllInput,
+  EventAnonCredsCredentialDefinitionsGetById,
+  EventAnonCredsCredentialDefinitionsGetByIdInput,
+  EventAnonCredsCredentialDefinitionsRegister,
+  EventAnonCredsCredentialDefinitionsRegisterInput,
+} from '@ocm/shared';
+
+import { CredentialDefinitionsService } from './credentialDefinitions.service.js';
+
+@Controller('credentialDefinitions')
+export class CredentialDefinitionsController {
+  public constructor(
+    private credentialDefinitionsService: CredentialDefinitionsService,
+  ) {}
+
+  @MessagePattern(EventAnonCredsCredentialDefinitionsGetAll.token)
+  public async getAll(
+    options: EventAnonCredsCredentialDefinitionsGetAllInput,
+  ): Promise<EventAnonCredsCredentialDefinitionsGetAll> {
+    return new EventAnonCredsCredentialDefinitionsGetAll(
+      await this.credentialDefinitionsService.getAll(options),
+      options.tenantId,
+    );
+  }
+
+  @MessagePattern(EventAnonCredsCredentialDefinitionsGetById.token)
+  public async getById(
+    options: EventAnonCredsCredentialDefinitionsGetByIdInput,
+  ): Promise<EventAnonCredsCredentialDefinitionsGetById> {
+    return new EventAnonCredsCredentialDefinitionsGetById(
+      await this.credentialDefinitionsService.getById(options),
+      options.tenantId,
+    );
+  }
+
+  @MessagePattern(EventAnonCredsCredentialDefinitionsRegister.token)
+  public async register(
+    options: EventAnonCredsCredentialDefinitionsRegisterInput,
+  ): Promise<EventAnonCredsCredentialDefinitionsRegister> {
+    return new EventAnonCredsCredentialDefinitionsRegister(
+      await this.credentialDefinitionsService.register(options),
+      options.tenantId,
+    );
+  }
+}
diff --git a/apps/ssi-abstraction/src/agent/credentialDefinitions/credentialDefinitions.module.ts b/apps/ssi-abstraction/src/agent/credentialDefinitions/credentialDefinitions.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0bff1d8222ca053214c4051ce051c6b772b14ef4
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/credentialDefinitions/credentialDefinitions.module.ts
@@ -0,0 +1,13 @@
+import { Module } from '@nestjs/common';
+
+import { AgentModule } from '../agent.module.js';
+
+import { CredentialDefinitionsController } from './credentialDefinitions.controller.js';
+import { CredentialDefinitionsService } from './credentialDefinitions.service.js';
+
+@Module({
+  imports: [AgentModule],
+  providers: [CredentialDefinitionsService],
+  controllers: [CredentialDefinitionsController],
+})
+export class CredentialDefinitionsModule {}
diff --git a/apps/ssi-abstraction/src/agent/credentialDefinitions/credentialDefinitions.service.ts b/apps/ssi-abstraction/src/agent/credentialDefinitions/credentialDefinitions.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..32b2b619a18f502f25228540eaaf1620990b9ff3
--- /dev/null
+++ b/apps/ssi-abstraction/src/agent/credentialDefinitions/credentialDefinitions.service.ts
@@ -0,0 +1,88 @@
+import type { AnonCredsCredentialDefinition } from '@aries-framework/anoncreds';
+import type { IndyVdrRegisterCredentialDefinitionOptions } from '@aries-framework/indy-vdr';
+import type {
+  EventAnonCredsCredentialDefinitionsGetAllInput,
+  EventAnonCredsCredentialDefinitionsGetByIdInput,
+  EventAnonCredsCredentialDefinitionsRegisterInput,
+} from '@ocm/shared';
+
+import { Injectable } from '@nestjs/common';
+
+import { WithTenantService } from '../withTenantService.js';
+
+@Injectable()
+export class CredentialDefinitionsService {
+  public withTenantService: WithTenantService;
+
+  public constructor(withTenantService: WithTenantService) {
+    this.withTenantService = withTenantService;
+  }
+
+  public async getAll({
+    tenantId,
+  }: EventAnonCredsCredentialDefinitionsGetAllInput): Promise<
+    Array<AnonCredsCredentialDefinition>
+  > {
+    return this.withTenantService.invoke(tenantId, async (t) =>
+      (await t.modules.anoncreds.getCreatedCredentialDefinitions({})).map(
+        (r) => r.credentialDefinition,
+      ),
+    );
+  }
+
+  public async getById({
+    tenantId,
+    credentialDefinitionId,
+  }: EventAnonCredsCredentialDefinitionsGetByIdInput): Promise<AnonCredsCredentialDefinition | null> {
+    return this.withTenantService.invoke(tenantId, async (t) => {
+      const { credentialDefinition } =
+        await t.modules.anoncreds.getCredentialDefinition(
+          credentialDefinitionId,
+        );
+      return credentialDefinition ?? null;
+    });
+  }
+
+  public async register({
+    tenantId,
+    schemaId,
+    issuerDid,
+    tag,
+  }: EventAnonCredsCredentialDefinitionsRegisterInput): Promise<
+    AnonCredsCredentialDefinition & { credentialDefinitionId: string }
+  > {
+    return this.withTenantService.invoke(tenantId, async (t) => {
+      const { credentialDefinitionState } =
+        await t.modules.anoncreds.registerCredentialDefinition<IndyVdrRegisterCredentialDefinitionOptions>(
+          {
+            credentialDefinition: {
+              issuerId: issuerDid,
+              type: 'CL',
+              schemaId,
+              tag,
+            },
+            options: {
+              endorserMode: 'internal',
+              endorserDid: issuerDid,
+            },
+          },
+        );
+
+      if (credentialDefinitionState.state !== 'finished') {
+        throw new Error(
+          `Error registering credentialDefinition: ${
+            credentialDefinitionState.state === 'failed'
+              ? credentialDefinitionState.reason
+              : 'Not Finished'
+          }`,
+        );
+      }
+
+      return {
+        credentialDefinitionId:
+          credentialDefinitionState.credentialDefinitionId,
+        ...credentialDefinitionState.credentialDefinition,
+      };
+    });
+  }
+}
diff --git a/apps/ssi-abstraction/src/agent/dids/__tests__/dids.controller.spec.ts b/apps/ssi-abstraction/src/agent/dids/__tests__/dids.controller.spec.ts
index eb513d9d30947af2b8a02112ec885911fa424805..7bdb15e4d35aadc60eca85b1b1c056b094cf41d8 100644
--- a/apps/ssi-abstraction/src/agent/dids/__tests__/dids.controller.spec.ts
+++ b/apps/ssi-abstraction/src/agent/dids/__tests__/dids.controller.spec.ts
@@ -34,4 +34,20 @@ describe('DidsController', () => {
       expect(event.data).toStrictEqual(result);
     });
   });
+
+  describe('register indy did from seed', () => {
+    it('should register an indy did from seed', async () => {
+      const result = ['did:indy:bcovrin:test:mock'];
+      jest
+        .spyOn(didsService, 'registerDidIndyFromSeed')
+        .mockResolvedValue(result);
+
+      const event = await didsController.registerFromSeed({
+        seed: 'random-secure-seed',
+        tenantId: 'some-id',
+      });
+
+      expect(event.data).toStrictEqual(result);
+    });
+  });
 });
diff --git a/apps/ssi-abstraction/src/agent/dids/dids.controller.ts b/apps/ssi-abstraction/src/agent/dids/dids.controller.ts
index c869d0db7f116abed9397c663753c5d2a52a3ad0..688d68862c41553b2e9d83fc60d16aad11a741a1 100644
--- a/apps/ssi-abstraction/src/agent/dids/dids.controller.ts
+++ b/apps/ssi-abstraction/src/agent/dids/dids.controller.ts
@@ -1,8 +1,8 @@
 import { Controller } from '@nestjs/common';
 import { MessagePattern } from '@nestjs/microservices';
 import {
-  EventDidsPublicDid,
-  EventDidsPublicDidInput,
+  EventDidsRegisterIndyFromSeed,
+  EventDidsRegisterIndyFromSeedInput,
   EventDidsResolve,
   EventDidsResolveInput,
 } from '@ocm/shared';
@@ -13,10 +13,10 @@ import { DidsService } from './dids.service.js';
 export class DidsController {
   public constructor(private didsService: DidsService) {}
 
-  @MessagePattern(EventDidsPublicDid.token)
-  public async publicDid(options: EventDidsPublicDidInput) {
-    return new EventDidsPublicDid(
-      await this.didsService.getPublicDid(options),
+  @MessagePattern(EventDidsRegisterIndyFromSeed.token)
+  public async registerFromSeed(options: EventDidsRegisterIndyFromSeedInput) {
+    return new EventDidsRegisterIndyFromSeed(
+      await this.didsService.registerDidIndyFromSeed(options),
       options.tenantId,
     );
   }
diff --git a/apps/ssi-abstraction/src/agent/dids/dids.module.ts b/apps/ssi-abstraction/src/agent/dids/dids.module.ts
index 18668fe548266d2334051dc2f806eecb824060f7..d0a3f1559bf3705567da090720d4164fb3910a86 100644
--- a/apps/ssi-abstraction/src/agent/dids/dids.module.ts
+++ b/apps/ssi-abstraction/src/agent/dids/dids.module.ts
@@ -1,4 +1,5 @@
 import { Module } from '@nestjs/common';
+import { ConfigModule } from '@nestjs/config';
 
 import { AgentModule } from '../agent.module.js';
 
@@ -6,7 +7,7 @@ import { DidsController } from './dids.controller.js';
 import { DidsService } from './dids.service.js';
 
 @Module({
-  imports: [AgentModule ],
+  imports: [AgentModule, ConfigModule],
   providers: [DidsService],
   controllers: [DidsController],
 })
diff --git a/apps/ssi-abstraction/src/agent/dids/dids.service.ts b/apps/ssi-abstraction/src/agent/dids/dids.service.ts
index 2adf4c401c4c9d93e10e5f79ff1185bbd6cdf5a8..cf7807236f57ca373f86fe84a05b95dba260dda5 100644
--- a/apps/ssi-abstraction/src/agent/dids/dids.service.ts
+++ b/apps/ssi-abstraction/src/agent/dids/dids.service.ts
@@ -1,18 +1,23 @@
-import type {
-  EventDidsPublicDidInput,
-  EventDidsResolveInput,
-} from '@ocm/shared';
+import type { EventDidsResolveInput } from '@ocm/shared';
 
+import { KeyType, TypedArrayEncoder } from '@aries-framework/core';
 import { Injectable } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
 
+import { registerPublicDids } from '../ledger/register.js';
 import { WithTenantService } from '../withTenantService.js';
 
 @Injectable()
 export class DidsService {
-  public withTenantService: WithTenantService;
+  private withTenantService: WithTenantService;
+  private configService: ConfigService;
 
-  public constructor(withTenantService: WithTenantService) {
+  public constructor(
+    withTenantService: WithTenantService,
+    configService: ConfigService,
+  ) {
     this.withTenantService = withTenantService;
+    this.configService = configService;
   }
 
   public async resolve({ did, tenantId }: EventDidsResolveInput) {
@@ -34,26 +39,35 @@ export class DidsService {
     });
   }
 
-  public async getPublicDid({ tenantId }: EventDidsPublicDidInput) {
-    return this.withTenantService.invoke(tenantId, async (t) => {
-      const dids = await t.dids.getCreatedDids({ method: 'indy' });
-      if (dids.length === 0) {
-        throw new Error('No registered public DIDs');
-      }
+  public async registerDidIndyFromSeed({
+    tenantId,
+    seed,
+  }: {
+    tenantId: string;
+    seed: string;
+  }): Promise<Array<string>> {
+    const ledgerIds = this.configService.get('agent.ledgerIds');
 
-      if (dids.length > 1) {
-        throw new Error('Multiple public DIDs found');
-      }
+    const registeredPublicDidResponses = await registerPublicDids({
+      ledgerIds,
+      seed,
+    });
 
-      const didRecord = dids[0];
+    for (const publicDidResponse of registeredPublicDidResponses) {
+      await this.withTenantService.invoke(tenantId, (t) =>
+        t.dids.import({
+          overwrite: true,
+          did: publicDidResponse.did,
+          privateKeys: [
+            {
+              keyType: KeyType.Ed25519,
+              privateKey: TypedArrayEncoder.fromString(seed),
+            },
+          ],
+        }),
+      );
+    }
 
-      if (!didRecord.didDocument) {
-        throw new Error(
-          'A public DID was found, but did not include a DID Document',
-        );
-      }
-
-      return didRecord.didDocument;
-    });
+    return registeredPublicDidResponses.map((r) => r.did);
   }
 }
diff --git a/apps/ssi-abstraction/src/agent/ledger/register.ts b/apps/ssi-abstraction/src/agent/ledger/register.ts
index 1396428d29e68d21ab7df21390bdd070fdb1336c..8600a454b48e609e2798aba6482cbd719720c419 100644
--- a/apps/ssi-abstraction/src/agent/ledger/register.ts
+++ b/apps/ssi-abstraction/src/agent/ledger/register.ts
@@ -6,14 +6,12 @@ import axios from 'axios';
 import { LEDGERS } from '../../config/ledger.js';
 
 type RegisterPublicDidOptions = {
-  alias: string;
   ledgerIds: Array<LedgerIds>;
   seed: string;
 };
 
 type LedgerRegistrationBody = {
   role?: 'ENDORSER';
-  alias?: string;
   seed: string;
 };
 
@@ -25,7 +23,6 @@ type RegisterPublicDidResponse = {
 
 export const registerPublicDids = async ({
   ledgerIds,
-  alias,
   seed,
 }: RegisterPublicDidOptions): Promise<Array<RegisterPublicDidResponse>> => {
   const responses: Array<RegisterPublicDidResponse> = [];
@@ -36,7 +33,6 @@ export const registerPublicDids = async ({
 
       const body: LedgerRegistrationBody = {
         role: 'ENDORSER',
-        alias,
         seed,
       };
 
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
index d761a213358d7eb77c68c21053480efa68193867..95fb19a52438c757538e3ee4e479d87b7ceecd74 100644
--- a/apps/ssi-abstraction/src/agent/schemas/__tests__/schemas.controller.spec.ts
+++ b/apps/ssi-abstraction/src/agent/schemas/__tests__/schemas.controller.spec.ts
@@ -7,7 +7,7 @@ import { AgentModule } from '../../agent.module.js';
 import { SchemasController } from '../schemas.controller.js';
 import { SchemasService } from '../schemas.service.js';
 
-describe('ConnectionsController', () => {
+describe('SchemassController', () => {
   let schemasController: SchemasController;
   let schemasService: SchemasService;
 
diff --git a/apps/ssi-abstraction/src/agent/schemas/schemas.service.ts b/apps/ssi-abstraction/src/agent/schemas/schemas.service.ts
index 5cb4222db53d420bde4ea769d3dc30866dda4ce8..9f07491a7d3d927a8e8d701a54714783d187c3a3 100644
--- a/apps/ssi-abstraction/src/agent/schemas/schemas.service.ts
+++ b/apps/ssi-abstraction/src/agent/schemas/schemas.service.ts
@@ -42,7 +42,9 @@ export class SchemasService {
     version,
     issuerDid,
     attributeNames,
-  }: EventAnonCredsSchemasRegisterInput): Promise<AnonCredsSchema> {
+  }: EventAnonCredsSchemasRegisterInput): Promise<
+    AnonCredsSchema & { schemaId: string }
+  > {
     return this.withTenantService.invoke(tenantId, async (t) => {
       const { schemaState } =
         await t.modules.anoncreds.registerSchema<IndyVdrRegisterSchemaOptions>({
@@ -53,7 +55,7 @@ export class SchemasService {
             attrNames: attributeNames,
           },
           options: {
-            endorserMode: 'external',
+            endorserMode: 'internal',
             endorserDid: issuerDid,
           },
         });
@@ -66,7 +68,7 @@ export class SchemasService {
         );
       }
 
-      return schemaState.schema;
+      return { schemaId: schemaState.schemaId, ...schemaState.schema };
     });
   }
 }
diff --git a/apps/ssi-abstraction/src/app.module.ts b/apps/ssi-abstraction/src/app.module.ts
index 44a25ae0403ab4bd45ec9a8663f09231f491ff2c..876afc5e240475e99fb9ca9649fe4af9087b52dd 100644
--- a/apps/ssi-abstraction/src/app.module.ts
+++ b/apps/ssi-abstraction/src/app.module.ts
@@ -6,6 +6,8 @@ import { HealthController } from '@ocm/shared';
 
 import { AgentModule } from './agent/agent.module.js';
 import { ConnectionsModule } from './agent/connections/connections.module.js';
+import { CredentialDefinitionsModule } from './agent/credentialDefinitions/credentialDefinitions.module.js';
+import { SchemasModule } from './agent/schemas/schemas.module.js';
 import { TenantsModule } from './agent/tenants/tenants.module.js';
 import { config } from './config/config.js';
 import { validationSchema } from './config/validation.js';
@@ -20,7 +22,10 @@ import { validationSchema } from './config/validation.js';
     }),
     AgentModule,
     ConnectionsModule,
+    SchemasModule,
+    CredentialDefinitionsModule,
     DidsModule,
+    SchemasModule,
     TenantsModule,
   ],
   controllers: [HealthController],
diff --git a/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts b/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts
index 8a5eb2ed2efb4ddbcbea7f3d4975a60a29a83af7..202a2b05ede19e8d39aa47dc5790dcb89157c001 100644
--- a/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts
+++ b/apps/ssi-abstraction/src/config/__tests__/mockConfig.ts
@@ -5,7 +5,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
 
 import { validationSchema } from '../validation.js';
 
-const mockConfig = (port: number = 3001): AppConfig => ({
+const mockConfig = (port: number = 3001, withLedger = false): AppConfig => ({
   agentHost: '',
   port: 3000,
   jwtSecret: '',
@@ -16,19 +16,19 @@ const mockConfig = (port: number = 3001): AppConfig => ({
     name: 'my-test-agent',
     walletId: utils.uuid(),
     walletKey: 'some-key',
-    ledgerIds: [],
+    ledgerIds: withLedger ? ['BCOVRIN_TEST'] : [],
     host: 'http://localhost',
     inboundPort: port,
     path: '',
-    publicDidSeed: '',
+    publicDidSeed: withLedger ? '12312367897123300000000000000000' : '',
     autoAcceptConnection: true,
     autoAcceptCredential: AutoAcceptCredential.ContentApproved,
   },
 });
 
-export const mockConfigModule = (port: number = 3000) =>
+export const mockConfigModule = (port: number = 3000, withLedger = false) =>
   ConfigModule.forRoot({
-    load: [() => mockConfig(port)],
+    load: [() => mockConfig(port, withLedger)],
     validationSchema,
   });
 
diff --git a/apps/ssi-abstraction/test/credentialDefinitions.e2e-spec.ts b/apps/ssi-abstraction/test/credentialDefinitions.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2f87be6acd96465e540ba843a504c689fa6a10af
--- /dev/null
+++ b/apps/ssi-abstraction/test/credentialDefinitions.e2e-spec.ts
@@ -0,0 +1,137 @@
+import type { INestApplication } from '@nestjs/common';
+import type { ClientProxy } from '@nestjs/microservices';
+import type {
+  EventAnonCredsCredentialDefinitionsGetAllInput,
+  EventAnonCredsCredentialDefinitionsGetByIdInput,
+  EventAnonCredsCredentialDefinitionsRegisterInput,
+} from '@ocm/shared';
+
+import { ClientsModule, Transport } from '@nestjs/microservices';
+import { Test } from '@nestjs/testing';
+import {
+  EventAnonCredsCredentialDefinitionsGetById,
+  EventAnonCredsCredentialDefinitionsGetAll,
+  EventAnonCredsCredentialDefinitionsRegister,
+} from '@ocm/shared';
+import { firstValueFrom } from 'rxjs';
+
+import { AgentModule } from '../src/agent/agent.module.js';
+import { CredentialDefinitionsModule } from '../src/agent/credentialDefinitions/credentialDefinitions.module.js';
+import { DidsModule } from '../src/agent/dids/dids.module.js';
+import { DidsService } from '../src/agent/dids/dids.service.js';
+import { SchemasModule } from '../src/agent/schemas/schemas.module.js';
+import { SchemasService } from '../src/agent/schemas/schemas.service.js';
+import { TenantsModule } from '../src/agent/tenants/tenants.module.js';
+import { TenantsService } from '../src/agent/tenants/tenants.service.js';
+import { mockConfigModule } from '../src/config/__tests__/mockConfig.js';
+
+describe('CredentialDefinitions', () => {
+  const TOKEN = 'CREDENTIAL_DEFINITIONS_CLIENT_SERVICE';
+  let app: INestApplication;
+  let client: ClientProxy;
+  let tenantId: string;
+
+  let issuerDid: string;
+  let schemaId: string;
+
+  beforeAll(async () => {
+    const moduleRef = await Test.createTestingModule({
+      imports: [
+        mockConfigModule(3004, true),
+        AgentModule,
+        SchemasModule,
+        CredentialDefinitionsModule,
+        TenantsModule,
+        DidsModule,
+        ClientsModule.register([{ name: TOKEN, transport: Transport.NATS }]),
+      ],
+    }).compile();
+
+    app = moduleRef.createNestApplication();
+
+    app.connectMicroservice({ transport: Transport.NATS });
+
+    await app.startAllMicroservices();
+    await app.init();
+
+    client = app.get(TOKEN);
+    await client.connect();
+
+    const tenantsService = app.get(TenantsService);
+    const { id } = await tenantsService.create(TOKEN);
+    tenantId = id;
+
+    const didsService = app.get(DidsService);
+    const [did] = await didsService.registerDidIndyFromSeed({
+      tenantId,
+      seed: '12312367897123300000000000000000',
+    });
+    issuerDid = did;
+
+    const schemaService = app.get(SchemasService);
+    const { schemaId: sid } = await schemaService.register({
+      issuerDid,
+      tenantId,
+      name: 'test-schema-name',
+      version: `1.${new Date().getTime()}`,
+      attributeNames: ['none'],
+    });
+    schemaId = sid;
+  });
+
+  afterAll(async () => {
+    await app.close();
+    client.close();
+  });
+
+  it(EventAnonCredsCredentialDefinitionsGetAll.token, async () => {
+    const response$ = client.send<
+      EventAnonCredsCredentialDefinitionsGetAll,
+      EventAnonCredsCredentialDefinitionsGetAllInput
+    >(EventAnonCredsCredentialDefinitionsGetAll.token, { tenantId });
+    const response = await firstValueFrom(response$);
+    const eventInstance =
+      EventAnonCredsCredentialDefinitionsGetAll.fromEvent(response);
+
+    expect(eventInstance.instance).toEqual(expect.arrayContaining([]));
+  });
+
+  it(EventAnonCredsCredentialDefinitionsGetById.token, async () => {
+    const response$ = client.send<
+      EventAnonCredsCredentialDefinitionsGetById,
+      EventAnonCredsCredentialDefinitionsGetByIdInput
+    >(EventAnonCredsCredentialDefinitionsGetById.token, {
+      tenantId,
+      credentialDefinitionId: 'some-id',
+    });
+    const response = await firstValueFrom(response$);
+    const eventInstance =
+      EventAnonCredsCredentialDefinitionsGetById.fromEvent(response);
+
+    expect(eventInstance.instance).toEqual(null);
+  });
+
+  it(EventAnonCredsCredentialDefinitionsRegister.token, async () => {
+    const tag = `tag:${new Date().getTime()}`;
+    const response$ = client.send<
+      EventAnonCredsCredentialDefinitionsRegister,
+      EventAnonCredsCredentialDefinitionsRegisterInput
+    >(EventAnonCredsCredentialDefinitionsRegister.token, {
+      tenantId,
+      schemaId,
+      issuerDid,
+      tag,
+    });
+
+    const response = await firstValueFrom(response$);
+    const eventInstance =
+      EventAnonCredsCredentialDefinitionsRegister.fromEvent(response);
+
+    expect(eventInstance.instance).toMatchObject({
+      schemaId,
+      tag,
+      issuerId: issuerDid,
+      type: 'CL',
+    });
+  });
+});
diff --git a/apps/ssi-abstraction/test/dids.e2e-spec.ts b/apps/ssi-abstraction/test/dids.e2e-spec.ts
index 8f3bafe813ab0b21b5c7ce63ab71df7e7da923ca..52c8a7b66caaba8d59e7d2c64d98ce725e977532 100644
--- a/apps/ssi-abstraction/test/dids.e2e-spec.ts
+++ b/apps/ssi-abstraction/test/dids.e2e-spec.ts
@@ -1,37 +1,21 @@
 import type { INestApplication } from '@nestjs/common';
 import type { ClientProxy } from '@nestjs/microservices';
 import type {
+  EventDidsRegisterIndyFromSeedInput,
   EventDidsResolveInput,
-  EventDidsPublicDidInput,
 } from '@ocm/shared';
 
-import { DidDocument } from '@aries-framework/core';
 import { ClientsModule, Transport } from '@nestjs/microservices';
 import { Test } from '@nestjs/testing';
-import { EventDidsResolve, EventDidsPublicDid } from '@ocm/shared';
+import { EventDidsRegisterIndyFromSeed, EventDidsResolve } from '@ocm/shared';
 import { firstValueFrom } from 'rxjs';
 
 import { AgentModule } from '../src/agent/agent.module.js';
 import { DidsModule } from '../src/agent/dids/dids.module.js';
-import { DidsService } from '../src/agent/dids/dids.service.js';
 import { TenantsModule } from '../src/agent/tenants/tenants.module.js';
 import { TenantsService } from '../src/agent/tenants/tenants.service.js';
 import { mockConfigModule } from '../src/config/__tests__/mockConfig.js';
 
-const mockDidDocument = {
-  context: ['https://w3id.org/did/v1'],
-  id: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM',
-  verificationMethod: [
-    {
-      id: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM#verkey',
-      type: 'Ed25519VerificationKey2018',
-      controller: 'did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM',
-      publicKeyBase58: '4SySYXQUtuK26zfC7RpQpWYMThfbXphUf8LWyXXmxyTX',
-    },
-  ],
-  authentication: ['did:indy:bcovrin:test:7KuDTpQh3GJ7Gp6kErpWvM#verkey'],
-};
-
 describe('Dids', () => {
   const TOKEN = 'DIDS_CLIENT_SERVICE';
   let app: INestApplication;
@@ -39,12 +23,9 @@ describe('Dids', () => {
   let tenantId: string;
 
   beforeAll(async () => {
-    jest
-      .spyOn(DidsService.prototype, 'getPublicDid')
-      .mockResolvedValue(new DidDocument(mockDidDocument));
     const moduleRef = await Test.createTestingModule({
       imports: [
-        mockConfigModule(3005),
+        mockConfigModule(3005, true),
         AgentModule,
         DidsModule,
         TenantsModule,
@@ -72,16 +53,21 @@ describe('Dids', () => {
     client.close();
   });
 
-  it(EventDidsPublicDid.token, async () => {
-    const response$ = client.send<EventDidsPublicDid, EventDidsPublicDidInput>(
-      EventDidsPublicDid.token,
-      { tenantId },
-    );
+  it(EventDidsRegisterIndyFromSeed.token, async () => {
+    const response$ = client.send<
+      EventDidsRegisterIndyFromSeed,
+      EventDidsRegisterIndyFromSeedInput
+    >(EventDidsRegisterIndyFromSeed.token, {
+      seed: '12312367897123300000000000000000',
+      tenantId,
+    });
 
     const response = await firstValueFrom(response$);
-    const eventInstance = EventDidsPublicDid.fromEvent(response);
+    const eventInstance = EventDidsRegisterIndyFromSeed.fromEvent(response);
 
-    expect(eventInstance.instance).toMatchObject(mockDidDocument);
+    expect(eventInstance.instance).toMatchObject(
+      expect.arrayContaining(['did:indy:bcovrin:test:9MMeff63VnCpogD2FWfKnJ']),
+    );
   });
 
   it(EventDidsResolve.token, async () => {
diff --git a/apps/ssi-abstraction/test/schemas.e2e-spec.ts b/apps/ssi-abstraction/test/schemas.e2e-spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..018b1d3b63b436f0fc7c320f18e7259a0ef8a5ca
--- /dev/null
+++ b/apps/ssi-abstraction/test/schemas.e2e-spec.ts
@@ -0,0 +1,119 @@
+import type { INestApplication } from '@nestjs/common';
+import type { ClientProxy } from '@nestjs/microservices';
+import type {
+  EventAnonCredsSchemasGetAllInput,
+  EventAnonCredsSchemasGetByIdInput,
+  EventAnonCredsSchemasRegisterInput,
+} from '@ocm/shared';
+
+import { ClientsModule, Transport } from '@nestjs/microservices';
+import { Test } from '@nestjs/testing';
+import {
+  EventAnonCredsSchemasGetAll,
+  EventAnonCredsSchemasGetById,
+  EventAnonCredsSchemasRegister,
+} from '@ocm/shared';
+import { firstValueFrom } from 'rxjs';
+
+import { AgentModule } from '../src/agent/agent.module.js';
+import { DidsModule } from '../src/agent/dids/dids.module.js';
+import { DidsService } from '../src/agent/dids/dids.service.js';
+import { SchemasModule } from '../src/agent/schemas/schemas.module.js';
+import { TenantsModule } from '../src/agent/tenants/tenants.module.js';
+import { TenantsService } from '../src/agent/tenants/tenants.service.js';
+import { mockConfigModule } from '../src/config/__tests__/mockConfig.js';
+
+describe('Schemas', () => {
+  const TOKEN = 'SCHEMAS_CLIENT_SERVICE';
+  let app: INestApplication;
+  let client: ClientProxy;
+  let tenantId: string;
+  let issuerDid: string;
+
+  beforeAll(async () => {
+    const moduleRef = await Test.createTestingModule({
+      imports: [
+        mockConfigModule(3004, true),
+        AgentModule,
+        SchemasModule,
+        TenantsModule,
+        DidsModule,
+        ClientsModule.register([{ name: TOKEN, transport: Transport.NATS }]),
+      ],
+    }).compile();
+
+    app = moduleRef.createNestApplication();
+
+    app.connectMicroservice({ transport: Transport.NATS });
+
+    await app.startAllMicroservices();
+    await app.init();
+
+    client = app.get(TOKEN);
+    await client.connect();
+
+    const ts = app.get(TenantsService);
+    const { id } = await ts.create(TOKEN);
+    tenantId = id;
+
+    const ds = app.get(DidsService);
+    const [did] = await ds.registerDidIndyFromSeed({
+      tenantId,
+      seed: '12312367897123300000000000000000',
+    });
+    issuerDid = did;
+  });
+
+  afterAll(async () => {
+    await app.close();
+    client.close();
+  });
+
+  it(EventAnonCredsSchemasGetAll.token, async () => {
+    const response$ = client.send<
+      EventAnonCredsSchemasGetAll,
+      EventAnonCredsSchemasGetAllInput
+    >(EventAnonCredsSchemasGetAll.token, { tenantId });
+    const response = await firstValueFrom(response$);
+    const eventInstance = EventAnonCredsSchemasGetAll.fromEvent(response);
+
+    expect(eventInstance.instance).toEqual(expect.arrayContaining([]));
+  });
+
+  it(EventAnonCredsSchemasGetById.token, async () => {
+    const response$ = client.send<
+      EventAnonCredsSchemasGetById,
+      EventAnonCredsSchemasGetByIdInput
+    >(EventAnonCredsSchemasGetById.token, { tenantId, schemaId: 'some-id' });
+    const response = await firstValueFrom(response$);
+    const eventInstance = EventAnonCredsSchemasGetById.fromEvent(response);
+
+    expect(eventInstance.instance).toEqual(null);
+  });
+
+  it(EventAnonCredsSchemasRegister.token, async () => {
+    const version = `1.${new Date().getTime()}`;
+    const attributeNames = ['names', 'age'];
+    const name = 'my-schema';
+    const response$ = client.send<
+      EventAnonCredsSchemasRegister,
+      EventAnonCredsSchemasRegisterInput
+    >(EventAnonCredsSchemasRegister.token, {
+      tenantId,
+      name,
+      version,
+      issuerDid,
+      attributeNames,
+    });
+
+    const response = await firstValueFrom(response$);
+    const eventInstance = EventAnonCredsSchemasRegister.fromEvent(response);
+
+    expect(eventInstance.instance).toMatchObject({
+      attrNames: attributeNames,
+      issuerId: issuerDid,
+      name,
+      version,
+    });
+  });
+});