Skip to Content

SMS

The SMS tool (ctx.SMS) is a built-in tool available inside any loop. It queues text messages with SMS Portal  and returns the portal event id for that send.

What this tool is for

  • Texting a member a link, a reminder, or a short result.
  • Sending the same content to many numbers in one step (up to 500).

What this tool is not for

  • Not delivery confirmation. eventId means SMS Portal queued the message. It does not mean the handset received it.
  • Not a conversation. Replies are not delivered back into the loop.

Credentials

Set both under the loop’s Variables and Secrets, or on the worker. Store the API secret as a secret. A complete loop account wins; otherwise the worker account is used. The platform reads them when the loop sends — loop code does not pass them to ctx.SMS.send.

NameWhat it is
SMS_PORTAL_CLIENT_IDSMS Portal client id (Basic auth username)
SMS_PORTAL_API_SECRETSMS Portal API secret (Basic auth password)

Tool API

Must be called inside a step.do callback — ctx only exists there. See Step context.

type SmsSendInput = { /** Single recipient, an MSISDN such as "27821234567". */ to?: string; content?: string; customerId?: string; /** Batch. Use this or `to`/`content`, not both. At most 500. */ messages?: Array<{ destination: string; content?: string; customerId?: string; }>; options?: { senderId?: string; testMode?: boolean; checkOptOuts?: boolean; shortenUrls?: boolean; allowContentTrimming?: boolean; campaignName?: string; costCentre?: string; validityPeriod?: number; startDeliveryUtc?: string; endDeliveryUtc?: string; }; }; type SmsTool = { send(input: SmsSendInput): Promise<{ eventId: string }>; };

send throws when credentials are missing, the portal rejects the request, or it queues zero messages (an invalid number, an opt-out, or another fault). Defaults: content trimming on, opt-out checks on, URL shortening off. Pass options to override those, or testMode: true to exercise the call without sending.

Example

import { Workflow } from "./loop"; export class HealthLoop extends Workflow { async run(event, step) { const { eventId } = await step.do("Text the member", async ({ ctx }) => { return ctx.SMS.send({ to: event.body.phone, content: "Your scan results are ready.", }); }); return { eventId }; } }
Last updated on