Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 62 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ All services start with sensible defaults. No config file needed:
- **MongoDB Atlas** on `http://localhost:4010`
- **Clerk** on `http://localhost:4011`
- **Linear** on `http://localhost:4012`
- **Twilio** on `http://localhost:4013`

## CLI

Expand Down Expand Up @@ -147,7 +148,7 @@ afterAll(() => Promise.all([github.close(), vercel.close()]))

| Option | Default | Description |
|--------|---------|-------------|
| `service` | *(required)* | Service name: `'vercel'`, `'github'`, `'google'`, `'slack'`, `'apple'`, `'microsoft'`, `'okta'`, `'aws'`, `'resend'`, `'stripe'`, `'mongoatlas'`, `'clerk'`, or `'linear'` |
| `service` | *(required)* | Service name: `'vercel'`, `'github'`, `'google'`, `'slack'`, `'apple'`, `'microsoft'`, `'okta'`, `'aws'`, `'resend'`, `'stripe'`, `'mongoatlas'`, `'clerk'`, `'linear'`, or `'twilio'` |
| `port` | `4000` | Port for the HTTP server |
| `seed` | none | Inline seed data (same shape as YAML config) |
| `baseUrl` | none | Override advertised base URL. Per-service `baseUrl` in seed config takes highest priority, then this option, then `EMULATE_BASE_URL` env var (supports `{service}`), then `PORTLESS_URL` (supports `{service}`, automatically set by the `portless` CLI wrapper), then `http://localhost:<port>`. |
Expand Down Expand Up @@ -865,6 +866,63 @@ Linear scope checks are relaxed by default so local tests can use simple bearer

Current Linear limits: full schema coverage, exact production rate limiting, notification inbox behavior, rich document APIs, customer APIs, initiative APIs, exact search relevance, and production agent behavior are not implemented. Agent support is a focused local-test subset.

## Twilio API

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. No real SMS, MMS, WhatsApp, email, voice, carrier, compliance, billing, or SendGrid traffic is performed.

Default local credentials:

```text
TWILIO_ACCOUNT_SID=AC00000000000000000000000000000000
TWILIO_AUTH_TOKEN=twilio_test_auth_token
TWILIO_API_KEY=SK00000000000000000000000000000000
TWILIO_API_SECRET=twilio_test_api_secret
TWILIO_PHONE_NUMBER=+15551234567
TWILIO_VERIFY_SERVICE_SID=VA00000000000000000000000000000000
```

### REST Routes

- `GET /2010-04-01/Accounts/{AccountSid}.json` - fetch account
- `GET /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json` - list phone numbers
- `POST /2010-04-01/Accounts/{AccountSid}/Messages.json` - create outbound message
- `GET /2010-04-01/Accounts/{AccountSid}/Messages.json` - list messages
- `POST /2010-04-01/Accounts/{AccountSid}/Calls.json` - create outbound call
- `POST /messaging/v1/Services` - create Messaging Service
- `POST /verify/v2/Services/{ServiceSid}/Verifications` - start verification
- `POST /verify/v2/Services/{ServiceSid}/VerificationCheck` - check verification code
- `POST /conversations/v1/Services` - create Conversation Service
- `POST /conversations/v1/Services/{ServiceSid}/Conversations` - create Conversation
- `POST /conversations/v1/Services/{ServiceSid}/Conversations/{ConversationSid}/Participants` - add participant
- `POST /conversations/v1/Services/{ServiceSid}/Conversations/{ConversationSid}/Messages` - add message

Twilio uses multiple product hosts. For local SDK tests, rewrite Twilio SDK requests to the emulator and map `messaging.twilio.com` to `/messaging`, `verify.twilio.com` to `/verify`, and `conversations.twilio.com` to `/conversations`.

### SMS And OTP Testing

For the common SMS verification loop, the seeded Verify Service uses code `123456`. Start a verification through the normal Verify API, then either submit `123456` in your app test or fetch the latest local code with the authenticated helper route:

```sh
curl -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
"http://localhost:4000/_twilio/simulate/verification-code?To=%2B15550002222&ServiceSid=$TWILIO_VERIFY_SERVICE_SID"
```

