SMS and messaging

Twilio integration

Ahena works with your Twilio account through an API key: it catches trial accounts, suspended accounts and test credentials before production, creates the messaging service your app sends through, adds numbers you already own to its sender pool, points its inbound and status webhooks at your route, and reports A2P 10DLC registration. Your app sends straight to Twilio with its own key through a small generated sms.send adapter. Ahena never sends a message or reads message content.

What Ahena inspects

Read-only: inspecting and Doctor never change anything at Twilio.

  • The account's status (active, suspended, closed) and type (trial or upgraded), when the key may read it. The Auth Token in Twilio's response is dropped, never returned.
  • Messaging services: name, use case, inbound request URL, status callback, and whether they defer to each number's webhook. Credentials and query values in webhook URLs are hidden.
  • Each service's sender pool (phone numbers, short codes, alphanumeric sender IDs) and SMS capability.
  • The account's own phone numbers and their SMS webhook settings.
  • A2P 10DLC campaign and brand status for US long codes (never the message samples).
  • Whether your webhook route rejects a request without an X-Twilio-Signature.

What Ahena configures

You declare what you want in ahena.config.ts; ahena plan shows each change with its classification before anything is applied.

ChangeClassificationNotes
Create the messaging serviceCONFIRMATION_REQUIREDFree. Matched by exact name, so it's never duplicated; created with your webhooks.
Set the service's inbound request URL and status callbackCONFIRMATION_REQUIREDIn production too. Only the fields that differ are sent.
Add a number the account already owns to the sender poolCONFIRMATION_REQUIREDOnly SMS-capable numbers that aren't in another service. Buying numbers is billable, so it stays a manual step in the Twilio Console.

Refused outright:

  • Buying or releasing phone numbers, deleting services, removing or moving senders, and sending messages.
  • Registering A2P 10DLC brands or campaigns (billable, needs business details).
  • Webhook URLs that aren't public https or that embed credentials.

Generated code: src/ahena/sms/index.ts (the provider-neutral sms.send({ to, body }) resolving to { id }, and sms.raw), src/ahena/providers/twilio.ts, .env.example.twilio and, with Next.js, a webhook route that verifies X-Twilio-Signature with the SDK's validateRequest.

How Ahena verifies

After every write, Ahena reads Twilio back. Each change ends in one of these states, and the plan's outcome says why:

  • VERIFIED: Ahena read the provider back and saw the desired state. The CLI shows ✓ only for VERIFIED.
  • APPLIED_UNVERIFIED: The provider accepted the change, but the re-read doesn't show it yet (after bounded polling), or the re-read failed.

Every write re-reads first (service by name, sender pool by number) and is skipped if already done, then the plan is read again and must come back empty.

Listings follow Twilio's paging to the end (next_page_uri, meta.next_page_url) and never conclude something is missing from a partial list.

Read permissions are probed with one-item GETs; write permissions come from the key's own policy, or are reported unknown for a Standard key.

If a request may have reached Twilio but the answer was lost, Ahena re-reads before deciding: done, safe to retry, or “check before retrying”.

What Doctor diagnoses

Key checks from ahena doctor. Each finding says why it matters, where, the impact and whether Ahena can fix it. The full list is in the Twilio docs.

CheckWhat it means
twilio.credentials / .testThe key works; test credentials fail in production.
twilio.account / .typeA suspended account or a trial account in production fails, with the manual upgrade steps.
twilio.serviceThe declared messaging service exists; a missing one has a plannable fix.
twilio.service.senders / .senderThe pool has an SMS-capable sender and includes sms.from.
twilio.webhook.inbound / .statusThe service sends inbound messages and delivery statuses to your route.
twilio.webhook.endpointOne unsigned POST to your route: rejected is good; a 2xx means it doesn't verify X-Twilio-Signature.
twilio.a2pUS long codes without a verified A2P 10DLC campaign warn, with the manual registration steps.

More on findings, health and continuous checks: Doctor.

Approvals

Twilio changes here are classified CONFIRMATION_REQUIRED. Every change is planned and shown as a diff first. Ahena itself enforces who can approve it:

  • SAFE changes are applied without asking.
  • In production, every other change needs approval in the Ahena dashboard, signed in, by an admin.
  • In other environments, CONFIRMATION_REQUIRED changes are approved in the CLI; BILLABLE and DESTRUCTIVE changes always need the dashboard.
  • MANUAL steps are never applied by Ahena.

--yes and --allow-billable don't replace a dashboard approval. See approvals for how it works and why the browser.

Manual steps

These are MANUAL: Ahena never does them. It lists them with the exact steps when they apply.

  • Upgrade a trial account before production (Twilio Console → Upgrade).
  • Buy or port the numbers you send from in the Twilio Console; Ahena then adds them to the service.
  • Register an A2P 10DLC brand and campaign for US long codes, or verify toll-free numbers.
  • Store the Auth Token for the webhook route: ahena env set <environment> TWILIO_AUTH_TOKEN.

Credentials and permissions

NameSecretNotes
TWILIO_API_KEY_SIDYesA Standard or Restricted API key made for Ahena (SK…).
TWILIO_API_KEY_SECRETYesThe key's secret, shown once when it's created.
TWILIO_ACCOUNT_SIDNoThe account the key belongs to (AC…).
TWILIO_AUTH_TOKENYesOnly for the webhook route, which verifies Twilio's signature with it. Ahena never reads it; you store it with ahena env set.

Least privilege

  • Read permissions are checked with one-item GETs; write permissions are read from a Restricted key's policy (with /twilio/iam/api-keys/read), and reported unknown for Standard keys.
  • A Restricted key needs services list/read/create/update, services.phonenumbers list/create, services.usa2p-campaign/list and phone-numbers/active-numbers/list. Never grant message or number-purchase permissions.

Each credential Ahena stores gets its own key and is envelope-encrypted, bound to the environment. See security.

Workflow example

Connect, check, plan, then apply. Placeholders (…) stand for your own values.

  1. TWILIO_API_KEY_SID=… TWILIO_API_KEY_SECRET=… ahena connect twilio -e production --set TWILIO_ACCOUNT_SID=…

    Connect with an API key made for Ahena.

  2. ahena doctor -e production

    Check the account, service, senders, webhooks, route and A2P 10DLC.

  3. ahena plan -e production

    See the messaging service, webhook and sender changes.

  4. ahena apply -e production

    Approve and apply; nothing billable is ever planned.

  5. ahena generate twilio -e production --framework nextjs

    Write the SMS adapter and the signed webhook route.

Verification status

Beta

Available in beta. Every capability is validated by Ahena's automated provider contract and conformance tests; verification against the real service is next.

How Twilio is tested
  • Twilio is tested against a simulated version of its APIs: Main, Standard and Restricted keys with policies, test credentials (error 20008), both paging styles, the read-back after each write and idempotent re-apply.
  • It hasn't been run against the real Twilio API yet. The HTTP status that comes with a Restricted key's missing-permission error (70051) is taken from the docs, not observed.
  • The generated adapter and webhook route are typechecked against the real twilio SDK, and the route is run in tests with signatures made by the SDK.

The providers overview explains Ahena's testing methodology and what has been verified for every provider.

Limitations

  • Ahena doesn't buy, release or reconfigure numbers, register A2P 10DLC, verify toll-free numbers, or manage short codes and sender IDs.
  • With a Standard key, the account's type and status and the key's write access can't be read; Doctor says they're not verified.
  • Only Twilio's default (US1) API hosts are supported.

Disconnecting

ahena disconnect twilio removes the stored key; delete Ahena's API key in the Twilio Console. Services, numbers, webhooks and registrations are untouched.