Mursil Documentation

Sending mail

Integration is POST /api/v1/mursil/messages/email and the Mursil.Integration package. Production send uses a published template version. A draft is not live.

Request

POST /api/v1/mursil/messages/email
Authorization: Bearer <access-token>
X-Mursil-ApplicationId: 22222222-2222-4222-8222-222222222222
X-Mursil-ClientId: mursil-platform-service
Content-Type: application/json

{
  "templateKey": "mursil.identity.password-reset",
  "recipient": {
    "email": "user@example.com",
    "displayName": "Ayesha"
  },
  "locale": "en",
  "model": {
    "userName": "Ayesha",
    "resetUrl": "https://identity.example.com/reset?token=example",
    "expiresAt": "2026-05-15T12:00:00Z"
  },
  "idempotencyKey": "password-reset:user-123:2026-05-15T11:45Z",
  "correlationId": "auth-flow-abc123"
}
FieldRequiredMeaning
templateKeyyesPublished template key.
recipient.emailyesRecipient. Status responses do not echo it.
recipient.displayNamenoDisplay name.
localenoRequested locale.
modelyesKeys match the published schema.
idempotencyKeyyesOne delivery per key.
correlationIdnoCaller correlation for status lookup.
notBeforenoUTC time. The message stays accepted until then.
attachmentsnoFile name, content type, and base64 bytes. Oversize or disallowed types are rejected before queueing.
allowLocaleFallbacknoDefault true. False returns not-found when that locale is unpublished.
providerConnectionIdnoOptional provider guid. Empty keeps the router order.

The API accepts the request and returns a message request id. It does not wait for the provider. A successful repeat of the same idempotency key returns the existing request and does not create a second delivery. The same key with a different payload is a conflict.

The first-party seed publishes mursil.email.raw-fallback with model fields subject and body. The same seed includes the Kayan identity keys (password-reset, email-verification, tenant-invitation, security-alert, passkey-alert, recovery). High-risk keys still need review before publish.

Status

GET /api/v1/mursil/messages/status?idempotencyKey=password-reset:user-123:2026-05-15T11:45Z

Pass the application and client headers, and exactly one of messageRequestId, idempotencyKey, or correlationId. Add X-Mursil-TenantId only when multi-tenancy is on.

MursilMessageStatusDto includes message request id, state, template key, template version id, provider kind, and a redacted failure. It has no rendered body and no raw recipient.

Method and pathPurpose
POST /api/v1/mursil/messages/email/batchBatch template send.
POST /api/v1/mursil/messages/email/testTest-send.
POST /api/v1/mursil/messages/{messageRequestId}/cancelCancel a scheduled message.
POST /api/v1/mursil/templates/previewRender a preview.
POST /api/v1/mursil/templates/lintLint template content.

The full route index is on the Reference page. Public recipient routes are separate: /p/{token} for the preference center, /t/o/{token} for the open pixel, and /t/c/{token} for signed click redirects.

NuGet package

Package ids are Mursil.Integration and Mursil.Contracts, version 0.1.0-preview.1, on https://nuget.pkg.github.com/imabdul-dev/index.json.

AddMursilIntegration registers IEmailEngine, ICommunicationQueue, and ITransactionalEmailSender. SendAsync(TemplateEmailRequest) and EnqueueEmailAsync(QueuedEmailRequest) post the template key, locale, model, idempotency key, correlation id, and optional provider connection id. The queue returns Result<Guid>, the accepted message request id.

SendAsync(EmailRequest) with an empty template key sends RawEmailFallbackTemplateKey using subject and body. IsHtml is not a separate HTTP field. Older SMS queue methods return not-supported and do not call HTTP.

The package targets CleanArch Pro 0.1.0-preview.10. NuGet will not replace the existing 0.1.0-preview.1 package. This tree did not publish a newer version. DefaultTenantId is not required at startup. The package sends a tenant id only when Mursil:MultiTenancy is true.