The helper returns the latest local verification for that phone number, including `verification_sid`, `status`, `attempts`, and `code`. It is local-only test support and is not part of Twilio's production API. The Verify inspector also shows each attempted code.

To test inbound SMS webhooks, configure a seeded 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`. To test outbound delivery transitions, create a message with `StatusCallback`, then call `POST /_twilio/simulate/message-status`.

### Simulator And Inspector

- `POST /_twilio/simulate/inbound-message` - create an inbound message and invoke the configured SMS webhook
- `POST /_twilio/simulate/message-status` - advance message status and send status callbacks
- `GET /_twilio/simulate/verification-code` - fetch the latest local Verify code by `VerificationSid` or `To`
- `POST /_twilio/simulate/inbound-call` - create an inbound call and invoke the configured voice webhook
- `POST /_twilio/simulate/call-status` - advance call status
- `POST /_twilio/simulate/verification-status` - force a verification state by `VerificationSid` or `To`
- `GET /` - tabbed inspector for messages, Verify, calls, Conversations, phone numbers, services, auth, and webhook deliveries

Current Twilio limits: no carrier delivery, A2P 10DLC, toll-free verification, real phone number purchasing, exact rate limits, Studio, Flex, TaskRouter, Video, Sync, Segment, SendGrid, Conversations SDK websocket behavior, or complete TwiML interpreter.

## Apple Sign In

Sign in with Apple emulation with authorization code flow, PKCE support, RS256 ID tokens, and OIDC discovery.
Expand Down Expand Up @@ -1049,6 +1107,7 @@ packages/
google/ # Google OAuth 2.0 / OIDC + Gmail, Calendar, Drive
slack/ # Slack Web API, OAuth v2, incoming webhooks
linear/ # Linear GraphQL API, OAuth, webhooks
twilio/ # Twilio Messaging, Verify, Voice, webhooks
apple/ # Apple Sign In / OIDC
microsoft/ # Microsoft Entra ID OAuth 2.0 / OIDC + Graph /me
aws/ # AWS S3, SQS, IAM, STS
Expand All @@ -1072,6 +1131,8 @@ Tokens are configured in the seed config and map to users. Pass them as `Authori

**Linear**: GraphQL accepts `Authorization: Bearer <token>` or a bare personal API key value. Seeded Linear tokens map to users or app actors, OAuth apps support local authorization code and client credentials flows, and optional strict scope mode checks supported GraphQL operations.

**Twilio**: HTTP Basic auth accepts the seeded Account SID/Auth Token pair or API Key/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`.

**Apple**: OIDC authorization code flow with RS256 ID tokens. On first auth per user/client pair, a `user` JSON blob is included.

**Microsoft**: OIDC authorization code flow with PKCE support. Also supports client credentials grants. Microsoft Graph `/v1.0/me` available.
Expand Down
2 changes: 1 addition & 1 deletion apps/web/app/api/docs-chat/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const maxDuration = 60;

const DEFAULT_MODEL = "anthropic/claude-haiku-4.5";

const SYSTEM_PROMPT = `You are a helpful documentation assistant for emulate, a local drop-in replacement for Vercel, GitHub, Google, Slack, Apple, Microsoft, Okta, AWS, Resend, Stripe, MongoDB Atlas, Clerk, and Linear APIs used in CI and no-network sandboxes.
const SYSTEM_PROMPT = `You are a helpful documentation assistant for emulate, a local drop-in replacement for Vercel, GitHub, Google, Slack, Apple, Microsoft, Okta, AWS, Resend, Stripe, MongoDB Atlas, Clerk, Linear, and Twilio APIs used in CI and no-network sandboxes.

emulate provides fully stateful, production-fidelity API emulation, not mocks. The CLI is installed as the "emulate" npm package and run via "npx emulate". It also supports a programmatic API via createEmulator and a Next.js adapter (@emulators/adapter-next) for embedding emulators in your app.

Expand Down
7 changes: 7 additions & 0 deletions apps/web/app/docs/twilio/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { pageMetadata } from "@/lib/page-metadata";

export const metadata = pageMetadata("twilio");

export default function Layout({ children }: { children: React.ReactNode }) {
return children;
}
113 changes: 113 additions & 0 deletions apps/web/app/docs/twilio/page.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Twilio API

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.

## Start

```bash
npx emulate --service twilio
```

When Twilio is the only enabled service, it starts on `http://localhost:4000`. When all services are started, it uses `http://localhost:4013`.

