diff --git a/apps/credential-manager/src/application.ts b/apps/credential-manager/src/application.ts
index c0ad214c9e168df74c7ff3f31a15253f0cbafa39..f60686bfdfb1359cffcd9c418d60ee6bd4459be6 100644
--- a/apps/credential-manager/src/application.ts
+++ b/apps/credential-manager/src/application.ts
@@ -12,6 +12,7 @@ import { natsConfig } from './config/nats.config.js';
 import { ssiConfig } from './config/ssi.config.js';
 import { validationSchema } from './config/validation.js';
 import { CredentialOffersModule } from './credential-offers/credential-offers.module.js';
+import { CredentialRequestsModule } from './credential-requests/credential-requests.module.js';
 
 @Module({
   imports: [
@@ -59,10 +60,12 @@ import { CredentialOffersModule } from './credential-offers/credential-offers.mo
     }),
 
     CredentialOffersModule,
+    CredentialRequestsModule,
 
     RouterModule.register([
       { module: HealthModule, path: '/health' },
       { module: CredentialOffersModule, path: '/credential-offers' },
+      { module: CredentialRequestsModule, path: '/credential-requests' },
     ]),
   ],
 })
diff --git a/apps/credential-manager/src/credential-requests/__tests__/credential-requests.controller.spec.ts b/apps/credential-manager/src/credential-requests/__tests__/credential-requests.controller.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..302cc4058798f0e1bb4cb257d2d53d22b47aa914
--- /dev/null
+++ b/apps/credential-manager/src/credential-requests/__tests__/credential-requests.controller.spec.ts
@@ -0,0 +1,90 @@
+import type { TestingModule } from '@nestjs/testing';
+import type {
+  EventAnonCredsCredentialRequestGetAll,
+  EventAnonCredsCredentialRequestGetById,
+} from '@ocm/shared';
+
+import { Test } from '@nestjs/testing';
+import { Subject, of, takeUntil } from 'rxjs';
+
+import { NATS_CLIENT } from '../../common/constants.js';
+import { CredentialRequestsController } from '../credential-requests.controller.js';
+import { CredentialRequestsService } from '../credential-requests.service.js';
+
+describe('CredentialRequestsController', () => {
+  const natsClientMock = {};
+
+  let controller: CredentialRequestsController;
+  let service: CredentialRequestsService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      controllers: [CredentialRequestsController],
+      providers: [
+        { provide: NATS_CLIENT, useValue: natsClientMock },
+        CredentialRequestsService,
+      ],
+    }).compile();
+
+    controller = module.get<CredentialRequestsController>(
+      CredentialRequestsController,
+    );
+    service = module.get<CredentialRequestsService>(CredentialRequestsService);
+  });
+
+  describe('find', () => {
+    it('should return a list of credential requests', (done) => {
+      const unsubscribe$ = new Subject<void>();
+      const tenantId = 'exampleTenantId';
+      const expectedResult: EventAnonCredsCredentialRequestGetAll['data'] = [];
+
+      jest
+        .spyOn(service, 'findCredentialRequests')
+        .mockReturnValueOnce(of(expectedResult));
+
+      controller
+        .find({ tenantId })
+        .pipe(takeUntil(unsubscribe$))
+        .subscribe((result) => {
+          expect(result).toStrictEqual(expectedResult);
+
+          unsubscribe$.next();
+          unsubscribe$.complete();
+
+          done();
+        });
+    });
+  });
+
+  describe('getById', () => {
+    it('should return a credential request', (done) => {
+      const unsubscribe$ = new Subject<void>();
+      const tenantId = 'exampleTenantId';
+      const credentialRequestId = 'exampleCredentialRequestId';
+      const expectedResult: EventAnonCredsCredentialRequestGetById['data'] = {
+        blinded_ms: {},
+        blinded_ms_correctness_proof: {},
+        cred_def_id: 'cred_def_id',
+        nonce: 'nonce',
+        entropy: 'entropy',
+        prover_did: 'prover_did',
+      };
+
+      jest
+        .spyOn(service, 'getCredentialRequestById')
+        .mockReturnValueOnce(of(expectedResult));
+
+      controller
+        .getById({ credentialRequestId }, { tenantId })
+        .pipe(takeUntil(unsubscribe$))
+        .subscribe((result) => {
+          expect(result).toStrictEqual(expectedResult);
+
+          unsubscribe$.next();
+          unsubscribe$.complete();
+
+          done();
+        });
+    });
+  });
+});
diff --git a/apps/credential-manager/src/credential-requests/__tests__/credential-requests.module.spec.ts b/apps/credential-manager/src/credential-requests/__tests__/credential-requests.module.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7fcd088315f73bfcf6cfc81f4e3c895705b58092
--- /dev/null
+++ b/apps/credential-manager/src/credential-requests/__tests__/credential-requests.module.spec.ts
@@ -0,0 +1,41 @@
+import { ClientsModule } from '@nestjs/microservices';
+import { Test } from '@nestjs/testing';
+
+import { NATS_CLIENT } from '../../common/constants.js';
+import { CredentialRequestsController } from '../credential-requests.controller.js';
+import { CredentialRequestsModule } from '../credential-requests.module.js';
+import { CredentialRequestsService } from '../credential-requests.service.js';
+
+describe('CredentialRequestsModule', () => {
+  let credentialRequestsController: CredentialRequestsController;
+  let credentialRequestsService: CredentialRequestsService;
+
+  beforeEach(async () => {
+    const moduleRef = await Test.createTestingModule({
+      imports: [
+        ClientsModule.registerAsync({
+          isGlobal: true,
+          clients: [{ name: NATS_CLIENT, useFactory: () => ({}) }],
+        }),
+        CredentialRequestsModule,
+      ],
+    }).compile();
+
+    credentialRequestsController = moduleRef.get<CredentialRequestsController>(
+      CredentialRequestsController,
+    );
+    credentialRequestsService = moduleRef.get<CredentialRequestsService>(
+      CredentialRequestsService,
+    );
+  });
+
+  it('should be defined', () => {
+    expect(credentialRequestsController).toBeDefined();
+    expect(credentialRequestsController).toBeInstanceOf(
+      CredentialRequestsController,
+    );
+
+    expect(credentialRequestsService).toBeDefined();
+    expect(credentialRequestsService).toBeInstanceOf(CredentialRequestsService);
+  });
+});
diff --git a/apps/credential-manager/src/credential-requests/__tests__/credential-requests.service.spec.ts b/apps/credential-manager/src/credential-requests/__tests__/credential-requests.service.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a9a8d844eb55d25548ccb150edac1d6b5a42dcbc
--- /dev/null
+++ b/apps/credential-manager/src/credential-requests/__tests__/credential-requests.service.spec.ts
@@ -0,0 +1,83 @@
+import type { TestingModule } from '@nestjs/testing';
+
+import { Test } from '@nestjs/testing';
+import { EventAnonCredsCredentialRequestGetAll } from '@ocm/shared';
+import { Subject, of, takeUntil } from 'rxjs';
+
+import { NATS_CLIENT } from '../../common/constants.js';
+import { CredentialRequestsService } from '../credential-requests.service.js';
+
+describe('CredentialRequestsService', () => {
+  const natsClientMock = { send: jest.fn() };
+  let service: CredentialRequestsService;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      providers: [
+        { provide: NATS_CLIENT, useValue: natsClientMock },
+        CredentialRequestsService,
+      ],
+    }).compile();
+
+    service = module.get<CredentialRequestsService>(CredentialRequestsService);
+  });
+
+  describe('findCredentialRequests', () => {
+    it('should call the natsClient send method with the correct arguments', (done) => {
+      const unsubscribe$ = new Subject<void>();
+      const tenantId = 'tenantId';
+      const expectedResult: EventAnonCredsCredentialRequestGetAll['data'] = [];
+
+      natsClientMock.send.mockReturnValueOnce(
+        of(new EventAnonCredsCredentialRequestGetAll(expectedResult, tenantId)),
+      );
+
+      service
+        .findCredentialRequests(tenantId)
+        .pipe(takeUntil(unsubscribe$))
+        .subscribe((result) => {
+          expect(natsClientMock.send).toHaveBeenCalledWith(
+            EventAnonCredsCredentialRequestGetAll.token,
+            { tenantId },
+          );
+
+          expect(result).toStrictEqual(expectedResult);
+
+          unsubscribe$.next();
+          unsubscribe$.complete();
+
+          done();
+        });
+    });
+  });
+
+  describe('getCredentialRequestById', () => {
+    it('should call the natsClient send method with the correct arguments', (done) => {
+      const unsubscribe$ = new Subject<void>();
+      const tenantId = 'tenantId';
+      const credentialRequestId = 'credentialRequestId';
+      const expectedResult: EventAnonCredsCredentialRequestGetAll['data'] = [];
+
+      natsClientMock.send.mockReturnValueOnce(
+        of(new EventAnonCredsCredentialRequestGetAll(expectedResult, tenantId)),
+      );
+
+      service
+        .getCredentialRequestById(tenantId, credentialRequestId)
+        .pipe(takeUntil(unsubscribe$))
+        .subscribe((result) => {
+          expect(natsClientMock.send).toHaveBeenCalledWith(
+            EventAnonCredsCredentialRequestGetAll.token,
+            { tenantId, credentialRequestId },
+          );
+
+          expect(result).toStrictEqual(expectedResult);
+
+          unsubscribe$.next();
+          unsubscribe$.complete();
+
+          done();
+        });
+    });
+  });
+});
diff --git a/apps/credential-manager/src/credential-requests/credential-requests.controller.ts b/apps/credential-manager/src/credential-requests/credential-requests.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..99297fdf950fcc017bdb1330a356a6f6e07f8ee9
--- /dev/null
+++ b/apps/credential-manager/src/credential-requests/credential-requests.controller.ts
@@ -0,0 +1,186 @@
+import {
+  Controller,
+  Get,
+  HttpStatus,
+  Param,
+  Query,
+  UseInterceptors,
+  UsePipes,
+  ValidationPipe,
+} from '@nestjs/common';
+import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
+import { MultitenancyParams, ResponseFormatInterceptor } from '@ocm/shared';
+
+import { CredentialRequestsService } from './credential-requests.service.js';
+import { GetByIdParams } from './dto/get-by-id.dto.js';
+
+@Controller()
+@UsePipes(new ValidationPipe({ transform: true, whitelist: true }))
+@UseInterceptors(ResponseFormatInterceptor)
+@ApiTags('Credential Requests')
+export class CredentialRequestsController {
+  public constructor(private readonly service: CredentialRequestsService) {}
+
+  @Get()
+  @ApiOperation({
+    summary: 'Fetch a list of credential requests',
+    description:
+      'This call provides a list of credential requests for a given tenant',
+  })
+  @ApiResponse({
+    status: HttpStatus.OK,
+    description: 'Credential requests fetched successfully',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Credential requests fetched successfully': {
+            value: {
+              statusCode: 200,
+              message: 'Credential requests fetched successfully',
+              data: [
+                {
+                  id: '71b784a3',
+                },
+              ],
+            },
+          },
+          'Tenant not found': {
+            value: {
+              statusCode: 404,
+              message: 'Tenant not found',
+              data: null,
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.NOT_FOUND,
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Credential request not found': {
+            value: {
+              statusCode: 404,
+              message: 'Credential request not found',
+              data: null,
+            },
+          },
+          'Tenant not found': {
+            value: {
+              statusCode: 404,
+              message: 'Tenant not found',
+              data: null,
+            },
+          },
+        },
+      },
+    },
+  })
+  @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',
+              data: null,
+            },
+          },
+        },
+      },
+    },
+  })
+  public find(@Query() { tenantId }: MultitenancyParams) {
+    return this.service.findCredentialRequests(tenantId);
+  }
+
+  @Get(':id')
+  @ApiOperation({
+    summary: 'Fetch a credential request by id',
+    description:
+      'This call provides a credential request for a given tenant by id',
+  })
+  @ApiResponse({
+    status: HttpStatus.OK,
+    description: 'Credential request fetched successfully',
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Credential request fetched successfully': {
+            value: {
+              statusCode: 200,
+              message: 'Credential request fetched successfully',
+              data: {
+                id: '71b784a3',
+              },
+            },
+          },
+          'Tenant not found': {
+            value: {
+              statusCode: 404,
+              message: 'Tenant not found',
+              data: null,
+            },
+          },
+        },
+      },
+    },
+  })
+  @ApiResponse({
+    status: HttpStatus.NOT_FOUND,
+    content: {
+      'application/json': {
+        schema: {},
+        examples: {
+          'Credential request not found': {
+            value: {
+              statusCode: 404,
+              message: 'Credential request not found',
+              data: null,
+            },
+          },
+          'Tenant not found': {
+            value: {
+              statusCode: 404,
+              message: 'Tenant not found',
+              data: null,
+            },
+          },
+        },
+      },
+    },
+  })
+  @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',
+              data: null,
+            },
+          },
+        },
+      },
+    },
+  })
+  public getById(
+    @Param() { credentialRequestId }: GetByIdParams,
+    @Query() { tenantId }: MultitenancyParams,
+  ) {
+    return this.service.getCredentialRequestById(tenantId, credentialRequestId);
+  }
+}
diff --git a/apps/credential-manager/src/credential-requests/credential-requests.module.ts b/apps/credential-manager/src/credential-requests/credential-requests.module.ts
new file mode 100644
index 0000000000000000000000000000000000000000..af559d79bd371d0d0b848ae77f27c89f087b8091
--- /dev/null
+++ b/apps/credential-manager/src/credential-requests/credential-requests.module.ts
@@ -0,0 +1,10 @@
+import { Module } from '@nestjs/common';
+
+import { CredentialRequestsController } from './credential-requests.controller.js';
+import { CredentialRequestsService } from './credential-requests.service.js';
+
+@Module({
+  providers: [CredentialRequestsService],
+  controllers: [CredentialRequestsController],
+})
+export class CredentialRequestsModule {}
diff --git a/apps/credential-manager/src/credential-requests/credential-requests.service.ts b/apps/credential-manager/src/credential-requests/credential-requests.service.ts
new file mode 100644
index 0000000000000000000000000000000000000000..81675508bb0135a679f2136b031a7bb2eba64d0f
--- /dev/null
+++ b/apps/credential-manager/src/credential-requests/credential-requests.service.ts
@@ -0,0 +1,43 @@
+import type {
+  EventAnonCredsCredentialRequestGetAllInput,
+  EventAnonCredsCredentialRequestGetById,
+  EventAnonCredsCredentialRequestGetByIdInput,
+} from '@ocm/shared';
+
+import { Inject, Injectable } from '@nestjs/common';
+import { ClientProxy } from '@nestjs/microservices';
+import { EventAnonCredsCredentialRequestGetAll } from '@ocm/shared';
+import { map } from 'rxjs';
+
+import { NATS_CLIENT } from '../common/constants.js';
+
+@Injectable()
+export class CredentialRequestsService {
+  public constructor(
+    @Inject(NATS_CLIENT) private readonly natsClient: ClientProxy,
+  ) {}
+
+  public findCredentialRequests(tenantId: string) {
+    return this.natsClient
+      .send<
+        EventAnonCredsCredentialRequestGetAll,
+        EventAnonCredsCredentialRequestGetAllInput
+      >(EventAnonCredsCredentialRequestGetAll.token, { tenantId })
+      .pipe(map(({ data }) => data));
+  }
+
+  public getCredentialRequestById(
+    tenantId: string,
+    credentialRequestId: string,
+  ) {
+    return this.natsClient
+      .send<
+        EventAnonCredsCredentialRequestGetById,
+        EventAnonCredsCredentialRequestGetByIdInput
+      >(EventAnonCredsCredentialRequestGetAll.token, {
+        tenantId,
+        credentialRequestId,
+      })
+      .pipe(map(({ data }) => data));
+  }
+}
diff --git a/apps/credential-manager/src/credential-requests/dto/get-by-id.dto.ts b/apps/credential-manager/src/credential-requests/dto/get-by-id.dto.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9c3e323437fe7c0cfaffb4b295d30540f8581623
--- /dev/null
+++ b/apps/credential-manager/src/credential-requests/dto/get-by-id.dto.ts
@@ -0,0 +1,12 @@
+import { ApiProperty } from '@nestjs/swagger';
+import { IsNotEmpty, IsString } from 'class-validator';
+
+export class GetByIdParams {
+  @IsString()
+  @IsNotEmpty()
+  @ApiProperty({
+    description: 'The credential request ID to retrieve',
+    format: 'string',
+  })
+  public credentialRequestId: string;
+}