Skip to main content
Stateful Twilio REST emulation with seeded accounts, Auth Tokens, API keys, incoming phone numbers, Programmable Messaging, Messaging Services, Verify, basic Voice calls, Conversations REST resources, signed webhooks, local simulator routes, and an inspector. Every route is exercised through the twilio Node SDK in the emulator’s test suite, and responses are checked against Twilio’s published OpenAPI specs.

At a glance

Start

When Twilio is the only enabled service, it starts on http://localhost:4000. When all services are started, it uses http://localhost:4012. The default account, credentials, phone number, and Verify Service:

URL mapping

The official Node SDK builds absolute Twilio product URLs. In SDK tests, use a custom request client that rewrites those hosts to the emulator prefixes above. Lists on the Messaging, Verify, and Conversations hosts page with Twilio’s meta block (next_page_url), as the SDK expects.

Auth

HTTP Basic auth accepts the seeded Account SID and Auth Token pair or an API Key SID and API Secret pair. Product-host APIs are exposed under local prefixes such as /messaging/v1 and /verify/v2; the 2010 API lives at /2010-04-01.

Test credentials

Every account also has Twilio’s test credentials: a separate account SID and auth token (TWILIO_TEST_ACCOUNT_SID and TWILIO_TEST_AUTH_TOKEN above). As on Twilio, they can only create messages, calls, and incoming phone numbers under the test account SID. Nothing they create is stored, sent, or listed, and any other request is 403 with error 20008. Twilio’s magic numbers decide each outcome: Any other To succeeds. Set your own pair with test_account_sid and test_auth_token under account in the seed config.

Accounts

GET /2010-04-01/Accounts.json, GET and POST /2010-04-01/Accounts/{AccountSid}.json.

Incoming phone numbers

GET and POST /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json; GET, POST, and DELETE .../IncomingPhoneNumbers/{Sid}.json.

Messages

GET and POST /2010-04-01/Accounts/{AccountSid}/Messages.json; GET, POST, and DELETE .../Messages/{MessageSid}.json; GET .../Messages/{MessageSid}/Media.json and .../Media/{MediaSid}.json.
  • POST /_twilio/simulate/inbound-message — Simulate inbound message: creates an inbound message and invokes the configured SMS webhook
  • POST /_twilio/simulate/message-status — Advance message status: sends status callbacks
For inbound SMS, configure a phone number sms_url, then call POST /_twilio/simulate/inbound-message with To, From, and Body. If the destination number is assigned to a Messaging Service with inbound_request_url, the simulator sends the inbound webhook there and includes MessagingServiceSid; otherwise it uses the phone number sms_url. A text from a Conversations participant’s address to its proxy address goes into that conversation instead. For outbound delivery transitions, create a message with StatusCallback, then call POST /_twilio/simulate/message-status with MessageSid, Status, and optionally ErrorCode.

Messaging Services

GET and POST /messaging/v1/Services; GET, POST, and DELETE /messaging/v1/Services/{ServiceSid}; GET and POST .../PhoneNumbers; DELETE .../PhoneNumbers/{Sid}. A Messaging Service sender is the phone number itself: it is listed and removed under the number’s PN sid. A number sends for one Messaging Service at a time, so adding it again, or to a second service, is 409 with error 21710; a seeded phone_numbers entry moves the number to the service that lists it.

Verify

GET and POST /verify/v2/Services; GET, POST, and DELETE /verify/v2/Services/{ServiceSid}; POST .../Verifications; GET and POST .../Verifications/{Sid} (cancel); POST .../VerificationCheck.
  • GET /_twilio/simulate/verification-code — Fetch the latest local Verify code: by VerificationSid or To
  • POST /_twilio/simulate/verification-status — Force a verification state: by VerificationSid or To
The seeded Verify Service uses code 123456, so most local OTP tests can start a verification through the normal Verify API and submit that code. Tests that need to read the current local code can use the authenticated helper route:
The helper returns the latest local verification for that phone number, including verification_sid, status, attempts, and code. It is local-only test support, not a production Twilio API. The Verify inspector also shows each attempted code.

Voice

GET and POST /2010-04-01/Accounts/{AccountSid}/Calls.json; GET, POST, and DELETE .../Calls/{CallSid}.json.
  • POST /_twilio/simulate/inbound-call — Simulate inbound call: creates an inbound call and invokes the configured voice webhook
  • POST /_twilio/simulate/call-status — Advance call status

Conversations

GET and POST /conversations/v1/Conversations on the account’s default service, and the same routes under /conversations/v1/Services/{ServiceSid}/Conversations; GET, POST, and DELETE .../Conversations/{ConversationSid} (by sid or unique name); GET and POST .../Participants and .../Messages; GET, POST, and DELETE .../Participants/{Sid} and .../Messages/{Sid}. Every account has a default Conversations service, created the first time it is needed, and client.conversations.v1.conversations works in it; its conversations are also visible under the service’s own path. A new conversation takes the account’s first Messaging Service unless MessagingServiceSid is given. Unique names are unique within a service (409, error 50353), a participant identity or SMS address can join a conversation once (409, errors 50433 and 50416), message indexes only grow (a deleted message’s index is not reused), and Order=desc lists the newest messages first. An SMS address and proxy address pair belongs to one conversation that is not closed: adding it to a second is 409 (50416) naming the first.