## Defaults

```text
TWILIO_ACCOUNT_SID=AC00000000000000000000000000000000
TWILIO_AUTH_TOKEN=twilio_test_auth_token
TWILIO_API_KEY=SK00000000000000000000000000000000
TWILIO_API_SECRET=twilio_test_api_secret
TWILIO_PHONE_NUMBER=+15551234567
TWILIO_VERIFY_SERVICE_SID=VA00000000000000000000000000000000
```

## URL Mapping

| Real Twilio URL | Emulator URL |
|-----------------|--------------|
| `https://api.twilio.com/2010-04-01/...` | `$TWILIO_EMULATOR_URL/2010-04-01/...` |
| `https://messaging.twilio.com/v1/...` | `$TWILIO_EMULATOR_URL/messaging/v1/...` |
| `https://verify.twilio.com/v2/...` | `$TWILIO_EMULATOR_URL/verify/v2/...` |
| `https://conversations.twilio.com/v1/...` | `$TWILIO_EMULATOR_URL/conversations/v1/...` |

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.

## Supported Routes

- `GET /2010-04-01/Accounts/{AccountSid}.json`
- `GET /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json`
- `POST /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json`
- `POST /2010-04-01/Accounts/{AccountSid}/Messages.json`
- `GET /2010-04-01/Accounts/{AccountSid}/Messages.json`
- `GET /2010-04-01/Accounts/{AccountSid}/Messages/{MessageSid}.json`
- `POST /2010-04-01/Accounts/{AccountSid}/Messages/{MessageSid}.json`
- `DELETE /2010-04-01/Accounts/{AccountSid}/Messages/{MessageSid}.json`
- `POST /2010-04-01/Accounts/{AccountSid}/Calls.json`
- `GET /2010-04-01/Accounts/{AccountSid}/Calls.json`
- `POST /messaging/v1/Services`
- `GET /messaging/v1/Services`
- `POST /verify/v2/Services/{ServiceSid}/Verifications`
- `POST /verify/v2/Services/{ServiceSid}/VerificationCheck`
- `POST /conversations/v1/Services`
- `POST /conversations/v1/Services/{ServiceSid}/Conversations`
- `POST /conversations/v1/Services/{ServiceSid}/Conversations/{ConversationSid}/Participants`
- `POST /conversations/v1/Services/{ServiceSid}/Conversations/{ConversationSid}/Messages`

## SMS And OTP Testing

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:

