diff --git a/EnvelopeGenerator.GeneratorAPI/Controllers/ReadOnlyController.cs b/EnvelopeGenerator.GeneratorAPI/Controllers/ReadOnlyController.cs
new file mode 100644
index 00000000..775f1bb8
--- /dev/null
+++ b/EnvelopeGenerator.GeneratorAPI/Controllers/ReadOnlyController.cs
@@ -0,0 +1,95 @@
+using DigitalData.Core.Abstraction.Application.DTO;
+using EnvelopeGenerator.Application.Common.Dto.EnvelopeReceiverReadOnly;
+using EnvelopeGenerator.Application.Common.Interfaces.Services;
+using EnvelopeGenerator.Domain.Constants;
+using EnvelopeGenerator.GeneratorAPI.Extensions;
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Newtonsoft.Json;
+
+namespace EnvelopeGenerator.GeneratorAPI.Controllers;
+
+///
+/// Manages read-only envelope sharing flows.
+///
+[Route("api/[controller]")]
+[ApiController]
+public class ReadOnlyController : ControllerBase
+{
+ private readonly ILogger _logger;
+ private readonly IEnvelopeReceiverReadOnlyService _readOnlyService;
+ private readonly IEnvelopeMailService _mailService;
+ private readonly IEnvelopeHistoryService _historyService;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public ReadOnlyController(ILogger logger, IEnvelopeReceiverReadOnlyService readOnlyService, IEnvelopeMailService mailService, IEnvelopeHistoryService historyService)
+ {
+ _logger = logger;
+ _readOnlyService = readOnlyService;
+ _mailService = mailService;
+ _historyService = historyService;
+ }
+
+ ///
+ /// Creates a new read-only receiver for the current envelope.
+ ///
+ /// Creation payload.
+ [HttpPost]
+ [Authorize(Roles = ReceiverRole.FullyAuth)]
+ public async Task CreateAsync([FromBody] EnvelopeReceiverReadOnlyCreateDto createDto)
+ {
+ var authReceiverMail = User.GetAuthReceiverMail();
+ if (authReceiverMail is null)
+ {
+ _logger.LogError("EmailAddress claim is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto));
+ return Unauthorized();
+ }
+
+ var envelopeId = User.GetAuthEnvelopeId();
+ if (envelopeId is null)
+ {
+ _logger.LogError("Envelope Id claim is not found in envelope-receiver-read-only creation process. Create DTO is:\n {dto}", JsonConvert.SerializeObject(createDto));
+ return Unauthorized();
+ }
+
+ createDto.AddedWho = authReceiverMail;
+ createDto.EnvelopeId = envelopeId;
+
+ var creationRes = await _readOnlyService.CreateAsync(createDto: createDto);
+
+ if (creationRes.IsFailed)
+ {
+ _logger.LogNotice(creationRes);
+ return StatusCode(StatusCodes.Status500InternalServerError);
+ }
+
+ var readRes = await _readOnlyService.ReadByIdAsync(creationRes.Data.Id);
+ if (readRes.IsFailed)
+ {
+ _logger.LogNotice(creationRes);
+ return StatusCode(StatusCodes.Status500InternalServerError);
+ }
+
+ var newReadOnly = readRes.Data;
+
+ return await _mailService.SendAsync(newReadOnly).ThenAsync(SuccessAsync: async _ =>
+ {
+ var histRes = await _historyService.RecordAsync((int)createDto.EnvelopeId, createDto.AddedWho, EnvelopeStatus.EnvelopeShared);
+ if (histRes.IsFailed)
+ {
+ _logger.LogError("Although the envelope was sent as read-only, the EnvelopeShared history could not be saved. Create DTO:\n{createDto}", JsonConvert.SerializeObject(createDto));
+ _logger.LogNotice(histRes.Notices);
+ }
+
+ return Ok();
+ },
+
+ Fail: (msg, ntc) =>
+ {
+ _logger.LogNotice(ntc);
+ return StatusCode(StatusCodes.Status500InternalServerError);
+ });
+ }
+}