Services and defaults

GET and POST /conversations/v1/Services; GET /conversations/v1/Services/{ServiceSid}; GET and POST /conversations/v1/Configuration (DefaultChatServiceSid).

Conversations webhooks

GET and POST /conversations/v1/Configuration/Webhooks (the default service’s) and /conversations/v1/Services/{ServiceSid}/Configuration/Webhooks; GET and POST .../Conversations/{ConversationSid}/Webhooks; GET, POST, and DELETE .../Webhooks/{Sid}. Conversations webhooks follow Twilio’s rules. A service’s PostWebhookUrl receives the events named in Filters (onMessageAdded, onConversationStateUpdated, onParticipantRemoved, onDeliveryUpdated, and the rest), signed with X-Twilio-Signature and sent with the service’s Method. The account-level /v1/Configuration/Webhooks reads and writes the default service’s settings. A REST action sends events only when the request has X-Twilio-Webhook-Enabled: true (xTwilioWebhookEnabled: "true" in the SDK), with Source=API. Scoped webhooks under .../Conversations/{Sid}/Webhooks hear one conversation: a webhook target sends its Configuration.Filters, a trigger target sends onMessageAdded when the message has one of its Configuration.Triggers as a word, Configuration.ReplayAfter replays the messages from that index on, and studio targets are stored without running a flow.

Delivery receipts

GET .../Conversations/{ConversationSid}/Messages/{MessageSid}/Receipts and .../Receipts/{Sid}. A message is texted from each SMS or WhatsApp participant’s proxy address to the participant (not to its author, and not to participants without a proxy address), and each text gets a delivery receipt (DY..., status sent). POST /_twilio/simulate/message-status on that text moves the receipt to delivered, read, failed, or undelivered (ErrorCode sets the error), sends onDeliveryUpdated, and updates the message’s delivery summary. POST /_twilio/simulate/inbound-message from a participant’s address to its proxy address adds the text to that conversation (Source=SMS) instead of calling the number’s sms_url, texts the other SMS participants, and wakes an inactive conversation. Texts run the service’s PreWebhookUrl first when Filters has onMessageAdd: a 4xx or 5xx answer drops the text, and a JSON answer may change body, author, and attributes. REST actions never reach the pre-event URL, as on Twilio.

Roles and users

On the default service at /conversations/v1 and under /conversations/v1/Services/{ServiceSid}: GET and POST .../Roles and .../Users; GET, POST, and DELETE .../Roles/{Sid} and .../Users/{Sid} (a user by sid or identity); GET .../Users/{Sid}/Conversations; GET, POST, and DELETE .../Users/{Sid}/Conversations/{ConversationSid}; GET .../ParticipantConversations?Identity= or ?Address=. Every service starts with the four roles Conversations inherits from Programmable Chat: service admin and service user (type service), channel admin and channel user (type conversation). New users get service user and new participants channel user. Custom roles take only the permissions Twilio lists for their type (400, error 20001, otherwise), and an update replaces the whole list. A participant’s RoleSid must be a conversation role of its service and a user’s a service role. A participant with an identity is a user of the service, made the first time it joins. User identities are unique per service (409, 50201). .../Users/{Sid}/Conversations reports each conversation’s unread_messages_count (null until the participant has read something), last_read_message_index, and notification_level, and deleting one removes the user from that conversation. onUserAdded and onUserUpdated go to the service’s post-event URL.

Address configurations

GET and POST /conversations/v1/Configuration/Addresses; GET, POST, and DELETE .../Addresses/{Sid} (by sid or address). An address configuration (IG..., one per address, 409 20409 for a second) with AutoCreation.Enabled makes a text from someone in no conversation start one in its AutoCreation.ConversationServiceSid (or the default service). The sender joins with the address as proxy, and a webhook or studio type becomes the conversation’s scoped webhook, which hears onParticipantAdded and onMessageAdded with Source=SMS.

Seed config

Inspector

Open GET / in the Twilio emulator to inspect messages, Verify attempts, calls, Conversations, phone numbers, services, credentials, and webhook deliveries.

Limits

No real SMS, MMS, WhatsApp, email, voice, carrier delivery, A2P 10DLC, toll-free verification, real phone number purchasing, exact rate limits, compliance, billing, SendGrid, Studio, Flex, TaskRouter, Video, Sync, Segment, Conversations SDK websocket behavior, or complete TwiML interpreter behavior is implemented. Conversations media, push notifications, and user online state are not modeled, Studio flows do not run, and webhooks are not retried.

Conformance

See CONFORMANCE.md for every provider.