diff --git a/apps/schema-manager/src/credential-definitions/__tests__/credential-definitions.controller.spec.ts b/apps/schema-manager/src/credential-definitions/__tests__/credential-definitions.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3980056858bf8cea92149e54e81d3bc8b6f1b694
--- /dev/null
+++ b/apps/schema-manager/src/credential-definitions/__tests__/credential-definitions.controller.spec.ts
@@ -0,0 +1,138 @@
+import type { CreateCredentialDefinitionPayload } from '../dto/create-credential-definition.dto.js';
+import type { TestingModule } from '@nestjs/testing';
+import type {
+  EventAnonCredsCredentialDefinitionsGetAll,
+  EventAnonCredsCredentialDefinitionsGetById,
+  EventAnonCredsCredentialDefinitionsRegister,
+} from '@ocm/shared';
+
+import { Test } from '@nestjs/testing';
+import { Subject, of, takeUntil } from 'rxjs';
+
+import { NATS_CLIENT } from '../../common/constants.js';
+import { CredentialDefinitionsController } from '../credential-definitions.controller.js';
+import { CredentialDefinitionsService } from '../credential-definitions.service.js';
+
+describe('CredentialDefinitionsController', () => {
+  const natsClientMock = {};
+
+  let controller: CredentialDefinitionsController;
+  let service: CredentialDefinitionsService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      controllers: [CredentialDefinitionsController],
+      providers: [
+        { provide: NATS_CLIENT, useValue: natsClientMock },
+        CredentialDefinitionsService,
+      ],
+    }).compile();
+
+    controller = module.get<CredentialDefinitionsController>(
+      CredentialDefinitionsController,
+    );
+    service = module.get<CredentialDefinitionsService>(
+      CredentialDefinitionsService,
+    );
+  });
+
+  describe('find', () => {
+    it('should return a list of credential definitions', (done) => {
+      const unsubscribe$ = new Subject<void>();
+      const tenantId = 'exampleTenantId';
+      const expectedResult: EventAnonCredsCredentialDefinitionsGetAll['data'] =
+        [];
+
+      jest
+        .spyOn(service, 'findCredentialDefinitions')
+        .mockReturnValueOnce(of(expectedResult));
+
+      controller
+        .find({ tenantId })
+        .pipe(takeUntil(unsubscribe$))
+        .subscribe((result) => {
+          expect(result).toStrictEqual(expectedResult);
+
+          unsubscribe$.next();
+          unsubscribe$.complete();
+
+          done();
+        });
+    });
+  });
+
+  describe('get', () => {
+    it('should return a credential definition', (done) => {
+      const unsubscribe$ = new Subject<void>();
+      const tenantId = 'exampleTenantId';
+      const credentialDefinitionId = 'exampleCredentialDefinitionId';
+      const expectedResult: EventAnonCredsCredentialDefinitionsGetById['data'] =
+        {
+          credentialDefinitionId: 'exampleCredentialDefinitionId',
+          issuerId: 'exampleIssuerId',
+          schemaId: 'exampleSchemaId',
+          tag: 'exampleTag',
+          type: 'CL',
+          value: {
+            primary: {},
+            revocation: {},
+          },
+        };
+
+      jest
+        .spyOn(service, 'getCredentialDefinitionById')
+        .mockReturnValueOnce(of(expectedResult));
+
+      controller
+        .get({ tenantId }, credentialDefinitionId)
+        .pipe(takeUntil(unsubscribe$))
+        .subscribe((result) => {
+          expect(result).toStrictEqual(expectedResult);
+
+          unsubscribe$.next();
+          unsubscribe$.complete();
+
+          done();
+        });
+    });
+  });
+
+  describe('register', () => {
+    it('should return a credential definition', (done) => {
+      const unsubscribe$ = new Subject<void>();
+      const tenantId = 'exampleTenantId';
+      const payload: CreateCredentialDefinitionPayload = {
+        schemaId: 'exampleSchemaId',
+        tag: 'exampleTag',
+      };
+      const expectedResult: EventAnonCredsCredentialDefinitionsRegister['data'] =
+        {
+          credentialDefinitionId: 'exampleCredentialDefinitionId',
+          issuerId: 'exampleIssuerId',
+          schemaId: 'exampleSchemaId',
+          tag: 'exampleTag',
+          type: 'CL',
+          value: {
+            primary: {},
+            revocation: {},
+          },
+        };
+
+      jest
+        .spyOn(service, 'registerCredentialDefinition')
+        .mockReturnValueOnce(of(expectedResult));
+
+      controller
+        .register({ tenantId }, payload)
+        .pipe(takeUntil(unsubscribe$))
+        .subscribe((result) => {
+          expect(result).toStrictEqual(expectedResult);
+
+          unsubscribe$.next();
+          unsubscribe$.complete();
+
+          done();
+        });
+    });
+  });
+});
diff --git a/apps/schema-manager/src/credential-definitions/__tests__/credential-definitions.module.spec.ts b/apps/schema-manager/src/credential-definitions/__tests__/credential-definitions.module.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2df90a796c49d0c0f523616582fee65a517091a3
--- /dev/null
+++ b/apps/schema-manager/src/credential-definitions/__tests__/credential-definitions.module.spec.ts
@@ -0,0 +1,33 @@
+import { ClientsModule } from '@nestjs/microservices';
+import { Test } from '@nestjs/testing';
+
+import { NATS_CLIENT } from '../../common/constants.js';
+import { CredentialDefinitionsController } from '../credential-definitions.controller.js';
+import { CredentialDefinitionsModule } from '../credential-definitions.module.js';
+import { CredentialDefinitionsService } from '../credential-definitions.service.js';
+
+describe('CredentialDefinitionsModule', () => {
+  let credentialDefinitionsModule: CredentialDefinitionsModule;
+
+  beforeEach(async () => {
+    const moduleRef = await Test.createTestingModule({
+      imports: [
+        ClientsModule.registerAsync({
+          isGlobal: true,
+          clients: [{ name: NATS_CLIENT, useFactory: () => ({}) }],
+        }),
+        CredentialDefinitionsModule,
+      ],
+      controllers: [CredentialDefinitionsController],
+      providers: [CredentialDefinitionsService],
+    }).compile();
+
+    credentialDefinitionsModule = moduleRef.get<CredentialDefinitionsModule>(
+      CredentialDefinitionsModule,
+    );
+  });
+
+  it('should be defined', () => {
+    expect(credentialDefinitionsModule).toBeDefined();
+  });
+});
diff --git a/apps/schema-manager/src/credential-definitions/__tests__/credential-definitions.service.spec.ts b/apps/schema-manager/src/credential-definitions/__tests__/credential-definitions.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..be86f4634ad8dedd0775b491f22a2260cfe054c0
--- /dev/null
+++ b/apps/schema-manager/src/credential-definitions/__tests__/credential-definitions.service.spec.ts
@@ -0,0 +1,152 @@
+import type { TestingModule } from '@nestjs/testing';
+
+import { Test } from '@nestjs/testing';
+import {
+  EventAnonCredsCredentialDefinitionsGetAll,
+  EventAnonCredsCredentialDefinitionsGetById,
+  EventAnonCredsCredentialDefinitionsRegister,
+} from '@ocm/shared';
+import { Subject, of, takeUntil } from 'rxjs';
+
+import { NATS_CLIENT } from '../../common/constants.js';
+import { CredentialDefinitionsService } from '../credential-definitions.service.js';
+
+describe('CredentialDefinitionsService', () => {
+  let service: CredentialDefinitionsService;
+  const natsClientMock = { send: jest.fn() };
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      providers: [
+        { provide: NATS_CLIENT, useValue: natsClientMock },
+        CredentialDefinitionsService,
+      ],
+    }).compile();
+
+    service = module.get<CredentialDefinitionsService>(
+      CredentialDefinitionsService,
+    );
+
+    jest.resetAllMocks();
+  });
+
+  describe('findCredentialDefinitions', () => {
+    it('should call natsClient.send with the correct pattern and payload', (done) => {
+      const unsubscribe$ = new Subject<void>();
+      const tenantId = 'testTenantId';
+      const expectedResult: EventAnonCredsCredentialDefinitionsGetAll['data'] =
+        [];
+
+      natsClientMock.send.mockReturnValueOnce(
+        of(new EventAnonCredsCredentialDefinitionsGetAll([], tenantId)),
+      );
+
+      service
+        .findCredentialDefinitions(tenantId)
+        .pipe(takeUntil(unsubscribe$))
+        .subscribe((result) => {
+          expect(natsClientMock.send).toHaveBeenCalledWith(
+            EventAnonCredsCredentialDefinitionsGetAll.token,
+            { tenantId },
+          );
+
+          expect(result).toStrictEqual(expectedResult);
+
+          unsubscribe$.next();
+          unsubscribe$.complete();
+
+          done();
+        });
+    });
+  });
+
+  describe('getCredentialDefinitionById', () => {
+    it('should call natsClient.send with the correct pattern and payload', (done) => {
+      const unsubscribe$ = new Subject<void>();
+      const tenantId = 'testTenantId';
+      const credentialDefinitionId = 'testCredentialDefinitionId';
+      const expectedResult: EventAnonCredsCredentialDefinitionsGetById['data'] =
+        {
+          credentialDefinitionId: 'testCredentialDefinitionId',
+          issuerId: 'testIssuerId',
+          schemaId: 'testSchemaId',
+          tag: 'testTag',
+          type: 'CL',
+          value: {
+            primary: {},
+            revocation: {},
+          },
+        };
+
+      natsClientMock.send.mockReturnValueOnce(
+        of(
+          new EventAnonCredsCredentialDefinitionsGetById(
+            expectedResult,
+            tenantId,
+          ),
+        ),
+      );
+
+      service
+        .getCredentialDefinitionById(tenantId, credentialDefinitionId)
+        .pipe(takeUntil(unsubscribe$))
+        .subscribe((result) => {
+          expect(natsClientMock.send).toHaveBeenCalledWith(
+            EventAnonCredsCredentialDefinitionsGetById.token,
+            { tenantId, credentialDefinitionId },
+          );
+
+          expect(result).toStrictEqual(expectedResult);
+
+          unsubscribe$.next();
+          unsubscribe$.complete();
+
+          done();
+        });
+    });
+  });
+
+  describe('createCredentialDefinition', () => {
+    it('should call natsClient.send with the correct pattern and payload', (done) => {
+      const unsubscribe$ = new Subject<void>();
+      const tenantId = 'testTenantId';
+      const payload = { test: 'payload' };
+      const expectedResult: EventAnonCredsCredentialDefinitionsRegister['data'] =
+        {
+          credentialDefinitionId: 'testCredentialDefinitionId',
+          issuerId: 'testIssuerId',
+          schemaId: 'testSchemaId',
+          tag: 'testTag',
+          type: 'CL',
+          value: {
+            primary: {},
+            revocation: {},
+          },
+        };
+
+      natsClientMock.send.mockReturnValueOnce(
+        of(
+          new EventAnonCredsCredentialDefinitionsRegister(
+            expectedResult,
+            tenantId,
+          ),
+        ),
+      );
+
+      service
+        .registerCredentialDefinition(tenantId, payload)
+        .pipe(takeUntil(unsubscribe$))
+        .subscribe(() => {
+          expect(natsClientMock.send).toHaveBeenCalledWith(
+            EventAnonCredsCredentialDefinitionsRegister.token,
+            { tenantId, payload },
+          );
+
+          unsubscribe$.next();
+          unsubscribe$.complete();
+
+          done();
+        });
+    });
+  });
+});
diff --git a/apps/schema-manager/src/credential-definitions/credential-definitions.controller.ts b/apps/schema-manager/src/credential-definitions/credential-definitions.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..099cb60782fdf8749ed7b15d7c24df351e262941
--- /dev/null
+++ b/apps/schema-manager/src/credential-definitions/credential-definitions.controller.ts
@@ -0,0 +1,292 @@
+import {
+  Body,
+  Controller,
+  Get,
+  HttpStatus,
+  Param,
+  Post,
+  Query,
+  UseInterceptors,
+  UsePipes,
+  ValidationPipe,
+} from '@nestjs/common';
+import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
+import { MultitenancyParams } from '@ocm/shared';
+
+import { ResponseFormatInterceptor } from '../common/response-format.interceptor.js';
+
+import { CredentialDefinitionsService } from './credential-definitions.service.js';
+import { CreateCredentialDefinitionPayload } from './dto/create-credential-definition.dto.js';
+
+@Controller()
+@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
+@UseInterceptors(ResponseFormatInterceptor)
+@ApiTags('Credential Definitions')
+export class CredentialDefinitionsController {
+  public constructor(private readonly service: CredentialDefinitionsService) {}
+
+  @Get()
+  @ApiOperation({
+    summary: 'Fetch a list of credential definitions',
+    description:
+      'This call provides a list of credential definitions for a given tenant',
+  })
+  @ApiResponse({
+    status: HttpStatus.OK,
+    description: 'Credential definitions fetched successfully',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Credential definitions fetched successfully': {
+            value: {
+              statusCode: 200,
+              message: 'Credential definitions fetched successfully',
+              data: [
+                {
+                  id: '71b784a3',
+                },
+              ],
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.NOT_FOUND,
+    description: 'Tenant not found',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Tenant not found': {
+            value: {
+              statusCode: 404,
+              message: 'Tenant not found',
+              error: 'Not Found',
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.INTERNAL_SERVER_ERROR,
+    description: 'Internal server error',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Internal server error': {
+            value: {
+              statusCode: 500,
+              message: 'Internal server error',
+              error: 'Internal Server Error',
+            },
+          },
+        },
+      },
+    },
+  })
+  public find(
+    @Query() { tenantId }: MultitenancyParams,
+  ): ReturnType<CredentialDefinitionsService['findCredentialDefinitions']> {
+    return this.service.findCredentialDefinitions(tenantId);
+  }
+
+  @Get(':credentialDefinitionId')
+  @ApiOperation({
+    summary: 'Fetch a credential definition by ID',
+    description:
+      'This call provides a credential definition for a given tenant',
+  })
+  @ApiResponse({
+    status: HttpStatus.OK,
+    description: 'Credential definition fetched successfully',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Credential definition fetched successfully': {
+            value: {
+              statusCode: 200,
+              message: 'Credential definition fetched successfully',
+              data: {
+                id: '71b784a3',
+              },
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.NOT_FOUND,
+    description: 'Credential definition not found',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Credential definition not found': {
+            value: {
+              statusCode: 404,
+              message: 'Credential definition not found',
+              error: 'Not Found',
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.NOT_FOUND,
+    description: 'Tenant not found',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Tenant not found': {
+            value: {
+              statusCode: 404,
+              message: 'Tenant not found',
+              error: 'Not Found',
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.INTERNAL_SERVER_ERROR,
+    description: 'Internal server error',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Internal server error': {
+            value: {
+              statusCode: 500,
+              message: 'Internal server error',
+              error: 'Internal Server Error',
+            },
+          },
+        },
+      },
+    },
+  })
+  public get(
+    @Query() { tenantId }: MultitenancyParams,
+    @Param('credentialDefinitionId') credentialDefinitionId: string,
+  ): ReturnType<CredentialDefinitionsService['getCredentialDefinitionById']> {
+    return this.service.getCredentialDefinitionById(
+      tenantId,
+      credentialDefinitionId,
+    );
+  }
+
+  @Post()
+  @ApiOperation({
+    summary: 'Create a credential definition',
+    description:
+      'This call allows you to create a credential definition for a given tenant',
+  })
+  @ApiResponse({
+    status: HttpStatus.CREATED,
+    description: 'Credential definition created successfully',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Credential definition created successfully': {
+            value: {
+              statusCode: 201,
+              message: 'Credential definition created successfully',
+              data: {
+                id: '71b784a3',
+              },
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.NOT_FOUND,
+    description: 'Tenant not found',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Tenant not found': {
+            value: {
+              statusCode: 404,
+              message: 'Tenant not found',
+              error: 'Not Found',
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.BAD_REQUEST,
+    description: 'Invalid request',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Invalid request': {
+            value: {
+              statusCode: 400,
+              message: 'Invalid request',
+              error: 'Bad Request',
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.CONFLICT,
+    description: 'Credential definition already exists',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Credential definition already exists': {
+            value: {
+              statusCode: 409,
+              message: 'Credential definition already exists',
+              error: 'Conflict',
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.INTERNAL_SERVER_ERROR,
+    description: 'Internal server error',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Internal server error': {
+            value: {
+              statusCode: 500,
+              message: 'Internal server error',
+              error: 'Internal Server Error',
+            },
+          },
+        },
+      },
+    },
+  })
+  public register(
+    @Query() { tenantId }: MultitenancyParams,
+    @Body() payload: CreateCredentialDefinitionPayload,
+  ): ReturnType<CredentialDefinitionsService['registerCredentialDefinition']> {
+    return this.service.registerCredentialDefinition(tenantId, payload);
+  }
+}
diff --git a/apps/schema-manager/src/credential-definitions/credential-definitions.module.ts b/apps/schema-manager/src/credential-definitions/credential-definitions.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e53667ea6eb6c1a5fa3ea1c23d73170666d66ac5
--- /dev/null
+++ b/apps/schema-manager/src/credential-definitions/credential-definitions.module.ts
@@ -0,0 +1,10 @@
+import { Module } from '@nestjs/common';
+
+import { CredentialDefinitionsController } from './credential-definitions.controller.js';
+import { CredentialDefinitionsService } from './credential-definitions.service.js';
+
+@Module({
+  providers: [CredentialDefinitionsService],
+  controllers: [CredentialDefinitionsController],
+})
+export class CredentialDefinitionsModule {}
diff --git a/apps/schema-manager/src/credential-definitions/credential-definitions.service.ts b/apps/schema-manager/src/credential-definitions/credential-definitions.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a24b0dc8f98aabcb0c2d940892a824c042cd4798
--- /dev/null
+++ b/apps/schema-manager/src/credential-definitions/credential-definitions.service.ts
@@ -0,0 +1,55 @@
+import type { EventAnonCredsCredentialDefinitionsGetAllInput } from '@ocm/shared';
+import type { Observable } from 'rxjs';
+
+import { Inject, Injectable } from '@nestjs/common';
+import { ClientProxy } from '@nestjs/microservices';
+import {
+  EventAnonCredsCredentialDefinitionsGetAll,
+  EventAnonCredsCredentialDefinitionsGetById,
+  EventAnonCredsCredentialDefinitionsRegister,
+} from '@ocm/shared';
+import { map } from 'rxjs';
+
+import { NATS_CLIENT } from '../common/constants.js';
+
+@Injectable()
+export class CredentialDefinitionsService {
+  public constructor(
+    @Inject(NATS_CLIENT) private readonly natsClient: ClientProxy,
+  ) {}
+
+  public findCredentialDefinitions(
+    tenantId: string,
+  ): Observable<EventAnonCredsCredentialDefinitionsGetAll['data']> {
+    return this.natsClient
+      .send<
+        EventAnonCredsCredentialDefinitionsGetAll,
+        EventAnonCredsCredentialDefinitionsGetAllInput
+      >(EventAnonCredsCredentialDefinitionsGetAll.token, { tenantId })
+      .pipe(map((result) => result.data));
+  }
+
+  public getCredentialDefinitionById(
+    tenantId: string,
+    credentialDefinitionId: string,
+  ): Observable<EventAnonCredsCredentialDefinitionsGetById['data']> {
+    return this.natsClient
+      .send(EventAnonCredsCredentialDefinitionsGetById.token, {
+        tenantId,
+        credentialDefinitionId,
+      })
+      .pipe(map((result) => result.data));
+  }
+
+  public registerCredentialDefinition(
+    tenantId: string,
+    payload: unknown,
+  ): Observable<EventAnonCredsCredentialDefinitionsRegister['data']> {
+    return this.natsClient
+      .send(EventAnonCredsCredentialDefinitionsRegister.token, {
+        tenantId,
+        payload,
+      })
+      .pipe(map((result) => result.data));
+  }
+}
diff --git a/apps/schema-manager/src/credential-definitions/dto/create-credential-definition.dto.ts b/apps/schema-manager/src/credential-definitions/dto/create-credential-definition.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..02c3612cf31ef82f23201347de2e87e1dec44bb4
--- /dev/null
+++ b/apps/schema-manager/src/credential-definitions/dto/create-credential-definition.dto.ts
@@ -0,0 +1,14 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsNotEmpty, IsString } from 'class-validator';
+
+export class CreateCredentialDefinitionPayload {
+  @IsString()
+  @IsNotEmpty()
+  @ApiProperty()
+  public schemaId: string;
+
+  @IsString()
+  @IsNotEmpty()
+  @ApiProperty()
+  public tag: string;
+}