SMS testing infrastructure for India
Test every SMS.
Ship with confidence.
Capture SMS requests, validate DLT templates and retrieve OTPs — without sending a single real SMS.
Scroll to follow one OTP through smsyra
- 01Your appSends an SMS request, as it always has.Same SDK, same request. Only the base URL points at smsyra in staging or CI.
POST /p/msg91/api/v5/flow - 02API gatewaysmsyra answers like your provider.AmazeSMS, MSG91, Fast2SMS, Twilio or Exotel — same paths, same fields, same answers.
- 03DLT engineEvery character is checked.Against the template you registered on the DLT portal: case, quotes, spaces, variable lengths.
- 04VerdictMatched — or the first difference, explained.“Character 36: the template has ' but the text has ’.” Found in staging, not by your users.
- 05Live inboxThe text lands in your inbox.Recipient, sender, template, parts, encoding and the raw request — live, for the whole team.
- 06OTP APIThe code, extracted for your tests.GET /v1/otp/latest waits for the text and returns the code. No phone, no SIM, no sleeps.
482913 - 07SandboxCaptured. Never sent.No phone received this text. Nothing smsyra receives is ever delivered to anyone.
The problem
Carriers drop SMS silently. Staging can’t see why.
Every Indian business text is checked against its registered DLT template, character by character. One difference and it’s dropped — no error, no bounce, just an OTP that never arrives.
- A curly quoteA translation tool turns ' into ’. The text no longer matches its DLT template, and the carrier drops it — silently.
- A variable too long{#var#} allows 30 characters. A long name or a URL with a token goes over, and the OTP never arrives.
- No way to see itStaging sends real texts to real phones, or none at all. The mismatch shows up as a support ticket.
smsyra is the staging SMS provider that tells you. It answers like your real provider, checks every text against your templates, and keeps it in an inbox — without sending a single real SMS.
How smsyra works
Change one URL. Keep everything else.
- 01Swap one URLPoint your provider's base URL at smsyra in staging or CI, with an smsyra key. Your code stays the same.
- 02Send as usualsmsyra answers exactly like your provider — success and errors — and sends nothing.
- 03Read the verdictEvery text lands in a live inbox, checked against your DLT templates, the first difference explained.
- AMAZESMS_URL=https://api.amazesms.com/api/sms+ AMAZESMS_URL=https://api.shriradhatechnologies.com/p/amazesms/api/smsSMS sandbox
Every request, captured. Live.
Recipient, sender, template, parts, encoding, the raw request and the answer — in an inbox the whole team shares, as texts arrive.
- Filters by verdict, number, provider and key
- A phone-style preview with GSM-7 / Unicode parts
- CSV export, history kept 7 or 30 days, then deleted
- +91 98••• ••821482913 is your ACME login code. Don't share it with anyone.Matched
- +91 90••• ••114Your ACME order has shipped and arrives Friday.Mismatched
- +91 87••• ••509Weekend offer: 20% off everything at ACME.No template
- +91 70••• ••362Your ACME appointment is at 4:30 pm today.Simulated
DLT validation
The first difference, explained in plain words.
Import your DLT portal’s export once. Classic {#var#} and the typed tags, 30-character limits, Unicode parts — checked on every text.
{#var#} is your ACME login code. Don't share it with anyone.
Carriers compare every character. A word processor or a translation tool swapping one apostrophe is enough for the text to be dropped, silently.
1007123456789012345- Error: Character 36: the template has ' but the text has ’ (a curly quote where the template has a straight one).
- Warning: That one curly quote makes the text Unicode: 70 characters a part instead of 160.
482913 is your ACME login code. Don’t share it with anyone. (this doesn't match the template)
fixed text variable doesn’t match
- Template has
't·share·it·- Text has
’t·share·it·- Around it
login code. Don’t share it with
Invisible characters are shown as · (space), ⍽ (non-breaking space), ↵ (line break), ⇥ (tab).
OTP automation
Your end-to-end tests read the code.
GET /v1/otp/latest waits up to 30 seconds for the next text to a number and returns the code — no polling loops, no shared phones, no sleeps.
- Reads the template’s variable, WebOTP lines, or the code near “OTP”
- Returns the DLT verdict with it, so a test can fail on a mismatch
- Delete a number’s texts between tests
// e2e/sign-in.spec.ts — read the OTP your app just "sent"
const res = await request.get(`${SMSYRA}/v1/otp/latest`, {
params: { to: "9876543210", wait: 25 },
headers: { Authorization: `Bearer ${process.env.SMSYRA_KEY}` },
});
const { code, dlt } = await res.json();
expect(dlt.status).toBe("MATCHED");
await page.getByLabel("6-digit code").fill(code);Failure simulation
Break the provider on purpose.
Switch a key into a failure mode and test your retries, fallbacks and timeouts. These are simulations you control — smsyra never reports a real provider outage.
- SuccessThe normal answer, every time.
HTTP 200
{"status":"success","messageid":"412093857261038475"} - Provider errorThe provider's own error, kept as “Simulated error”.
HTTP 200
{"status":"error","message":"Service temporarily unavailable"} - Provider downA 503, as if the provider were unreachable.
HTTP 503
{"error":"Service Unavailable"} - SlowWaits up to 30 seconds, then answers normally.
HTTP 200
…after the wait you set
Answers shown in AmazeSMS’s shape. Each provider gets its own — and strict DLT mode answers with the provider’s DLT error.
Provider-compatible APIs
Answers like the providers Indian teams use.
Same paths, same fields, same answers. Keep your SDK or HTTP client.
- AmazeSMSGET or POST /p/amazesms/api/smsThe GET /api/sms gateway: key, to, from, body and the DLT ids as parameters.
- MSG91 (Flow v5)POST /p/msg91/api/v5/flowFlow API v5 (template id + variables), and the older sendhttp.php.
- Fast2SMSGET or POST /p/fast2sms/dev/bulkV2bulkV2 with the dlt, dlt_manual, otp and q routes.
- TwilioPOST /p/twilio/2010-04-01/Accounts/{sid}/Messages.jsonThe Messages resource, with HTTP Basic auth.
- ExotelPOST /p/exotel/v1/Accounts/{sid}/Sms/send.jsonSend SMS (v1), JSON with .json or XML without it.
- smsyra JSON APIPOST /v1/messagesOur own JSON endpoint, for code you write yourself.
- Developers wiring OTPsSee the exact text your sign-in, booking or alert flow sends — before a carrier drops it in production.
- QA and CI pipelinesRead the code from the OTP API in end-to-end tests. No real phone, no SIM, no flaky waits.
- Teams registering DLT templatesImport the portal's export and see every mismatch before it ships.
- Teams switching providersTry the new provider's request shape against the same templates, without sending a single text.
For developers
Send it the way you already do.
An AmazeSMS request to smsyra — then the OTP API. Copy, paste, swap in your key.
All setup guidescurl -G "https://api.shriradhatechnologies.com/p/amazesms/api/sms" \
--data-urlencode "key=smy_YOUR_KEY" \
--data-urlencode "to=9876543210" \
--data-urlencode "from=ACMEOT" \
--data-urlencode "entityid=1001234567890123456" \
--data-urlencode "templateid=1007123456789012345" \
--data-urlencode "body=482913 is your ACME login code. It is valid for 10 minutes."curl -G "https://api.shriradhatechnologies.com/v1/otp/latest" \
-H "Authorization: Bearer smy_YOUR_KEY" \
--data-urlencode "to=9876543210" \
--data-urlencode "wait=25"Interactive demo
Run one request through the sandbox.
Pick what should happen. Watch the request, the check, the inbox, the OTP and the answer your app would get.
Pick a scenario
Illustrative data. This runs in your browser: nothing is sent to smsyra, to a provider, or to any phone.
- API request
GET /p/amazesms/api/sms?key=smy_…a4f2&from=ACMEOT&to=98765•••21&templateid=1007123456789012345&body=482913+is+your+ACME+login+code.+Don%27t+share+it+with+anyone. - GatewayKey accepted · answered in AmazeSMS's shape
- DLT validationMatchedTemplate 1007123456789012345: every character matches.
- Live inbox+91 98765 •••21482913 is your ACME login code. Don't share it with anyone.Matched
- OTP extraction482913Read from the template's {#var#}.
- Provider response
HTTP 200 · {"status":"success","messageid":"412093857261038475"}Exactly what AmazeSMS would answer. Your code carries on.
The workspace
Built for the people fixing the text.
Inbox, templates, provider mappings, keys and setup guides — one workspace for the team.
- +91 98••• ••821482913 is your ACME login code. Don't share it with anyone.Matched
- +91 90••• ••114Your ACME order has shipped and arrives Friday.Mismatched
- +91 87••• ••509Weekend offer: 20% off everything at ACME.No template
- +91 70••• ••362Your ACME appointment is at 4:30 pm today.Simulated
Pricing
Free to start. Nothing per text.
- 200 test texts a month
- 7 days of history
- 2 members, 10 templates
- Every provider, the OTP API, failure modes
- 10,000 test texts a month
- 30 days of history
- 10 members, unlimited templates
- Or ₹9,990 a year — two months free
Questions
Straight answers.
Does smsyra ever send a text?
No. Nothing smsyra receives is delivered to anyone, ever. It answers your app the way your provider would, and keeps the text in your inbox instead.
Can I point production at it?
No — smsyra is for staging, CI and local testing. Your real users would never get their codes. Keep your real provider for production.
Do I have to change my code?
Only the base URL and the key, usually through environment variables. Requests and answers keep your provider’s shape.
How does the DLT check work?
Add your registered templates (paste them, or import your DLT portal’s export). Each text is matched character by character. The rules in full.
Are the failure simulations real outages?
No. They’re switches on your own key, so you can test how your app handles errors and slow answers. smsyra never reports a real provider’s status.
Where is my data kept, and for how long?
In India (Mumbai), for 7 days on Free and up to 30 days on Team — never longer. You can delete texts at any time.
Catch the mismatch in staging,
not in production.
Free for 200 test texts a month. No card, nothing to install — change one URL.