A send does not embed HTML from the caller. The caller names a published template, a recipient, and a model. Mursil accepts the work and dispatches it after the HTTP call returns.

Before you send

  • The template key exists and has a published version for the locale you send.
  • The model matches that version’s schema.
  • The application has a provider connection. On a local compose stack the default is Local SMTP, which delivers to Mailpit.
  • You have an access token for the API audience mursil-api.

With Mursil:MultiTenancy left at false, do not send a tenant id.

Request

POST /api/v1/mursil/messages/email

  Authorization: Bearer <access-token>
X-Mursil-ApplicationId: <application-guid>
X-Mursil-ClientId: <oauth-client-id>
Content-Type: application/json
  

X-Mursil-ClientId is required when the token does not already identify the client (client_id, azp, or sub). When Mursil:MultiTenancy is true, also send X-Mursil-TenantId unless the token already carries tenant_id.

  {
  "templateKey": "orders.shipped",
  "recipient": {
    "email": "ada@example.com",
    "displayName": "Ada"
  },
  "locale": "en",
  "model": {
    "orderNumber": "1842",
    "trackingUrl": "https://example.com/track/1842"
  },
  "idempotencyKey": "orders-1842-shipped",
  "correlationId": "orders-1842",
  "allowLocaleFallback": true
}
  
FieldRequiredMeaning
templateKeyyesStable key. Callers do not send a version id.
recipient.emailyesDestination address.
localenoLocale of the version to render. Fallback is allowed unless you set allowLocaleFallback to false.
modelyesJSON object checked against the published version’s schema.
idempotencyKeyyesRepeating the same key does not create a second send.
correlationIdnoYour identifier for later status lookup.
notBeforenoDelay dispatch until this timestamp.
providerConnectionIdnoUse one connection instead of the application default.
attachmentsnoFile name, content type, and base64 content.

A successful call returns 202 Accepted with messageRequestId, state, and correlationId. Accepted means Mursil stored the request. It does not mean the recipient’s mail server has accepted the message.

See it locally

Open Mailpit at http://localhost:18025/. The local SMTP connection captures the rendered message there. That capture is the local stand-in for delivery. It is not inbox placement.

If the application is in sandbox mode, the request state is Sandbox and the message is not handed to a real provider. Sandbox is an application control on Settings.

Read status

GET /api/v1/mursil/messages/status

Pass one of messageRequestId, idempotencyKey, or correlationId. The same application headers are required. The response includes the request state, template key, template version, provider kind, and a failure string when there is one.

Request states are Received, Accepted, Rejected, Suppressed, and Sandbox. Delivery continues on its own timeline: queued, delivering, sent, retry scheduled, failed, dead-lettered, suppressed, cancelled, resolved, or captured.

Send from another .NET host

Reference the Mursil.Integration package and call AddMursilIntegration. The package posts to the same route. Set Mursil:Integration from configuration:

KeyPurpose
BaseAddressServer origin, for example http://localhost:8088.
AuthorityToken issuer.
ClientId, ClientSecretClient credentials. Store the secret outside source.
ScopesInclude mursil.messages.send for send.
ApplicationId, ApplicationKeyThe Mursil application this host sends as.
DefaultTenantIdUsed only when Mursil:MultiTenancy is true.
RawEmailFallbackTemplateKeyTemplate used when a caller submits raw content through the integration queue.

The package requests a token from {Authority}/connect/token. It adds a tenant to that token request only when multi-tenancy is enabled.

Several recipients

POST /api/v1/mursil/messages/email/batch accepts one template key and a list of recipients. Each recipient has its own model and idempotency key. The response lists one acceptance per recipient.

A portal test send uses POST /api/v1/mursil/messages/email/test and requires a template-management role or the mursil.templates.manage scope. Use it to check a draft render path. Production traffic uses the send route above.