SMS YANU documentation home

Verify (one-time codes)

The Verify API makes a one-time code, sends it to a Malawi number and checks what the person typed.

Use Verify for sign-ups, logins and payment confirmations. SMS YANU makes the code, sends it and checks it. Your system keeps only the verification id. The code is stored only as a keyed hash.

Each code sent is one normal SMS, charged at your price. If the gateway refuses it, it is not charged. Checking a code is free.

Start a verification

POST https://api.smsyanu.com/v1/verify/start
Authorization: Bearer yanu_live_YOUR_KEY
Content-Type: application/json
FieldTypeRequiredMeaning
tostringYesMalawi number, for example +265990000000.
sender_idstringNoSender name, up to 11 characters.
app_namestringNoYour name in the default message, up to 40 characters.
templatestringNoYour own wording, up to 320 characters. It must contain {code}. {minutes} is replaced with the time limit.
code_lengthintegerNo4 to 8. Default 6.
ttl_secondsintegerNo60 to 1800. Default 300.
max_attemptsintegerNoWrong tries allowed, 1 to 10. Default 5.
testbooleanNoSimulate. Nothing is sent or charged, and the code comes back in sandbox_code.
{
  "to": "+265990000000",
  "app_name": "Shop",
  "ttl_seconds": 300
}

The default message is: Shop: your verification code is 482913. It expires in 5 minutes. Do not share it.

HTTP 201:

{
  "id": "vrf_8f2c0a6e1b7d4c3a9e5f1d20",
  "status": "pending",
  "to": "+265990****00",
  "environment": "live",
  "livemode": true,
  "attempts": 0,
  "attempts_left": 5,
  "sends": 1,
  "expires_at": "2026-11-07T10:05:00+00:00",
  "verified_at": null,
  "cost": 20.0,
  "currency": "MWK",
  "created_at": "2026-11-07T10:00:00+00:00"
}

Starting again for the same number replaces the open verification, so only the newest code works. Add an Idempotency-Key header to make a retry safe: the same key returns the first verification and sends nothing.

Check a code

POST https://api.smsyanu.com/v1/verify/check
FieldTypeRequiredMeaning
idstringOne of the twoThe id from start.
tostringOne of the twoUses the newest pending verification for this number.
codestringYesWhat the person typed.
testbooleanNoSet it when the verification was started with test.
{ "id": "vrf_8f2c0a6e1b7d4c3a9e5f1d20", "code": "482913" }

The answer has the same fields as start, plus valid:

{ "id": "vrf_8f2c0a6e1b7d4c3a9e5f1d20", "status": "approved", "valid": true, "attempts": 1, "attempts_left": 4 }

Test for valid. A wrong code answers 200 with "valid": false.

StatusMeaning
pendingWaiting for the right code.
approvedThe right code was given.
expiredThe time limit passed.
failedToo many wrong tries, or the code could not be sent.
cancelledCancelled, or replaced by a newer verification.

Other calls

MethodPathWhat it does
GET/v1/verify/{id}Status of one verification. Does not use a try.
POST/v1/verify/{id}/resendSend a fresh code.
POST/v1/verify/{id}/cancelCancel a pending verification.
GET/v1/verifyList verifications.
GET/v1/verify/statsTotals and conversion for up to 90 days.

Test without sending

With a yanu_test_ key, or "test": true, nothing reaches a phone and nothing is charged. The code comes back in sandbox_code, so your automated tests can use it. Never show sandbox_code to a user.

Limits

LimitValue
Codes to one number5 in any 10 minutes
Codes per account1,000 per hour
Wait between resends30 seconds
Sends per verification4, counting the first

Going over a limit answers 429 with verify_rate_limited and a Retry-After header.

Errors

HTTPCodeWhat it means
400template_missing_codeYour template has no {code}.
400id_requiredPass id or to to check.
404verification_not_foundNo such verification on this account.
409verification_closedResend on a verification that has finished.
429verify_rate_limitedA limit above was reached.
502otp_send_failedThe code could not be sent. Nothing was charged.

On this page