HTTP API
The public routes on Mursil.Server.
Mursil.Server exposes the product API under /api/v1/mursil. In Development it also maps an OpenAPI document and a Scalar reference UI. The .NET client of the send, push, and status subset is Mursil.Integration.
Health for the process is GET /health.
Authentication
Send a bearer token whose audience is mursil-api. Scopes:
| Scope | Allows |
|---|---|
mursil.messages.send | Send, batch send, cancel, status, engagement, and inbound replies. |
mursil.templates.manage | Template routes, preview, lint, test send, and brand profiles. |
A portal role is an alternative to the matching scope. The claim type is role.
| Policy | Roles |
|---|---|
| Viewer | SystemAdmin, TenantOwner, TenantAdmin, TemplateEditor, Operator, Viewer |
| Template management | SystemAdmin, TenantOwner, TenantAdmin, TemplateEditor, plus the templates scope |
| Operators | SystemAdmin, TenantOwner, TenantAdmin, Operator |
| Tenant owner | SystemAdmin, TenantOwner |
Application routes also require scope headers:
| Header | When |
|---|---|
X-Mursil-ApplicationId | Always, unless the token already carries the application id. |
X-Mursil-ClientId | When the token has no client_id, azp, or sub. |
X-Mursil-TenantId | Only when Mursil:MultiTenancy is true, and the token has no tenant_id. |
While multi-tenancy is false the server ignores a caller-supplied tenant id.
Missing scope returns 400 with code HeadersRequired.
Send and status
| Method | Path | Auth | Purpose |
|---|---|---|---|
POST | /messages/email | send | Accept one template email. Returns 202 and messageRequestId, state, correlationId. |
POST | /messages/email/batch | send | Accept many recipients for one template key. |
POST | /messages/email/test | templates | Test send from the portal or a template tool. |
GET | /messages/status | send | Look up by messageRequestId, idempotencyKey, or correlationId. |
POST | /messages/{messageRequestId}/cancel | send | Cancel a message that has not been dispatched. |
POST | /messages/{messageRequestId}/engagement | send | Record an engagement event from a trusted caller. |
POST | /messages/replies | send | Record an inbound reply. |
The send body is documented in First send. Status returns messageRequestId, state, templateKey, templateVersionId, providerKind, and failure.
Templates
All of these require template management.
| Method | Path | Purpose |
|---|---|---|
GET | /templates | List templates. Query page, pageSize. |
POST | /templates | Create a template. |
GET | /templates/{templateId} | Read one template, its variables, and its versions. |
POST | /templates/push | Upsert a draft from templateKey, locale, subject, html, text, and modelSchema. |
POST | /templates/lint | Lint a body without saving. |
POST | /templates/preview | Render a body against a model without sending. |
POST | /templates/catalog/import | Import the starter catalog. |
GET | /templates/{templateId}/bundle | Export a bundle. |
POST | /templates/bundles | Import a bundle. |
GET | /templates/{templateId}/diff | Compare leftVersion and rightVersion. |
GET | /templates/{templateId}/locale-coverage | Locales present on the template. |
POST | /templates/{templateId}/locale-preview | Render several locales. |
POST | /templates/{templateId}/versions | Add a version. |
POST | /templates/{templateId}/versions/{versionId}/submit-review | Move a draft to review. |
POST | /templates/{templateId}/versions/{versionId}/approve | Approve a version in review. |
POST | /templates/{templateId}/versions/{versionId}/publish | Publish. |
POST | /templates/{templateId}/versions/{versionId}/pause | Pause a published version. |
POST | /templates/{templateId}/versions/{versionId}/resume | Resume a paused version. |
PUT | /templates/{templateId}/versions/{versionId}/branding | Bind branding on that version. |
Brand profiles
Template-management authorization.
| Method | Path | Purpose |
|---|---|---|
GET | /brand-profiles | List profiles. |
POST | /brand-profiles | Create. Name, from-name, and theme JSON are required. |
PUT | /brand-profiles/{brandProfileId} | Update. |
POST | /brand-profiles/{brandProfileId}/activate | Make the profile active. |
Providers and sender domains
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET | /providers | operators | List connections. |
GET | /providers/kinds | operators | Selectable kind names. |
POST | /providers | tenant owner | Create. Body is name, kind, settingsSecretReference, and optional webhookSigningSecretReference. |
POST | /providers/{providerConnectionId}/pause | operators | Pause. |
POST | /providers/{providerConnectionId}/resume | operators | Resume. |
POST | /providers/{providerConnectionId}/routing | operators | Set optional purpose and region. |
POST | /providers/{providerConnectionId}/events | signed, anonymous OIDC | Ingest a provider callback. Headers X-Mursil-Provider-Signature and X-Mursil-Provider-Timestamp. |
GET | /sender-domains | operators | List domains. |
GET | /sender-domains/{senderDomainId} | operators | Read one domain. |
POST | /sender-domains | operators | Create from { "domainName": "mail.example.com" }. |
GET | /sender-domains/{senderDomainId}/lookup | operators | DNS lookup. |
POST | /sender-domains/{senderDomainId}/verify | operators | Verify. Body is { "actor": "<operator>" }. |
Operations, applications, and recipients
| Method | Path | Auth | Purpose |
|---|---|---|---|
GET | /dashboard | viewer | Dashboard figures. |
GET | /dashboard/watchlist | viewer | Watch list. |
GET | /dashboard/operator-action-queue | viewer | Items waiting on an operator. |
GET | /dashboard/delivery-health | viewer | Delivery health. |
GET | /dashboard/recipient-domain-failures | viewer | Failures grouped by domain. |
GET | /messages/deliveries/triage | operators | Failure and triage list. |
GET | /messages/deliveries/{deliveryId} | operators | One delivery. |
POST | /messages/deliveries/{deliveryId}/retry | operators | Retry. |
POST | /messages/deliveries/{deliveryId}/suppress | operators | Suppress. |
POST | /messages/deliveries/{deliveryId}/resolve | operators | Resolve. |
GET | /audit | operators | Audit records. page, pageSize. |
POST | /retention/rendered-content/purge | operators | Purge stored rendered bodies. |
GET | /applications/controls | operators | Sandbox, rate limit, default provider. |
POST | /applications | tenant owner | Create an application. |
POST | /applications/sandbox | operators | Set sandbox. |
POST | /applications/rate-limit | operators | Set sends per minute. |
POST | /applications/default-provider | operators | Set the default connection. |
POST | /tenants/engagement-tracking | tenant owner | Enable or disable open and click tracking. |
GET | /suppressions/export | operators | Export suppression rows. |
POST | /suppressions/import | operators | Import { scope, reason, recipientHash } rows. |
Anonymous recipient routes
These sit outside the /api/v1/mursil prefix, except one-click unsubscribe.
| Method | Path | Purpose |
|---|---|---|
GET | /t/o/{token} | Open pixel. Returns a transparent GIF. |
GET | /t/c/{token} | Click redirect. |
GET | /p/{token} | Preference center page. |
POST | /p/{token} | Preference center form. |
POST | /api/v1/mursil/preferences/one-click | One-click unsubscribe. |
Tokens are signed with Mursil:Tracking. They are not a tenant id and not a session.