diff --git a/EnvelopeGenerator.API/Controllers/CacheController.cs b/EnvelopeGenerator.API/Controllers/CacheController.cs
new file mode 100644
index 00000000..e1da376a
--- /dev/null
+++ b/EnvelopeGenerator.API/Controllers/CacheController.cs
@@ -0,0 +1,82 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Options;
+using System.Text.Json;
+using EnvelopeGenerator.API.Options;
+using EnvelopeGenerator.Domain.Constants;
+
+namespace EnvelopeGenerator.API.Controllers;
+
+///
+/// Manages cached data for receivers using distributed cache.
+///
+[ApiController]
+[Route("api/[controller]")]
+[Authorize(Policy = AuthPolicy.Receiver)]
+public class CacheController(
+ IDistributedCache cache,
+ IOptions cacheOptions) : ControllerBase
+{
+ private const string SignatureCacheKeyPrefix = "signature:91751687-8ae6-4777-bf5f-b8846085e62e:";
+
+ ///
+ /// Stores a receiver's signature in cache for the specified envelope.
+ ///
+ [HttpPost("SignatureCapture/{envelopeKey}")]
+ public async Task SaveSignature(
+ string envelopeKey,
+ [FromBody] SignatureCacheRequest request,
+ CancellationToken cancel)
+ {
+ var cacheKey = $"{SignatureCacheKeyPrefix}{envelopeKey}";
+ var json = JsonSerializer.Serialize(request);
+
+ var options = cacheOptions.Value.SignatureCacheExpiration.HasValue
+ ? new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = cacheOptions.Value.SignatureCacheExpiration.Value }
+ : null;
+
+ await cache.SetStringAsync(cacheKey, json, options ?? new DistributedCacheEntryOptions(), cancel);
+
+ return Ok();
+ }
+
+ ///
+ /// Retrieves a cached signature for the specified envelope.
+ ///
+ [HttpGet("SignatureCapture/{envelopeKey}")]
+ public async Task GetSignature(string envelopeKey, CancellationToken cancel)
+ {
+ var cacheKey = $"{SignatureCacheKeyPrefix}{envelopeKey}";
+ var json = await cache.GetStringAsync(cacheKey, cancel);
+
+ if (json is null)
+ return NotFound();
+
+ var signature = JsonSerializer.Deserialize(json);
+ return Ok(signature);
+ }
+
+ ///
+ /// Deletes a cached signature for the specified envelope.
+ ///
+ [HttpDelete("SignatureCapture/{envelopeKey}")]
+ public async Task DeleteSignature(string envelopeKey, CancellationToken cancel)
+ {
+ var cacheKey = $"{SignatureCacheKeyPrefix}{envelopeKey}";
+ await cache.RemoveAsync(cacheKey, cancel);
+
+ return Ok();
+ }
+}
+
+///
+/// Request model for caching signature data.
+///
+public sealed record SignatureCacheRequest(
+ string? DataUrl,
+ string? FullName,
+ string? Position,
+ string? Place);
+
+
diff --git a/EnvelopeGenerator.API/Options/CacheOptions.cs b/EnvelopeGenerator.API/Options/CacheOptions.cs
new file mode 100644
index 00000000..c39da3d7
--- /dev/null
+++ b/EnvelopeGenerator.API/Options/CacheOptions.cs
@@ -0,0 +1,18 @@
+namespace EnvelopeGenerator.API.Options;
+
+///
+/// Configuration options for distributed caching.
+///
+public sealed class CacheOptions
+{
+ ///
+ /// Configuration section name in appsettings.json.
+ ///
+ public const string SectionName = "Cache";
+
+ ///
+ /// Signature cache expiration time.
+ /// If null, signatures will not expire automatically.
+ ///
+ public TimeSpan? SignatureCacheExpiration { get; set; }
+}
diff --git a/EnvelopeGenerator.API/Program.cs b/EnvelopeGenerator.API/Program.cs
index cc306a17..b01334b3 100644
--- a/EnvelopeGenerator.API/Program.cs
+++ b/EnvelopeGenerator.API/Program.cs
@@ -17,6 +17,7 @@ using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using DigitalData.Core.Abstractions.Security.Extensions;
using EnvelopeGenerator.API.Middleware;
+using EnvelopeGenerator.API.Options;
using NLog.Web;
using NLog;
using DigitalData.Auth.Claims;
@@ -265,6 +266,9 @@ try
// Localizer
builder.Services.AddCookieBasedLocalizer();
+ // Cache options
+ builder.Services.Configure(config.GetSection(CacheOptions.SectionName));
+
// Envelope generator serives
#pragma warning disable CS0618 // Type or member is obsolete
builder.Services
diff --git a/EnvelopeGenerator.API/appsettings.json b/EnvelopeGenerator.API/appsettings.json
index 25bff075..2efa9a48 100644
--- a/EnvelopeGenerator.API/appsettings.json
+++ b/EnvelopeGenerator.API/appsettings.json
@@ -174,6 +174,9 @@
"Receiver": [],
"EmailTemplate": [ "TBSIG_EMAIL_TEMPLATE_AFT_UPD" ]
},
+ "Cache": {
+ "SignatureCacheExpiration": null
+ },
"MainPageTitle": null,
"AnnotationParams": {
"Background": {