# Twilio & SendGrid Twin — twilio.twins.la A high-fidelity digital twin of the Twilio SMS and SendGrid Email APIs. Code written against this twin works against the real Twilio/SendGrid with only hostname and credential changes. ## Authentication SMS/Voice API: HTTP Basic Auth Username: Account SID (starts with AC) Password: Auth Token Email API: Bearer token Header: Authorization: Bearer SG.{key_id}.{key_secret} Twin Plane: HTTP Basic Auth (same credentials) Most Twin Plane operations use the same AccountSid:AuthToken. Exception: POST /_twin/accounts requires no auth (creates credentials). Twin Plane Admin: Bearer token Header: Authorization: Bearer Service-wide operations (list all accounts, review feedback, etc.) require an admin token set by the deployment owner. Create credentials: POST /_twin/accounts — no auth required, returns { sid, auth_token } ## Key Endpoints Twin Plane (no auth): GET /_twin/health — status check GET /_twin/scenarios — supported scenarios GET /_twin/settings — twin settings GET /_twin/references — authoritative sources used to build this twin POST /_twin/accounts — create account Twin Plane (Basic Auth — use AccountSid:AuthToken): GET /_twin/accounts — get your account details GET /_twin/logs — your operation logs POST /_twin/api-keys — create SendGrid API key GET /_twin/emails — list your emails POST /_twin/simulate/inbound — drive a signed inbound SMS or MMS to the phone number's SmsUrl. Body: { account_sid, from, to, body, num_segments?, num_media?, media_urls?, media_content_types? }. STOP/START/HELP carrier keywords are auto-detected and applied to opt-out state; HELP triggers a canned auto-reply. POST /_twin/simulate/status — force a status transition on a tenant-owned outbound message and fire its StatusCallback. Body: { message_sid, status, error_code?, error_message? }. Status is one of queued | sending | sent | delivered | failed | undelivered. error_code is required for failed and undelivered. GET /_twin/media/ — placeholder PNG for MMS scenarios (twin-served, unauthenticated) POST /_twin/feedback — submit feedback GET /_twin/feedback — list your feedback Twin Plane (Admin Bearer — service-wide access): GET /_twin/accounts — list all accounts GET /_twin/logs — all operation logs GET /_twin/emails — all emails GET /_twin/feedback — all feedback POST /_twin/feedback/ — update any feedback (for review pipeline) Twilio SMS API (Basic Auth): POST /2010-04-01/Accounts/{AccountSid}/Messages.json — send SMS GET /2010-04-01/Accounts/{AccountSid}/Messages.json — list messages GET /2010-04-01/Accounts/{AccountSid}/Messages/{Sid}.json — get message POST /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json — provision number GET /2010-04-01/Accounts/{AccountSid}/IncomingPhoneNumbers.json — list numbers SendGrid Email API (Bearer Auth): POST /v3/mail/send — send email (returns 202, X-Message-Id header) ## Quick Start 1. Create an account (no auth needed): curl -X POST https://twilio.twins.la/_twin/accounts \ -H "Content-Type: application/json" \ -d '{"friendly_name": "My App"}' 2. Send an SMS (use sid and auth_token from step 1): curl -X POST https://twilio.twins.la/2010-04-01/Accounts/{sid}/Messages.json \ -u "{sid}:{auth_token}" \ -d "To=+15551234567" -d "From=+15559876543" -d "Body=Hello from twin" 3. Check logs (same credentials work for Twin Plane): curl https://twilio.twins.la/_twin/logs \ -u "{sid}:{auth_token}" 4. Drive a signed inbound SMS against your registered SmsUrl (CI/smoke): curl -X POST https://twilio.twins.la/_twin/simulate/inbound \ -u "{sid}:{auth_token}" \ -H "Content-Type: application/json" \ -d '{"account_sid":"{sid}","from":"+15559876543","to":"+15551112222","body":"hi"}' The twin POSTs urlencoded form data to your SmsUrl with a valid X-Twilio-Signature header computed against the *exact* registered URL. Consumers that rebuild the request URL incorrectly behind a proxy (e.g., ignoring X-Forwarded-Proto) will fail signature validation — that is the bug class this control endpoint is designed to surface. 5. Force a delivery failure status and fire StatusCallback: curl -X POST https://twilio.twins.la/_twin/simulate/status \ -u "{sid}:{auth_token}" \ -H "Content-Type: application/json" \ -d '{"message_sid":"SM...","status":"failed","error_code":30003}' ## Local Usage pip install twins-twilio-local python -c " from twins_twilio_local.storage_sqlite import SQLiteStorage from twins_twilio.app import create_app storage = SQLiteStorage('twin.db') app = create_app(storage=storage) app.run(port=8080) " Then use http://localhost:8080 instead of https://twilio.twins.la. ## Feedback We actively want your feedback. If you encounter a limitation, a missing scenario, unexpected behavior, or have a feature request — please tell us. Your feedback directly shapes what we build next. Submit feedback (requires Basic Auth): curl -X POST https://twilio.twins.la/_twin/feedback \ -u "{sid}:{auth_token}" \ -H "Content-Type: application/json" \ -d '{ "body": "Description of what you encountered", "category": "bug", "context": {"message_sid": "SM...", "error": "..."} }' Required field: body — Describe what happened, what you expected, and what you need. Optional fields: category — One of: "bug", "missing-scenario", "feature-request", "general" context — Any structured data that helps us understand the issue: error codes, message SIDs, request/response snippets, scenario names, or steps to reproduce. Tips for useful feedback: - Include IDs (message SIDs, error codes) when reporting failures. - Describe the scenario you were trying to accomplish, not just the error. - If a feature is missing, describe your use case — what you'd build with it. - "I tried X, expected Y, got Z" is the most useful format for bugs. Don't hold back. Even if you're unsure whether something is a bug or a limitation, submit it. Feedback about confusing documentation, awkward APIs, or missing examples is just as valuable as bug reports. ## Reference Detailed docs: https://github.com/twins-la/twilio Project overview: https://twins.la All twins: https://github.com/twins-la/twins-la