SmsAddTrigger¶
POST sms/trigger
Subscribes a platform event to templates: when the event fires on the bus, the module renders every enabled template of this trigger and queues the messages.
merge renames event fields into placeholder names. Without it every event schema change
would require editing the templates instead of one trigger.
arguments decides whether this trigger reacts to a given event at all — see Matching below.
Access Control¶
Allowed role: admin. The trigger belongs to the brand of the caller unless global is set.
Request¶
{
"event": "customer.password_reset.delivery_requested",
"merge": {
"client.msisdn": "phone"
},
"arguments": {
"match": { "delivery_route": "customer-recovery" }
},
"type": "PRIVATE",
"status": "ENABLED"
}
{
"command": "SmsAddTrigger",
"extID": "1",
"data": {
"event": "customer.password_reset.delivery_requested",
"merge": {
"client.msisdn": "phone"
},
"arguments": {
"match": { "delivery_route": "customer-recovery" }
},
"type": "PRIVATE",
"status": "ENABLED"
}
}
const res = await platform.SmsAddTrigger({
event: "customer.password_reset.delivery_requested",
merge: {
"client.msisdn": "phone"
},
arguments: {
match: { delivery_route: "customer-recovery" }
},
type: "PRIVATE",
status: "ENABLED"
});
Request Data¶
| Field | Type | Required | Description |
|---|---|---|---|
event |
string | Yes | Bus event name to subscribe to, for example crm.customer.deposit |
description |
string | No | Free-form note |
merge |
object | No | Rename event fields for templates, { "client.msisdn": "phone" } |
arguments |
object | No | Firing condition, { "match": { "delivery_route": "customer-recovery" } } — see Matching |
type |
enum | No | PRIVATE (default) or PUBLIC |
status |
enum | No | ENABLED (default) or DISABLED |
global |
bool | No | true — the trigger works for every brand |
Matching¶
One platform event reaches every subscriber: the delivery channel is a field of the
payload (delivery_route), not the number of the receiver. arguments is how a trigger
claims only the events meant for it:
{ "match": { "delivery_route": "customer-recovery" } }
- the value is one string or a list of allowed values;
- the key is a path into the payload, so
{ "customer.brand": "sct" }works; - comparison is done on strings, so
1and"1"are the same value; - a field missing from the event does not match;
- all listed fields must match;
- without
argumentsthe trigger fires on every event with this name.
The same mechanism fans a single event out to several channels: the mailer subscribes to the same event with its own trigger, and each module delivers in its own channel. The platform cannot split channels by emitting two events — one request emits exactly one.
Behavior¶
- The module subscribes to the exact event names of its enabled triggers, and the subscription is what the platform reads from Moleculer INFO before it sends anything. A trigger that exists in the database but is not live yet receives nothing.
- Subscriptions are refreshed from the database by a cron, so a new trigger starts working without restarting the module. SmsGetTriggerSubscriptions shows what is live right now.
- Repeated delivery of one event does not send two messages: the deduplication key uses
event_id,request_idoridempotencyKeyfrom the payload, and falls back to the recipient number withindedupeWindowMinwhen the event carries none. - The event payload must resolve to a phone number after
merge; an event without one is counted as an error rather than sent to a blank recipient. - The recipient may also come from enrichment: an event that carries only a
customer_idor aloginis resolved against the platform before templates are picked, and the account, customer and margin values it returns become placeholders. See Platform event enrichment. mergeis applied before and after enrichment, so it renames both the fields of the event and the fields added from the platform (approved_time_utc→value_datetime_utc).- Service events are never subscribed to: an event whose name starts with
$,journal.,analytics.orsms.(the events of this module itself, which would loop) is accepted by this method but stays out of the subscription. SmsGetTriggerSubscriptions shows it assubscribed: false. - A
globaltrigger fires for every brand — use it for platform-wide notifications, not for brand campaigns.
Response Data¶
{
"id": 2,
"brand": "default",
"event": "crm.customer.deposit",
"description": null,
"merge": { "client.msisdn": "phone", "payment.sum": "amount" },
"arguments": null,
"type": "PRIVATE",
"global": false,
"status": "ENABLED",
"createdAt": "2026-08-24 13:18:02"
}
Errors¶
| Code | Error | Description |
|---|---|---|
400 |
VALIDATION_ERROR |
Missing event name |
500 |
INTERNAL_ERROR |
Storage error |