Skip to content

Mailer TCP API

The TCP transport addresses a module method by command name. It is the entry point for server-to-server integrations: CRM backends, notification services, statement jobs, campaign schedulers.

The same methods are available over HTTP — see REST API. The command name and the HTTP path are two addresses of one method, with identical fields and results.

Request envelope

{
  "command": "MailerSendEmail",
  "extID": "8f2a1c",
  "data": {
    "sendToParentType": "CUSTOMER",
    "sendToParentId": 140,
    "to": "[email protected]",
    "templateId": 4,
    "data": { "name": "John", "amount": "250 EUR" }
  }
}
Field Type Description
command string Method name, exactly as published by the module
extID string Caller-generated correlation id; the response carries it back
data object Method parameters

extID is what makes the connection full-duplex: responses and streamed events share one socket, so a reply is matched to its request by extID rather than by arrival order.

Response

{
  "extID": "8f2a1c",
  "status": 200,
  "data": {
    "emailId": 812,
    "status": "PENDING",
    "scheduleReason": "IMMEDIATE",
    "idempotent": false
  }
}

An error keeps the same envelope: the outcome is in status, and data carries the message.

{
  "extID": "8f2a1c",
  "status": 403,
  "data": "Address [email protected] is suppressed: HARD_BOUNCE"
}

Status codes are the same as in REST. 502 means the module itself is not on the bus — the platform answers instead of hanging the request. A malformed request never reaches the module and comes back as 400 with {"error": "NO_COMMAND_OR_EXT_ID"} or {"error": "INVALID_JSON_OBJECT"}.

MailerSendEmail answers as soon as the email is queued, not when it is delivered. The delivery outcome arrives later as an event.

Node.js SDK

scaletrade-server-api exposes any published command as a method, so no client update is needed when a module adds methods:

const STPlatform = require('scaletrade-server-api');

const platform = new STPlatform(
  'broker.scaletrade.com:8080',
  'notification-service',
  {},
  null, null,
  'your-jwt-auth-token'
);

const mail = await platform.MailerSendEmail({
  sendToParentType: 'CUSTOMER',
  sendToParentId: 140,
  to: '[email protected]',
  templateId: 4,
  data: { name: 'John', amount: '250 EUR' },
  kind: 'TRANSACTIONAL',
  idempotencyKey: 'deposit-140-1755000000'
});

if (mail.status === 200) {
  console.log('queued', mail.data.emailId);
} else {
  // On failure `data` is the message text, not an object.
  console.error(mail.status, mail.data);
}

idempotencyKey matters for exactly this caller: a retried request or a restarted job must not send the customer a second confirmation email. Repeating the call with the same key returns the original email with idempotent: true.

Commands

Emails

Command Description
MailerSendEmail Queue one email
MailerSendBulk Queue up to 10 000 recipients
MailerResendEmail Send a copy of an existing email
MailerCancelEmail Cancel an email still in the queue
MailerPreviewEmail Assemble the message without sending
MailerLintEmail Pre-send warnings
MailerSendTestEmail Send a test to your own address
MailerFindEmail Find emails by address, provider id or campaign
MailerGetEmail One email with attempts and events
MailerGetEmails Email history with filters and aggregates
MailerGetStats Delivery, bounce, complaint, open and click rates
MailerGetLastEmailsByParents Last email time for a batch of recipients

Campaigns

Command Description
MailerAddCampaign Create a campaign over a CRM segment
MailerUpdateCampaign Update a campaign
MailerCancelCampaign Stop a campaign and its queued emails
MailerResumeCampaign Continue an interrupted campaign
MailerEstimateCampaignRecipients Count recipients before the rollout
MailerDeleteCampaign Delete a campaign
MailerGetCampaigns Campaigns with rollout progress

Templates and triggers

Command Description
MailerAddTemplate Create a template
MailerUpdateTemplate Update a template
MailerCloneTemplate Copy a template
MailerDeleteTemplate Delete a template
MailerGetTemplates Templates of the brand
MailerAddTrigger Subscribe an event to templates
MailerUpdateTrigger Update a trigger
MailerDeleteTrigger Delete a trigger
MailerFireTrigger Fire an event by hand
MailerGetTriggers Triggers of the brand
MailerGetTriggerSubscriptions What the module listens to

Providers, profiles and agents

Command Description
MailerGetProviderAdapters Implemented adapters and capabilities
MailerAddProvider Register a provider
MailerUpdateProvider Update a provider
MailerDeleteProvider Soft-delete a provider
MailerGetProviders Provider catalogue
MailerAddProfile Create a provider profile
MailerUpdateProfile Update a provider profile
MailerDeleteProfile Soft-delete a provider profile
MailerGetProfiles Profiles with domain and pause state
MailerGetMyProfiles Profiles available to the current manager
MailerGetProfileBalance Provider quota and low-balance flags
MailerVerifyProfileDomain DKIM and SPF state of the sender domain
MailerAddAgent Link a manager to a sender address
MailerUpdateAgent Update the link
MailerDeleteAgent Remove the link
MailerGetAgents Links of the brand

Suppressions, send windows, webhooks, service

Command Description
MailerAddSuppression Suppress an address
MailerImportSuppressions Import a suppression list
MailerDeleteSuppression Remove a suppression
MailerCheckSuppression Check an address before sending
MailerGetSuppressions Suppression entries
MailerHandleUnsubscribe Public one-click unsubscribe (HTTP in practice)
MailerAddSendWindow Add a send window
MailerUpdateSendWindow Update a send window
MailerDeleteSendWindow Delete a send window
MailerCheckSendWindow Whether a country can be emailed now
MailerGetSendWindows Send windows
MailerAddWebhook Create a provider event URL
MailerUpdateWebhook Update a webhook
MailerDeleteWebhook Delete a webhook
MailerGetWebhooks Webhooks of the brand
MailerGetWebhookEvents Raw provider events
MailerHandleWebhook Public provider endpoint (HTTP in practice)
MailerPing Liveness probe with a problem list
MailerHealth Full module state

Events

Email progress is delivered as events on the same connection, so an integration does not poll MailerGetEmails:

Event When
mailer.email.queued Email accepted into the queue
mailer.email.sent Provider accepted the email
mailer.email.final Delivered, bounced, complained, rejected, expired or failed
mailer.email.engagement Open or click recorded
mailer.recipient.unsubscribed Recipient unsubscribed
mailer.profile.paused Profile paused by the reputation guard
platform.emitter.on('mailer.email.final', e => {
  console.log(e.data.emailId, e.data.status, e.data.bounceType);
});

platform.emitter.on('mailer.profile.paused', e => {
  console.error('sending paused:', e.data.profileId, e.data.reason);
});

Two of these deserve a subscriber in any serious integration. mailer.email.final with status: "BOUNCED" is how a CRM learns that an address is dead. mailer.profile.paused means the module stopped sending through a profile because its bounce or complaint rate crossed the threshold — nobody else will notice that the confirmation emails stopped going out.

An email whose provider never reports a final event is closed by the module as EXPIRED, so the event always arrives and a waiting integration cannot hang forever.

Monitoring the module

MailerPing is the probe to call from a monitor: it answers ok plus a problems list and costs one SELECT 1. A process that answers on the bus can still have lost its database or its queue cron, and in that state emails pile up in PENDING while nothing is sent — an empty problems list is what actually means healthy.

MailerHealth adds what a probe cannot: the queue depth, the per-profile bounce and complaint rates, and how many emails sit in SENT with no delivery event.