Send a one-time code
Create a one-time code and send it by email or SMS.
https://app.usenotix.dev/api/v1/verify/sendFull access and sending access keys can call this. Key permissions lists the extra rules some calls have.
Create a one-time code and send it by email or SMS.
Risk scoring
Every request is scored before anything is sent. The response carries a risk object with the score, the level and one entry per signal that fired. Pass clientIp when you have it, so the per IP address signal can contribute. Notix stores only a keyed hash of that address and never logs it.
SMS codes
Pass channel: "sms" to deliver the code by SMS instead of email.
{
"to": "+2348012345678",
"channel": "sms",
"appName": "Acme"
}
For channel: "sms", to is a phone number in E.164 form and appName is required. senderId is optional and defaults to your team's own approved sender ID for the country, or to the shared Notix sender ID when you have none (see SMS and sender IDs). SMS codes are prepaid from your wallet and need no paid plan.
from, subject and templateId do not apply to channel: "sms" and are rejected. The default text is "<appName>: your code is <code>. It expires in <minutes> minutes.".
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | BAD_REQUEST | The request is invalid: codeLength, expiresIn, the to address, an unverified from domain, or a suppressed recipient. |
| 401 | UNAUTHORIZED | The API key is missing or not valid. |
| 402 | INSUFFICIENT_BALANCE | The team wallet cannot cover an SMS code. Nothing was sent. |
| 403 | FORBIDDEN | The API key is restricted to a domain other than the resolved from address. |
| 404 | NOT_FOUND | The calling team no longer exists. |
| 422 | RISK_REFUSED | Risk scoring refused the request. Nothing was sent and no code exists. |
| 429 | RATE_LIMITED | Too many codes were requested for this recipient. |
Idempotency
Pass an Idempotency-Key header to make a retry safe for 24 hours. The same key and body answers the original result without sending again, with the response header Idempotent-Replayed: true. The same key with a different body answers 409 NOT_UNIQUE; the same key while the first request is still running answers 409 too, so retry shortly. Keys are scoped to the API key. If the idempotency store is briefly unavailable the code is still sent, without de-duplication: a login code is never held back by an outage.
Dry run
Send the header Notix-Dry-Run: true to see what the request would do without doing it. The answer is 200 with dryRun: true: who it would reach, what they would see, what it would cost, a verdict (allowed and every reasons it would be refused for) and the limits it would count against. Nothing is sent, charged, reserved or written, and the call is recorded in the audit log as a dry run. A write route that cannot preview answers 400.
Body parameters
The recipient email address, or for channel sms a phone number in E.164 form.
At least 3 characters. At most 320 characters.
Where the code is delivered. Default email. For sms, appName is required, from, subject and templateId are not allowed, and to must be a phone number with its country code.
One of email, sms. Default "email".
For channel sms only. A sender ID approved for this team. Defaults to the team's own approved sender ID for the country, or to the shared Notix sender ID when it has none.
At most 11 characters.
Address on a domain verified by the team. 'Name <address>' is allowed. Defaults to no-reply@ the team's first verified domain.
Shown in the email or the SMS. May not contain a link or web address. Defaults to the team name for email; required for sms.
At most 60 characters.
Digits in the generated code. 4 to 8, default 6.
Minimum 4. Maximum 8.
Seconds until the code expires. 60 to 1800, default 600.
Minimum 60. Maximum 1800.
A Notix template whose subject and body use {{code}}, {{appName}} and {{expiresMinutes}}. When absent, the built-in verification template is used.
Overrides the built-in template's subject. Default '{{appName}} verification code: {{code}}'.
At most 998 characters.
The IPv4 or IPv6 address the end user made the request from. Used by risk scoring. Notix stores only a keyed hash of it and never logs it.
Headers
Bearer followed by your API key.
Makes the request safe to retry for 24 hours. Same key and same body answers the original result without running again, with the response header Idempotent-Replayed: true. Same key and a different body answers 409 NOT_UNIQUE; same key while the first request is still running answers 409 too, so retry shortly. Keys are scoped to the API key.
Set to true to preview the request instead of running it. The answer is 200 with dryRun: true: who it would reach, what they would see, what it would cost, whether policy allows it and the limits it counts against. Nothing is sent, charged, reserved or written. A write route that cannot preview answers 400.
Errors
Invalid input, for example codeLength, expiresIn, an invalid 'to' address, an unverified from domain, a suppressed recipient, or for channel sms a missing appName, an email-only field (from, subject, templateId), or a 'to' that is not a phone number in E.164 form
Channel sms only: the team wallet cannot cover the message. The body carries code INSUFFICIENT_BALANCE. The message names the shortfall only to a key a team admin or the owner made.
The calling API key is restricted to a domain other than the resolved 'from' address, or (channel sms) the key may not spend the wallet on SMS: it was created by a team member without admin rights, or it is a send-only key where those are not allowed SMS
The calling team no longer exists
Risk scoring refused the request. The body carries code RISK_REFUSED and the risk object naming every signal that fired. Nothing was sent and no code exists.
Too many verification codes requested for this recipient, or (channel sms) the team reached its daily SMS spending limit
Every error has the same shape. See all error codes.
curl -X POST "https://app.usenotix.dev/api/v1/verify/send" \
-H "Authorization: Bearer $NOTIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "user@example.com",
"subject": "Your receipt for order 4471"
}'
import { Notix } from "notix-js";
const notix = new Notix(process.env.NOTIX_API_KEY);
const { data, error } = await notix.verify.send({
to: "user@example.com",
appName: "Acme",
clientIp: "203.0.113.10",
});
from notix import Notix
notix = Notix() # reads NOTIX_API_KEY
verification, _ = notix.verify.send({"to": "user@example.com", "appName": "Acme", "clientIp": "203.0.113.10"})
use Notix\Notix;
$notix = new Notix(getenv('NOTIX_API_KEY'));
$verification = $notix->verify->send(['to' => 'user@example.com', 'appName' => 'Acme'], '203.0.113.10');
{
"dryRun": true,
"kind": "string",
"verdict": {
"allowed": true,
"reasons": [
"string"
]
},
"checks": [
"string"
]
}