Skip to main content
Version: 0.9.17

SMS delivery operations

Delphi sends SMS messages through seven.io. A flow action enqueues an SMS_SEND job; the Tasker worker performs the authoritative feature and quota checks, dispatches the message, and records the provider result and usage.

Delivery path

  1. A voice or browser flow invokes sendSMS with a recipient, message text, and optional sender.
  2. The invoking application validates the request and enqueues one SMS_SEND job.
  3. Tasker checks the sms feature flag, estimates the billable message parts, and verifies the team's current SMS allowance and spending cap.
  4. Tasker sends the message to https://gateway.seven.io/api/sms through the deployment's configured HTTPS proxy.
  5. After seven.io accepts the message, Tasker saves a durable delivery checkpoint before updating subscription usage. A retry resumes accounting from that checkpoint instead of sending again.

Configuration

VariableSourceConsumerRequired when SMS is enabled
FEATURE_SMSFeature flagPlatform servicesSet to true.
SMS_API_KEYSecrets ManagerTasker workerYes.

These are the only SMS-specific deployment variables. The provider name is deliberately absent from the variable names so the deployment contract remains provider-neutral. There is no provider-specific sender variable: a flow may supply from, otherwise Delphi uses its validated default sender.

Before enabling SMS:

  • create and fund the seven.io account;
  • store its API key as SMS_API_KEY in the Ops secret;
  • allow HTTPS egress to gateway.seven.io:443 through Squid; and
  • enable the sms feature flag, then restart the affected application and Ops containers so they re-read their environment.

If SMS is disabled, the platform does not require SMS_API_KEY. Upstream services skip new dispatches and Tasker refuses SMS jobs that were already queued.

Multipart messages and limits

Quota enforcement is based on estimated parts, not action calls. Before provider dispatch, Tasker applies the following encoding limits:

EncodingSingle partEach concatenated part
GSM-7160 septets153 septets
UCS-270 units67 units

GSM-7 extension-table characters consume two septets. After acceptance, the provider-reported part count is recorded as the actual usage. This prevents a multipart message from bypassing the team's allowance or hard spending cap.

Team administrators can inspect current SMS usage and change applicable limits in My Subscription. The Dashboard shows current usage. Usage and overage notifications link to both surfaces; when subscription management is unavailable, the unavailable action is hidden.

Retry and rejection behavior

  • Tasker passes the job ID to seven.io as correlation metadata and disables seven.io's account-wide repeated-content lock. Distinct jobs with the same recipient and text remain valid.
  • Network failures, timeouts, HTTP 429, and server errors are retryable through the normal Tasker job policy.
  • Permanent provider rejections complete as rejected and are not retried.
  • A provider acceptance checkpoint is scoped to the original Tasker job. Do not create a new job merely because usage accounting failed; inspect the original job result first to avoid a second paid delivery.

Automated regression checks

From the application repository root:

pnpm --filter @delphi/notification exec vitest run src/sms/segmentation.test.ts src/transport/sms.test.ts
pnpm --filter @delphi/tasker exec vitest run src/services/handlers/__tests__/sms-send.test.ts \
src/services/worker/__tests__/sms-worker.integration.test.ts

The first command covers encoding, multipart estimation, seven.io request/response handling, and provider errors. The second covers quota enforcement, durable retry checkpoints, provider rejections, and usage accounting.

Controlled paid end-to-end test

The repository contains a local-only setup script that creates and publishes a dedicated Seven SMS E2E app without changing an existing phone or SIP endpoint.

  1. Confirm FEATURE_SMS=true, SMS_API_KEY is populated, the test team has SMS enabled, and its subscription has available SMS usage or permitted overage.

  2. From the application repository root, run:

    NODE_ENV=development ALLOW_PAID_SMS_E2E=true \
    SMS_E2E_TEAM_ID=<team-id> SMS_E2E_SOURCE_APP_ID=<source-app-id> \
    pnpm with-env pnpm --filter @delphi/api exec tsx scripts/setup-seven-sms-e2e-flow.ts
  3. Check the command's smsReady output. featureEnabled, apiKeyConfigured, and teamEnabled must be true; also review the subscription status and usage values.

  4. Sign in as a team administrator, open Apps → Seven SMS E2E, and start its browser test call.

  5. Give one real recipient in E.164 format, repeat the complete number, and explicitly authorize one paid SMS. Do not repeat the authorization or trigger the action twice.

  6. Confirm the flow reports one queued job ID, the recipient receives the uniquely timestamped message, and the Tasker job completes with success, messageId, providerCode, parts, and price fields.

  7. Verify the SMS count in Dashboard and My Subscription advances by the reported number of parts.

This sends a real paid SMS

Use a recipient that has consented to the test. Keep ALLOW_PAID_SMS_E2E scoped to the setup command and remove the temporary app after testing if it is no longer needed.

Troubleshooting

SymptomCheck
Job says SMS is disabledFEATURE_SMS in the affected services and whether Tasker restarted after the flag change.
Job fails before provider dispatchTeam SMS setting, estimated parts, included allowance, spending cap, and subscription status.
Provider authentication is rejectedSMS_API_KEY in the Ops secret; re-run Ops init and restart Tasker after rotation.
Timeout or network errorTasker proxy variables and Squid access to gateway.seven.io:443.
SMS arrived but usage update was retriedInspect the original job checkpoint; Tasker should resume accounting without another send.
Usage action opens the wrong settings tabConfirm the subscriptionManagement flag and use a current notification template.

See Rotate secrets for API-key rotation and rollback.