SMSTunnel for Ory
Kratos and Ory Network SMS codes from your own phone
kratos.courier.yml A few lines of config, no code
Ory writes the text from its SMS templates (sign-in, verification and recovery codes) and its courier hands it to SMSTunnel over HTTP. Your own Android phone then sends it from its own number, so people get their code from a number they know.
Before you start
- 1. Install the SMSTunnel app from Google Play on your Android phone and sign in.
- 2. In your dashboard, open Apps → Add an app, pick Sign-in codes, name it (for example "Ory") and click Create the key.
- 3. Copy the key right away: it's shown only once. You can stop or delete the app there anytime, and Ory can't send texts anymore.
Get the steps with your key in them
It opens Add an app with Sign-in codes picked. Once the key is made, the dashboard shows every step and command with the key already filled in, ready to copy.
Keep the key in your platform's secrets, never in code that runs in the browser. Codes must be 4 to 10 digits: the default 6-digit codes are fine; codes with letters aren't accepted.
1. Add the courier channel
Merge this into your identity config, with your key in place of YOUR_SMSTUNNEL_API_KEY:
courier:
channels:
- id: sms
type: http
request_config:
url: https://smstunnel.io/api/v1/sms/send-2fa
method: POST
body: base64://ZnVuY3Rpb24oY3R4KSB7CiAgdG86IGN0eC5yZWNpcGllbnQsCiAgbWVzc2FnZTogY3R4LmJvZHksCn0K
headers:
Content-Type: application/json
auth:
type: api_key
config:
name: X-API-Key
value: YOUR_SMSTUNNEL_API_KEY
in: header
The body is this Jsonnet, base64-encoded (self-hosted Kratos can also use
file://…):
function(ctx) {
to: ctx.recipient,
message: ctx.body,
} Both files are in examples/ory. The config holds your key, so keep it out of public repositories.
2. Apply it
Ory Network
Download the identity config, add the block, and upload it again. You can also enter it in the Console's SMS configuration.
ory get identity-config --project <id> --workspace <id> --format yaml > identity-config.yaml
# add the courier block, then:
ory update identity-config --project <id> --workspace <id> --file identity-config.yaml Self-hosted Kratos
Add the block to kratos.yml and run the courier:
kratos serve --watch-courier or kratos courier watch.
3. Send codes by SMS
Your identity schema needs a phone trait with "via": "sms", for example
"credentials": { "code": { "identifier": true, "via": "sms" } } for sign-in
codes and "verification": { "via": "sms" } for verification. Ory has SMS
templates for login_code, verification_code and
recovery_code, and they include the code.
Worth knowing
- Codes have their own allowance. They go through SMSTunnel's code endpoint,
POST /api/v1/sms/send-2fa, which counts them apart from your other texts. See Pricing. - What counts as a code: 4 to 10 digits standing on their own, like
482913, or two groups of 3 digits, like123-456. A text without one is refused. Details - One number per text, up to 320 characters.
- 30 sends a minute. Past that, SMSTunnel answers 429 and asks to retry a little later.
- Phone offline? The code waits and goes out as soon as the phone is back.
Build it with an AI assistant
Copy this prompt into Claude, ChatGPT, Cursor or Copilot and replace the part in [brackets]. Don't paste your API key into the chat: the prompt doesn't need it.
Help me send the SMS codes of Ory (Kratos self-hosted or Ory Network) from my own phone, with SMSTunnel.
About SMSTunnel (https://smstunnel.io): my own apps and tools hand text messages (SMS) to my SMSTunnel account, and my own Android phone sends them from my own number, on my own SIM and plan. The phone runs the SMSTunnel app from Google Play (https://play.google.com/store/apps/details?id=io.smstunnel.app), signed in to my account.
What I want: [describe what should send a text, to whom, and when]
How it works:
- No code needed: Ory's courier HTTP channel calls SMSTunnel directly. Merge this into the identity config:
courier:
channels:
- id: sms
type: http
request_config:
url: https://smstunnel.io/api/v1/sms/send-2fa
method: POST
body: base64://ZnVuY3Rpb24oY3R4KSB7CiAgdG86IGN0eC5yZWNpcGllbnQsCiAgbWVzc2FnZTogY3R4LmJvZHksCn0K
headers:
Content-Type: application/json
auth:
type: api_key
config:
name: X-API-Key
value: YOUR_SMSTUNNEL_API_KEY
in: header
- The body is base64 of this Jsonnet (self-hosted Kratos can also use file://...):
function(ctx) {
to: ctx.recipient,
message: ctx.body,
}
- Ory Network: ory get identity-config --project <id> --workspace <id> --format yaml > identity-config.yaml, add the block, then ory update identity-config --project <id> --workspace <id> --file identity-config.yaml (or enter it in the Console's SMS configuration). Self-hosted Kratos: add the block to kratos.yml and run the courier (kratos serve --watch-courier or kratos courier watch).
- The identity schema needs a phone trait with "via": "sms", for example "credentials": { "code": { "identifier": true, "via": "sms" } } for sign-in codes and "verification": { "via": "sms" } for verification. Ory's SMS templates login_code, verification_code and recovery_code include the code.
- SMSTunnel's POST https://smstunnel.io/api/v1/sms/send-2fa takes {"to", "message"} with the header X-API-Key. The text must contain the code as 4 to 10 digits (Ory's codes are fine) and can be up to 320 characters; otherwise it answers 400 "No valid verification code found". Other answers: 401 bad key, 403 missing permission, 429 too many requests (30 a minute). If the phone is offline, the code waits and goes out when it is back.
- The API key comes from my SMSTunnel dashboard (https://dashboard.smstunnel.io): Apps → Add an app → Create the key. It is shown only once and can send texts and check whether they went out. The config file then holds the key, so keep it out of public repositories.
Rules for your answer:
- Do not ask for my SMSTunnel password or API key and do not put a real key in your answer; I will not paste it in this chat.
- Phone numbers must be in international format (+ and the country code).
- Help me test it with my own number first.
- Only send texts people expect from me. No unsolicited texts. Ory is a trademark of its owner. SMSTunnel is a third-party integration and is not affiliated with, endorsed by, or sponsored by Ory.
Sign-in codes for other platforms
Another platform? sendCode() from the same package
sends any code: see its README.