diff --git a/EnvelopeGenerator.Application/Signature/Behaviors/AnnotationBehavior.cs b/EnvelopeGenerator.Application/Signature/Behaviors/AnnotationBehavior.cs
new file mode 100644
index 00000000..00d08747
--- /dev/null
+++ b/EnvelopeGenerator.Application/Signature/Behaviors/AnnotationBehavior.cs
@@ -0,0 +1,40 @@
+using DigitalData.Core.Abstraction.Application.Repository;
+using EnvelopeGenerator.Application.Common.Dto;
+using EnvelopeGenerator.Application.Signature.Commands;
+using EnvelopeGenerator.Domain.Entities;
+using MediatR;
+
+namespace EnvelopeGenerator.Application.Signature.Behaviors;
+
+///
+/// Pipeline behavior that saves annotations.
+/// Executes first in the signing process.
+///
+public class AnnotationBehavior : IPipelineBehavior
+{
+ private readonly IRepository _repo;
+
+ ///
+ ///
+ ///
+ ///
+ public AnnotationBehavior(IRepository repository)
+ {
+ _repo = repository;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task Handle(SignCommand request, RequestHandlerDelegate next, CancellationToken cancellationToken)
+ {
+ if (request.PsPdfKitAnnotation is PsPdfKitAnnotation annot)
+ await _repo.CreateAsync(annot.Structured, cancellationToken);
+
+ return await next();
+ }
+}
diff --git a/EnvelopeGenerator.Application/Signature/Behaviors/DocStatusBehavior.cs b/EnvelopeGenerator.Application/Signature/Behaviors/DocStatusBehavior.cs
new file mode 100644
index 00000000..666c4231
--- /dev/null
+++ b/EnvelopeGenerator.Application/Signature/Behaviors/DocStatusBehavior.cs
@@ -0,0 +1,49 @@
+using EnvelopeGenerator.Application.Common.Dto;
+using EnvelopeGenerator.Application.DocStatus.Commands;
+using EnvelopeGenerator.Application.Signature.Commands;
+using EnvelopeGenerator.Domain.Constants;
+using MediatR;
+using System.Text.Json;
+
+namespace EnvelopeGenerator.Application.Signature.Behaviors;
+
+///
+/// Pipeline behavior that creates document status.
+/// Executes second in the signing process.
+///
+public class DocStatusBehavior : IPipelineBehavior
+{
+ private const string BlankAnnotationJson = "{}";
+
+ private readonly ISender _sender;
+
+ ///
+ ///
+ ///
+ ///
+ public DocStatusBehavior(ISender sender)
+ {
+ _sender = sender;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task Handle(SignCommand request, RequestHandlerDelegate next, CancellationToken cancellationToken)
+ {
+ await _sender.Send(new CreateDocStatusCommand()
+ {
+ EnvelopeId = request.EnvelopeReceiver.EnvelopeId,
+ ReceiverId = request.EnvelopeReceiver.ReceiverId,
+ Value = request.PsPdfKitAnnotation is PsPdfKitAnnotation annot
+ ? JsonSerializer.Serialize(annot.Instant, Format.Json.ForAnnotations)
+ : BlankAnnotationJson
+ }, cancellationToken);
+
+ return await next();
+ }
+}
diff --git a/EnvelopeGenerator.Application/Signature/Behaviors/HistoryBehavior.cs b/EnvelopeGenerator.Application/Signature/Behaviors/HistoryBehavior.cs
new file mode 100644
index 00000000..0ba0fc14
--- /dev/null
+++ b/EnvelopeGenerator.Application/Signature/Behaviors/HistoryBehavior.cs
@@ -0,0 +1,47 @@
+using EnvelopeGenerator.Application.Common.Extensions;
+using EnvelopeGenerator.Application.Histories.Commands;
+using EnvelopeGenerator.Application.Signature.Commands;
+using EnvelopeGenerator.Domain.Constants;
+using MediatR;
+
+namespace EnvelopeGenerator.Application.Signature.Behaviors;
+
+///
+/// Pipeline behavior that records history.
+/// Executes third in the signing process.
+///
+public class HistoryBehavior : IPipelineBehavior
+{
+ private readonly ISender _sender;
+
+ ///
+ ///
+ ///
+ ///
+ public HistoryBehavior(ISender sender)
+ {
+ _sender = sender;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task Handle(SignCommand request, RequestHandlerDelegate next, CancellationToken cancellationToken)
+ {
+ if (request.EnvelopeReceiver.Receiver is null)
+ throw new InvalidOperationException($"Receiver information is missing in the notification. SignCommand:\n {request.ToJson(Format.Json.ForDiagnostics)}");
+
+ await _sender.Send(new CreateHistoryCommand()
+ {
+ EnvelopeId = request.EnvelopeReceiver.EnvelopeId,
+ UserReference = request.EnvelopeReceiver.Receiver.EmailAddress,
+ Status = EnvelopeStatus.DocumentSigned,
+ }, cancellationToken);
+
+ return await next();
+ }
+}
diff --git a/EnvelopeGenerator.Application/Signature/Behaviors/SendSignedMailBehavior.cs b/EnvelopeGenerator.Application/Signature/Behaviors/SendSignedMailBehavior.cs
new file mode 100644
index 00000000..7c2ec2c3
--- /dev/null
+++ b/EnvelopeGenerator.Application/Signature/Behaviors/SendSignedMailBehavior.cs
@@ -0,0 +1,122 @@
+using DigitalData.Core.Abstraction.Application.Repository;
+using DigitalData.EmailProfilerDispatcher.Abstraction.Entities;
+using EnvelopeGenerator.Application.Common.Configurations;
+using EnvelopeGenerator.Application.Common.Extensions;
+using EnvelopeGenerator.Application.Signature.Commands;
+using EnvelopeGenerator.Domain.Constants;
+using EnvelopeGenerator.Domain.Entities;
+using EnvelopeGenerator.Domain.Interfaces;
+using MediatR;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
+
+namespace EnvelopeGenerator.Application.Signature.Behaviors;
+
+///
+/// Pipeline behavior that sends signed mail notification.
+/// Executes LAST in the signing process - only if all previous behaviors succeed.
+///
+public class SendSignedMailBehavior : IPipelineBehavior
+{
+ private readonly IRepository _tempRepo;
+ private readonly IRepository _emailOutRepo;
+ private readonly MailParams _mailParams;
+ private readonly DispatcherParams _dispatcherParams;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public SendSignedMailBehavior(
+ IRepository tempRepo,
+ IRepository emailOutRepo,
+ IOptions mailParamsOptions,
+ IOptions dispatcherParamsOptions)
+ {
+ _tempRepo = tempRepo;
+ _emailOutRepo = emailOutRepo;
+ _mailParams = mailParamsOptions.Value;
+ _dispatcherParams = dispatcherParamsOptions.Value;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task Handle(SignCommand request, RequestHandlerDelegate next, CancellationToken cancellationToken)
+ {
+ var placeHolders = CreatePlaceHolders(request);
+
+ var temp = await _tempRepo
+ .Where(x => x.Name == EmailTemplateType.DocumentSigned.ToString())
+ .SingleOrDefaultAsync(cancellationToken)
+ ?? throw new InvalidOperationException($"Email template not found. SignCommand:\n {request.ToJson(Format.Json.ForDiagnostics)}");
+
+ temp.Subject = ReplacePlaceHolders(temp.Subject, placeHolders, _mailParams.Placeholders);
+ temp.Body = ReplacePlaceHolders(temp.Body, placeHolders, _mailParams.Placeholders);
+
+ var emailOut = new EmailOut
+ {
+ EmailAddress = request.EmailAddress,
+ EmailBody = temp.Body,
+ EmailSubj = temp.Subject,
+ AddedWhen = DateTime.Now,
+ AddedWho = _dispatcherParams.AddedWho,
+ SendingProfile = _dispatcherParams.SendingProfile,
+ ReminderTypeId = _dispatcherParams.ReminderTypeId,
+ EmailAttmt1 = _dispatcherParams.EmailAttmt1,
+ WfId = (int)EnvelopeStatus.MessageConfirmationSent,
+ ReferenceString = request.EmailAddress,
+ ReferenceId = request.EnvelopeReceiver.ReceiverId
+ };
+
+ await _emailOutRepo.CreateAsync(emailOut, cancellationToken);
+
+ return await next();
+ }
+
+ private Dictionary CreatePlaceHolders(SignCommand request)
+ {
+ var placeHolders = new Dictionary()
+ {
+ { "[NAME_RECEIVER]", request.EnvelopeReceiver.Name ?? string.Empty },
+ { "[DOCUMENT_TITLE]", request.EnvelopeReceiver.Envelope?.Title ?? string.Empty },
+ };
+
+ if (request.EnvelopeReceiver.Envelope.IsReadAndConfirm())
+ {
+ placeHolders["[SIGNATURE_TYPE]"] = "Lesen und bestätigen";
+ placeHolders["[DOCUMENT_PROCESS]"] = string.Empty;
+ placeHolders["[FINAL_STATUS]"] = "Lesebestätigung";
+ placeHolders["[FINAL_ACTION]"] = "Empfänger bestätigt";
+ placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Empfänger abgelehnt!";
+ placeHolders["[RECEIVER_ACTION]"] = "bestätigt";
+ }
+ else
+ {
+ placeHolders["[SIGNATURE_TYPE]"] = "Signieren";
+ placeHolders["[DOCUMENT_PROCESS]"] = " und elektronisch unterschreiben";
+ placeHolders["[FINAL_STATUS]"] = "Signatur";
+ placeHolders["[FINAL_ACTION]"] = "Vertragspartner unterzeichnet";
+ placeHolders["[REJECTED_BY_OTHERS]"] = "anderen Vertragspartner abgelehnt! Ihre notwendige Unterzeichnung wurde verworfen.";
+ placeHolders["[RECEIVER_ACTION]"] = "unterschrieben";
+ }
+
+ return placeHolders;
+ }
+
+ private static string ReplacePlaceHolders(string text, params Dictionary[] placeHoldersList)
+ {
+ foreach (var placeHolders in placeHoldersList)
+ foreach (var ph in placeHolders)
+ text = text.Replace(ph.Key, ph.Value);
+ return text;
+ }
+}