SMS verification codes for Georgia (+995), sent and checked. Try test codes with a free test key.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
Optional: Bearer sk_test_… from your NOTIFY dashboard. Without it, the agent calls create_test_key.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
Check the code the human typed, for the number the code was sent to. The result is the verification only when it is approved; every other outcome is a tool error with an `error.code`. Let the user in only when a check answers `200` with status `approved`; treat every other answer, a timeout and a network error as not approved. Errors: `incorrect_code` (attempts remain), `too_many_attempts` (start a new verification), `recipient_mismatch` (`to` is not the number the code was sent to), `verification_expired`, `verification_closed` (already approved or replaced), `verification_not_found`. A wrong code costs an attempt, and five close the verification: do not retry a check by yourself; ask the human for the code again.
The number the code was sent to, which your application is about to mark as verified.
The six digits the human typed, as a string.
The verification `id` from start_verification (ver_…).
An sk_test_ key, for example from create_test_key. Leave it out to use the key in this MCP server's Authorization header. A live key (sk_live_) is refused here: tool arguments are kept in the conversation, so a live key belongs in the header.
This tool isn't marked read-only, so it may change something on the server or in an account you connected.
Check the code the human typed, for the number the code was sent to. The result is the verification only when it is approved; every other outcome is a tool error with an `error.code`. Let the user in only when a check answers `200` with status `approved`; treat every other answer, a timeout and a network error as not approved. Errors: `incorrect_code` (attempts remain), `too_many_attempts` (start a new verification), `recipient_mismatch` (`to` is not the number the code was sent to), `verification_expired`, `verification_closed` (already approved or replaced), `verification_not_found`. A wrong code costs an attempt, and five close the verification: do not retry a check by yourself; ask the human for the code again.
| Argument | Type | Description |
|---|---|---|
| to* | string | The number the code was sent to, which your application is about to mark as verified. |
| code* | string | The six digits the human typed, as a string. |
| api_key | string | An sk_test_ key, for example from create_test_key. Leave it out to use the key in this MCP server's Authorization header. A live key (sk_live_) is refused here: tool arguments are kept in the conversation, so a live key belongs in the header. |
| verification_id* | string | The verification `id` from start_verification (ver_…). |
{
"type": "object",
"required": [
"verification_id",
"to",
"code"
],
"properties": {
"to": {
"type": "string",
"pattern": "^\\+9955[0-9]{8}$",
"description": "The number the code was sent to, which your application is about to mark as verified."
},
"code": {
"type": "string",
"pattern": "^[0-9]{6}$",
"description": "The six digits the human typed, as a string."
},
"api_key": {
"type": "string",
"description": "An sk_test_ key, for example from create_test_key. Leave it out to use the key in this MCP server's Authorization header. A live key (sk_live_) is refused here: tool arguments are kept in the conversation, so a live key belongs in the header."
},
"verification_id": {
"type": "string",
"description": "The verification `id` from start_verification (ver_…)."
}
},
"additionalProperties": false
}Get an sk_test_ API key instantly, with no account and no human. A test key never sends a real SMS and never spends credits, so a whole integration can be built and verified with it. Pass it as `api_key` to send_message and get_message, and keep it in the application's configuration (for example NOTIFY_API_KEY), never in source code. The result also has a `claim_url` for a human: opening it lets them sign in and take over the project. Anyone who opens it, signs in and confirms becomes the owner: give it only to the person who will own the project, and do not paste it into shared logs or tickets. Real SMS need an sk_live_ key that the human creates on the dashboard (https://n0t1fy.dev/dashboard). Limited to a few keys per minute and per day for each address: reuse a key you already have.
No arguments.
{
"type": "object",
"properties": {},
"additionalProperties": false
}Read the current state of a message sent with a key of the same project. Poll only while `status` is `queued` or `submitted`: `delivered`, `undeliverable` and `failed` are final, and `unknown` never changes. Wait about 2 seconds before the first read and double the wait each time, up to 30 seconds. Always set a time limit (two minutes is generous for a test message); when it passes, treat the message as unconfirmed and do not send it again.
| Argument | Type | Description |
|---|---|---|
| api_key | string | An sk_test_ key, for example from create_test_key. Leave it out to use the key in this MCP server's Authorization header. A live key (sk_live_) is refused here: tool arguments are kept in the conversation, so a live key belongs in the header. |
| message_id* | string | The message `id` from send_message, such as msg_01J8…. |
{
"type": "object",
"required": [
"message_id"
],
"properties": {
"api_key": {
"type": "string",
"description": "An sk_test_ key, for example from create_test_key. Leave it out to use the key in this MCP server's Authorization header. A live key (sk_live_) is refused here: tool arguments are kept in the conversation, so a live key belongs in the header."
},
"message_id": {
"type": "string",
"description": "The message `id` from send_message, such as msg_01J8…."
}
},
"additionalProperties": false
}Send one transactional SMS to a Georgian mobile number. The text comes from a built-in template; there is no free text. With a test key (sk_test_) nothing is really sent: a simulator answers. These recipients simulate outcomes: +995512345671 rejected (status `failed`); +995512345672 provider unreachable (error `provider_unavailable`: nothing was sent, retry with the same idempotency_key); +995512345673 ambiguous (status `unknown`); +995512345674 accepted, then `undeliverable`. Any other valid number is accepted, then `delivered`, within about 30 seconds. With a live key it sends a real SMS and spends one credit per segment. Read `message.status` in the result. `submitted`: the provider accepted it, which is not yet delivery to the phone. `failed`: refused; `message.error` says why. `unknown`: it may or may not have been sent, so do not send it again automatically. `replayed: true`: this idempotency_key and request were already processed, and no second SMS was sent. Call get_message to learn the final outcome.
| Argument | Type | Description |
|---|---|---|
| to* | string | Recipient in E.164 format: `+9955` followed by eight digits. No other country or format is accepted, and numbers are never normalized on your behalf. |
| locale | string | Language of the message: `ka` (Georgian, the default) or `en`. |
| api_key | string | An sk_test_ key, for example from create_test_key. Leave it out to use the key in this MCP server's Authorization header. A live key (sk_live_) is refused here: tool arguments are kept in the conversation, so a live key belongs in the header. |
| template* | string | Which message to send. Message text is never free-form: every message is rendered from one of these templates. - `verification_code` (`code`): Your verification code is {code}. Do not share it with anyone. - `login_code` (`code`): Your sign-in code is {code}. If you did not request it, ignore this message. - `password_reset_code` (`code`, `minutes`): Your password reset code is {code}. It expires in {minutes} minutes. |
Verify a Georgian mobile number: NOTIFY makes a random six-digit code and sends it by SMS with a built-in template. The code is never shown to you: ask the human for the code they received, then call check_verification. A code works for 10 minutes and 5 checks, once; a new start for the same number replaces it. With a test key (sk_test_), only the test numbers +995512345670 to +995512345679 are accepted and their code is always 000000; nothing is sent. At most 5 starts a minute go to one number, so spread test starts over the ten test numbers. +995512345671 simulates a refused SMS (status `failed`), +995512345672 an unreachable provider (error `provider_unavailable`: nothing was sent, retry with the same idempotency_key), +995512345673 an SMS that may or may not have arrived (still `pending`). With a live key it sends a real SMS and spends one credit. `replayed: true` means this idempotency_key and request were already processed and no second SMS was sent; the verification it shows may say `approved` by then, which is not a check result. On `too_many_verifications` (5 an hour or 10 in a rolling 24 hours to one number), tell the human when they can try again (`retry_at`), and never retry by yourself.
| Argument | Type | Description |
|---|---|---|
| to* | string | The phone number to verify, in E.164 format: `+9955` followed by eight digits. With a test key (`sk_test_`), only the test numbers +995512345670 to +995512345679 are accepted, and their code is always `000000`. |
| locale | string | Language of the message: `ka` (Georgian, the default) or `en`. |
| api_key | string | An sk_test_ key, for example from create_test_key. Leave it out to use the key in this MCP server's Authorization header. A live key (sk_live_) is refused here: tool arguments are kept in the conversation, so a live key belongs in the header. |
| variables* | object | Values for the template's placeholders, all as JSON strings. A `code` is 4 to 8 digits; `minutes` is 1 to 999. Values that look like a link, a web address, or a phone number are refused. |
| idempotency_key* | string | A new unique value (a UUID) for each logical message. Reuse it only to retry the identical request: the retry then returns the original message instead of sending a second SMS. |
{
"type": "object",
"required": [
"to",
"template",
"variables",
"idempotency_key"
],
"properties": {
"to": {
"type": "string",
"pattern": "^\\+9955[0-9]{8}$",
"description": "Recipient in E.164 format: `+9955` followed by eight digits. No other country or format is accepted, and numbers are never normalized on your behalf."
},
"locale": {
"enum": [
"ka",
"en"
],
"type": "string",
"default": "ka",
"description": "Language of the message: `ka` (Georgian, the default) or `en`."
},
"api_key": {
"type": "string",
"description": "An sk_test_ key, for example from create_test_key. Leave it out to use the key in this MCP server's Authorization header. A live key (sk_live_) is refused here: tool arguments are kept in the conversation, so a live key belongs in the header."
},
"template": {
"enum": [
"verification_code",
"login_code",
"password_reset_code"
],
"type": "string",
"description": "Which message to send. Message text is never free-form: every message is rendered from one of these templates.\n\n- `verification_code` (`code`): Your verification code is {code}. Do not share it with anyone.\n- `login_code` (`code`): Your sign-in code is {code}. If you did not request it, ignore this message.\n- `password_reset_code` (`code`, `minutes`): Your password reset code is {code}. It expires in {minutes} minutes."
},
"variables": {
"type": "object",
"description": "Values for the template's placeholders, all as JSON strings. A `code` is 4 to 8 digits; `minutes` is 1 to 999. Values that look like a link, a web address, or a phone number are refused.",
"additionalProperties": {
"type": "string"
}
},
"idempotency_key": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "A new unique value (a UUID) for each logical message. Reuse it only to retry the identical request: the retry then returns the original message instead of sending a second SMS."
}
},
"additionalProperties": false
}| template |
| string |
| Which message carries the code. NOTIFY fills in the code, and the minutes where the template names them (10). |
| idempotency_key* | string | A new unique value (a UUID) for each code you want sent. Reuse it only to retry the identical request: the retry then returns the original verification instead of sending a second SMS. |
{
"type": "object",
"required": [
"to",
"idempotency_key"
],
"properties": {
"to": {
"type": "string",
"pattern": "^\\+9955[0-9]{8}$",
"description": "The phone number to verify, in E.164 format: `+9955` followed by eight digits. With a test key (`sk_test_`), only the test numbers +995512345670 to +995512345679 are accepted, and their code is always `000000`."
},
"locale": {
"enum": [
"ka",
"en"
],
"type": "string",
"default": "ka",
"description": "Language of the message: `ka` (Georgian, the default) or `en`."
},
"api_key": {
"type": "string",
"description": "An sk_test_ key, for example from create_test_key. Leave it out to use the key in this MCP server's Authorization header. A live key (sk_live_) is refused here: tool arguments are kept in the conversation, so a live key belongs in the header."
},
"template": {
"enum": [
"verification_code",
"login_code",
"password_reset_code"
],
"type": "string",
"default": "verification_code",
"description": "Which message carries the code. NOTIFY fills in the code, and the minutes where the template names them (10)."
},
"idempotency_key": {
"type": "string",
"maxLength": 200,
"minLength": 1,
"description": "A new unique value (a UUID) for each code you want sent. Reuse it only to retry the identical request: the retry then returns the original verification instead of sending a second SMS."
}
},
"additionalProperties": false
}