```bash
curl -u "$TWILIO_ACCOUNT_SID:$TWILIO_AUTH_TOKEN" \
"http://localhost:4000/_twilio/simulate/verification-code?To=%2B15550002222&ServiceSid=$TWILIO_VERIFY_SERVICE_SID"
```

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.

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`. For outbound delivery transitions, create a message with `StatusCallback`, then call `POST /_twilio/simulate/message-status`.

## Simulator

- `POST /_twilio/simulate/inbound-message`
- `POST /_twilio/simulate/message-status`
- `GET /_twilio/simulate/verification-code`
- `POST /_twilio/simulate/inbound-call`
- `POST /_twilio/simulate/call-status`
- `POST /_twilio/simulate/verification-status`

## Seed Config

```yaml
twilio:
account:
sid: AC00000000000000000000000000000000
auth_token: twilio_test_auth_token
friendly_name: Local Twilio Account
api_keys:
- sid: SK00000000000000000000000000000000
secret: twilio_test_api_secret
friendly_name: Local API Key
phone_numbers:
- phone_number: "+15551234567"
friendly_name: Local SMS and Voice Number
sms_url: http://localhost:3000/api/twilio/sms
voice_url: http://localhost:3000/api/twilio/voice
messaging_services:
- friendly_name: Local Messaging Service
phone_numbers: ["+15551234567"]
verify_services:
- friendly_name: Local Verify Service
code: "123456"
default_channel: sms
conversations:
services:
- friendly_name: Local Conversations
```

## Inspector

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

## Current Limits

No real SMS, MMS, WhatsApp, email, voice, carrier, compliance, billing, SendGrid, Studio, Flex, TaskRouter, Video, Sync, Segment, Conversations SDK websocket behavior, or complete TwiML interpreter behavior is implemented.
1 change: 1 addition & 0 deletions apps/web/lib/docs-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const allDocsPages: NavItem[] = [
{ name: "Google API", href: "/docs/google" },
{ name: "Slack API", href: "/docs/slack" },
{ name: "Linear API", href: "/docs/linear" },
{ name: "Twilio API", href: "/docs/twilio" },
{ name: "Apple Sign In", href: "/docs/apple" },
{ name: "Microsoft Entra ID", href: "/docs/microsoft" },
{ name: "AWS", href: "/docs/aws" },
Expand Down
1 change: 1 addition & 0 deletions apps/web/lib/page-titles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const PAGE_TITLES: Record<string, string> = {
google: "Google API",
slack: "Slack API",
linear: "Linear API",
twilio: "Twilio API",
apple: "Apple Sign In",
microsoft: "Microsoft Entra ID",
aws: "AWS",
Expand Down
32 changes: 32 additions & 0 deletions packages/@emulators/twilio/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# @emulators/twilio

Twilio API emulator for local development and CI. Part of [emulate](https://github.com/vercel-labs/emulate).

```bash
npm install @emulators/twilio
```

Run it through the CLI:

```bash
npx emulate --service twilio
```

The first supported surface includes seeded Account SID/Auth Token credentials, API key credentials, incoming phone numbers, Programmable Messaging, Messaging Services, Verify, basic Voice calls, Conversations REST resources, signed webhooks, simulator routes, and an inspector.

Default local credentials:

```text
TWILIO_ACCOUNT_SID=AC00000000000000000000000000000000
TWILIO_AUTH_TOKEN=twilio_test_auth_token
TWILIO_API_KEY=SK00000000000000000000000000000000
TWILIO_API_SECRET=twilio_test_api_secret
TWILIO_PHONE_NUMBER=+15551234567
TWILIO_VERIFY_SERVICE_SID=VA00000000000000000000000000000000
```

Twilio uses multiple product hosts. When testing with the official Node SDK, use a custom request client that rewrites Twilio hosts to the local emulator. The emulator exposes product-prefixed local routes such as `/verify/v2` and `/messaging/v1`.

For SMS and OTP tests, the seeded Verify Service uses code `123456`. Test runners can also read the latest local code with `GET /_twilio/simulate/verification-code?To=...&ServiceSid=...` using Basic auth. Inbound SMS webhooks are simulated with `POST /_twilio/simulate/inbound-message`, using an assigned Messaging Service `inbound_request_url` before falling back to the phone number `sms_url`. Outbound delivery transitions are simulated with `POST /_twilio/simulate/message-status`.

No real SMS, voice, carrier, compliance, billing, or SendGrid traffic is performed.
46 changes: 46 additions & 0 deletions packages/@emulators/twilio/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"name": "@emulators/twilio",
"version": "0.7.0",
"license": "Apache-2.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"homepage": "https://emulate.dev",
"repository": {
"type": "git",
"url": "https://github.com/vercel-labs/emulate.git",
"directory": "packages/@emulators/twilio"
},
"bugs": {
"url": "https://github.com/vercel-labs/emulate/issues"
},
"publishConfig": {
"access": "public"
},
"files": [
"dist"
],
"scripts": {
"build": "tsup --clean",
"dev": "tsup --watch",
"test": "vitest run",
"clean": "rm -rf dist .turbo",
"type-check": "tsc --noEmit",
"lint": "eslint src"
},
"dependencies": {
"@emulators/core": "workspace:*"
},
"devDependencies": {
"tsup": "^8",
"twilio": "^6.0.2",
"typescript": "^5.7",
"vitest": "^4.1.0"
}
}
Loading
Loading