Email & MJML

Inklura's email stack has three moving parts: an MJML composer that produces email-safe HTML, a Mautic bridge that actually delivers it, and two tRPC routers on the company-manager API for campaign management (campaigns) and reporting (emailAnalytics).

The MJML composer

The mail designers (maildesigner.inklura.fr / mail.inklura.fr) build email-safe HTML with an MJML mirror kept alongside it for editing. An AI builder chat streams its output over Server-Sent Events:

GET /api/builder/stream/<chatId>

The composer's job is producing the HTML; sending it is a separate step.

Delivery — the Mautic bridge

Email is not sent directly. Delivery is handed to a self-hosted Mautic instance at infra.wd29.net via a bridge with two endpoints. The bridge secret is server-side only — never call these from the browser.

Enqueue a message for delivery:

POST https://infra.wd29.net/api/mautic/queue/enqueue
Content-Type: application/json

{
  "html": "<…rendered email HTML…>",
  "subject": "…",
  "tag": "…",
  "slugs": ["…"],
  "tenantId": "…",
  "userId": "…",
  "scheduledFor": "…",
  "segmentId": "…"
}
Field Required Notes
html yes The rendered, email-safe HTML.
subject yes Subject line.
tag yes Campaign/segment tag.
slugs yes Target slug list.
tenantId no Tenant scope.
userId no Actor.
scheduledFor no Deferred send time.
segmentId no Mautic segment.

Check the status of an enqueued item:

POST https://infra.wd29.net/api/mautic/queue/status
Content-Type: application/json

{ "id": "<queue-item-id>" }

Campaign CRUD & send — the campaigns router

Campaign lifecycle lives in the campaigns tRPC router. Call it at /api/trpc/campaigns.<procedure> using the standard tRPC conventions:

Procedure Purpose
getCampaigns List campaigns.
getCampaignById Fetch one campaign.
createCampaign Create a campaign.
createCampaignWizard Create via the guided wizard flow.
updateCampaign Update a campaign.
deleteCampaign Delete a campaign.
sendCampaign Send a campaign.
pauseCampaign Pause a sending campaign.
resumeCampaign Resume a paused campaign.
duplicateCampaign Clone a campaign.
getCampaignActivity Read campaign activity.
getCampaignTags List campaign tags.
createCampaignTag Create a campaign tag.

Analytics — the emailAnalytics router

Reporting is a separate router, emailAnalytics. Every procedure is a permissionProtectedProcedure(["analytics:read"]), so the caller needs a session holding the analytics:read permission (see tRPC Procedures).

Procedure Returns
getDashboardMetrics Headline dashboard metrics.
getCampaignAnalytics Per-campaign analytics.
getCampaignRevenue Revenue attributed to a campaign.
getTopCampaigns Best-performing campaigns.
getEngagementTrends Engagement over time.
getProviderPerformance Delivery-provider performance.
exportAnalytics Export analytics data.
getUnsubscribeAnalytics Unsubscribe metrics.
getUnsubscribeFunnel Unsubscribe funnel.
getHealthStatus Analytics pipeline health.
Note

These procedures delegate to an external analytics HTTP service at QUEUE_MANAGER_URL (default http://localhost:8085). When that service is unreachable, the router degrades gracefully rather than failing the call — so don't treat an empty or fallback response as a hard error.

Open & click tracking

The platform serves a tracking pixel and a click-redirect that proxy to the same analytics service:

${QUEUE_MANAGER_URL}/track/email/<id>/open
${QUEUE_MANAGER_URL}/track/email/<id>/click

An open is recorded when the pixel loads; a click is recorded as the redirect passes the reader through to the destination.

Related