OTP templates
Send WhatsApp OTPs in wording you pick from Meta's authentication library, from your own WhatsApp Business Account. Add a template once in the dashboard, wait for Meta to approve it, then pass its id — or make it a pipeline's default. With neither, nothing changes: the Built-in default keeps sending.
Browse the library, add templates, and copy Template ids.
Requires your own WhatsApp Business Account
Custom wording is only sent from your own WABA's phone number. Without an active own WABA, the dashboard shows Connect your WhatsApp Business Account and any request that selects a template is refused with TEMPLATE_WABA_REQUIRED. Connect your WABA first.
How it works
- Pick an entry in Browse library and choose English, Arabic, or both.
- Add it. Akedly creates it on your WABA and Meta reviews each language, usually within minutes.
- Wait for Approved in the OTP language. Akedly emails you when Meta decides.
- Use it: pass
template: { id }on V1.2 or V2, or make it a pipeline's default so requests need no change.
A template changes only the wording of a WhatsApp OTP sent from your own number. It does not choose the channel: routing is unchanged, and SMS, Telegram and email keep their usual wording.
You choose
- The library entry and its languages.
- The security sentence, when the entry does not already include one.
- Stored defaults for each non-fixed variable, per language.
- Per-request variable values.
- A default template per pipeline.
Fixed
- Meta's wording and variable slots.
- The Copy code button and the three-minute expiry footer.
- The language:
otpLanguagewhen supplied, otherwise the pipeline's OTP Language. - 15 characters per variable value.
Add a template
- Open OTP Templates under Channels setup → Setup and switch to Browse library. Use Preview: EN / AR to read each language.
- Select Add to my templates on an entry.
- Choose the languages. Entries marked English only have no Arabic version.
- Optionally store a default for each variable, per language. The counter shows the 15-character limit; fixed values such as
validityare filled for you. - Select Add to my templates. Each language starts In review.
Stored defaults decide how short your requests can be:
- A variable with a stored default can be left out of a request; a request value overrides it.
- A variable without one must be sent on every request, and the template cannot be a pipeline default for that language.
- Edit defaults changes future sends without another Meta review. Adding a language starts one.
Two limits apply to adding and editing:
- Each WhatsApp Business Account holds up to 20 templates. Removed templates don't count. At 20, adding another is refused with
TEMPLATE_LIMIT_REACHED; remove one first. - You can't hold two identical templates: same library entry, languages, security recommendation and stored defaults. Adding one, or an edit that would make two identical, is refused with
TEMPLATE_DUPLICATE, which names the template you already have. If an earlier add failed at Meta, select Retry Meta setup on that template instead of adding it again. Templates that differ only in their stored defaults are allowed.
Viewing needs whatsapp:read. Adding, editing, removing and setting a pipeline default need whatsapp:update; without it the page shows View only.
Track approval
Each card in My templates shows one badge per language, the stored defaults, the Template id to send, and a ready-made request body. While anything is in review the page refreshes the stored status every 30 seconds, and Akedly emails you when a language is approved or rejected.
| Badge | Meaning | What to do |
|---|---|---|
| In review | Meta is reviewing this language. | Wait. Requests in this language are refused with TEMPLATE_NOT_APPROVED. |
| Approved | Ready in this language. | Copy the Template id. |
| Rejected | Meta refused this language. The rejection email includes Meta's reason when Meta gives one. | The wording can't be edited: remove the template and add it again. |
| Missing on Meta | Akedly saved the template but Meta did not create this language. | Select Retry Meta setup. If Meta refuses the name, remove and add it again. |
| Re-add after WhatsApp change | The template belongs to a WABA you replaced. It overrides every language badge. | Remove it and add it again (see Remove and re-add). |
Approval is per language: an approved English variant does not make the template usable for an OTP requested in Arabic. The Meta name (akedly_auth_…) is for reference only; requests use the Template id. The read-only Built-in Default card at the top needs no id.
Send with a template
- Name
- template
- Type
- object
- Description
{ id, variables }only. Any other key, includinglanguage, is refused withTEMPLATE_VARIABLE_INVALID. Use top-levelotpLanguageto choose the OTP language.template: nullmeans the same as leaving it out.
- Name
- template.id
- Type
- string
- Description
The Template id from My templates.
- Name
- template.variables
- Type
- object
- Description
Curated variable names mapped to values. Required only for variables with no stored default in the OTP language.
Choose the template on your server. Never forward a template object from a browser or app. On V2 the request signature does not cover template.
The echo is not a delivery receipt
data.template is { id, language } as Akedly resolved it when the transaction or attempt was created — from your request or from the pipeline default. It never changes afterwards and does not say which channel or template delivered the code: it is still present when the OTP goes out by SMS, Telegram or email, when your account sends from a shared number, and after a send-time fallback. V2's request-otp and resend-otp return the same value. The dashboard does not yet show which template each OTP send used.
Variables
- Values are non-empty strings of 15 characters or fewer. Arabic letters count one each.
- No line breaks, tabs, or four spaces in a row.
- Use only the entry's curated names (Library reference).
- Leave out fixed variables. If you send one, it must equal its fixed value:
validityinverify_codeis3 minutesor٣ دقائق.
Commercial International Bank (29 characters) and 1,250,000.00 EGP (16) are both too long; use a shorter value.
Choose the OTP language
Top-level otpLanguage picks the English or Arabic variant for one request. When absent or null, the pipeline's OTP Language (Basic Details) picks it. template.language is refused; use otpLanguage.
Set a pipeline default
When a transaction or attempt is created, Akedly uses the first of:
- The
templatein the request. - The pipeline's default OTP template, with its stored defaults.
- The Built-in default.
A custom template, explicit or pipeline default, must be Approved in the OTP language. Otherwise creation returns HTTP 400 — TEMPLATE_NOT_APPROVED, or TEMPLATE_REJECTED if that language was rejected — never a fallback. See template errors.
To change a default's variables for one request, send that template explicitly.
In Authentication → Pipelines, open the pipeline, expand OTP Template, choose a template under Default template, and select Save changes. Only templates Approved in the pipeline's OTP Language, with a stored default for every non-fixed variable, can be chosen. Others show why, such as In review for English or Missing defaults: purpose. If the section shows Couldn't load the OTP template library, select Retry; custom templates stay unavailable until it loads.
A custom default needs WhatsApp among the pipeline's verification methods; otherwise the save is refused with TEMPLATE_PIPELINE_REQUIRES_WHATSAPP. Built-in default (akedly_otp_auth) can always be saved.
An unusable default blocks the pipeline
If the default belongs to a replaced WABA or your WABA is disconnected, every request on that pipeline that sends no template is refused with the matching 400. If the default has no approved variant in the OTP language, requests in that language that send no template are refused. Both apply to dev-mode test pairs until you fix the template or switch back to the Built-in default. Removing the template in Akedly clears it from every pipeline.
Set the pipeline default.
Add a missing language from a pipeline
If you switch a pipeline's OTP Language to a language its default template doesn't have yet, the OTP Template section says so and offers Add Arabic (or Add English).
- Select Add Arabic. The template's Edit defaults dialog opens with Add Arabic already checked. Your pipeline edits are kept but not saved.
- Fill in the Arabic defaults and select Save defaults. The dashboard confirms Arabic sent to Meta for review.
- When Meta approves Arabic (usually within minutes; Akedly emails you), select Save changes on the pipeline. Until then the section can't be saved with this template; choose the Built-in default if you need to save now.
The action appears only after the template library loads, when the target language is an available variant for that entry but has never been added, you have whatsapp:update, and the template belongs to your current WABA. A language that is In review, Rejected or Missing on Meta is fixed from OTP Templates instead.
When a template cannot be used
Refused before anything is sent
Nothing is sent or billed
Akedly checks the selection — from the request or the pipeline default (VLite uses the pipeline default only) — before it creates the transaction, whatever channel the OTP would have used. A problem returns HTTP 400 on V1, V1.2, V2, and VLite, with no message sent and no billing. Branch on code, never on the message.
| Status | Code | Cause | What to do |
|---|---|---|---|
| 400 | TEMPLATE_UNKNOWN | The id is malformed, not found, or does not belong to your account. | Copy the Template id from My templates. |
| 400 | TEMPLATE_ARCHIVED | The template was removed. | Add it again; the new copy has a new id. |
| 400 | TEMPLATE_WABA_REQUIRED | Your own WABA is not active, or its token cannot be read. | Connect or repair the WABA. |
| 400 | TEMPLATE_WABA_CHANGED | The template was created on a previous WABA. | Remove it and add it again. |
| 400 | TEMPLATE_NOT_APPROVED | No approved variant exists for the OTP language, or Meta is still reviewing it. | Wait for Approved. If it is Missing on Meta, retry Meta setup or remove and re-add the template, then add the OTP language if needed. |
| 400 | TEMPLATE_REJECTED | Meta rejected the variant for the OTP language. | Remove it and add it again. |
| 400 | TEMPLATE_VARIABLE_MISSING | A required variable has no request value and no stored default. | Pass the variable or store a default. |
| 400 | TEMPLATE_VARIABLE_INVALID | The variable name or value is invalid, a fixed value changed, or the object contains language or another unsupported key. | Read details and correct the request. |
Fallback at send time
This is rare, because approval is checked at creation. If the approved variant becomes unusable or Meta refuses it during sending, Akedly sends the Built-in default once, in the same language, on your WABA. The fallback is logged and Akedly's operations team is alerted. The API response you already received still shows the original data.template. If that send also fails, normal WhatsApp recovery continues with the Built-in default, which can use Akedly's shared number.
Shared senders always send the Built-in default
Custom wording is only sent from your own WABA's phone number. For an account with an active own WABA, if routing sends the OTP through Akedly's shared number or a send falls back to it, the selection is still checked and echoed but the message uses the Built-in default. Without an active own WABA, selecting a custom template is refused with TEMPLATE_WABA_REQUIRED (see above). On a legacy manually assigned shared phone, the Built-in default is also sent in English, whatever the OTP language.
Remove and re-add
Remove archives the template in Akedly at once: its Template id stops working (TEMPLATE_ARCHIVED), and every pipeline using it as a default goes back to the Built-in default. Akedly attempts deletion on Meta only when the template still belongs to your active WABA. If it belongs to a replaced WABA, Akedly archives it locally without calling Meta. If Meta refuses an attempted deletion, delete it in WhatsApp Business Manager. Meta blocks the name for about 30 days, so adding the same entry again creates a new Meta name and a new Template id — update your integration.
After you change WhatsApp Business Account
If you reconnect a different WABA through Embedded Signup, every template from the old one shows Re-add after WhatsApp change, and requests or pipeline defaults that use it are refused with TEMPLATE_WABA_CHANGED. Remove it and add it again. If you disconnect without reconnecting, they are refused with TEMPLATE_WABA_REQUIRED, and the pipeline section shows Custom OTP templates need your own WhatsApp Business Account. with a Use Built-in default button. A transaction created before the change sends the Built-in default in its selected language.
Library reference
Every entry below can be added from Browse library. The code slot is where Meta places the OTP; you never pass it. Types are Meta's descriptions, not format checks.
| Entry | Code slot | Variable name and type | Languages |
|---|---|---|---|
delivery_code_1 | 1 | None | en, ar |
delivery_code_2 | 6 | customerName TEXT, orderInfo TEXT, estimatedDeliveryDate DATE, deliveryStartTime DATE, deliveryEndTime DATE | en, ar |
delivery_code_3 | 2 | arrivalDate DATE | en, ar |
delivery_code_4 | 1 | None | en, ar |
delivery_code_5 | 3 | itemCount NUMBER, orderId TEXT | en, ar |
delivery_code_6 | 3 | orderId TEXT, deliveryDate DATE | en, ar |
in_person_banking_user_verification | 1 | None | en, ar |
login_code | 1 | None | en |
verify_account | 4 | action TEXT, accountName TEXT, linkedService TEXT, securityContact TEXT | en |
verify_account_2 | 4 | action TEXT, accountName TEXT, linkedService TEXT | en, ar |
verify_code | 1 | purpose TEXT, validity TEXT, supportPhone PHONE_NUMBER | en, ar |
verify_code_1 | 1 | None | en, ar |
verify_otp_usecase | 1 | purpose TEXT | en, ar |
verify_password_recovery | 1 | None | en, ar |
verify_transaction_1 | 1 | None | en, ar |
verify_transaction_2 | 1 | amount AMOUNT | en, ar |
verify_transaction_3 | 1 | amount AMOUNT, cardNumber CARD_NUMBER | en, ar |
verify_transaction_4 | 1 | amount AMOUNT, accountNumber NUMBER | en, ar |
verify_transfer_1 | 1 | amount AMOUNT | en, ar |
validity in verify_code is fixed. verify_otp_usecase already includes its security sentence, so the security option adds nothing to it.
Localized parts
| Language | Security sentence | Expiry footer | Button |
|---|---|---|---|
| en | For your security, do not share this code. | Expires in 3 minutes. | Copy code |
| ar | لأسباب تتعلق بأمانك، يرجى عدم مشاركة هذا الرمز. | تنتهي صلاحيتها خلال ٣ دقائق. | نسخ الرمز |
For verify_otp_usecase, the Arabic sentence already in the body is للحفاظ على أمانك، لا تشارك هذا الرمز.
