پیامک و OTP کاوهنگار
kavenegar-otpراهنماارسال OTP با Verify Lookup کاوهنگار: قالب تأییدشده، نرمالسازی شماره ۰۹، بررسی return.status و نمونه TypeScript آماده. کلید API فقط سمت سرور.
کی به کار میآد
- ورود با کد یکبارمصرف
- تأیید شماره موبایل ایرانی
- ارسال پیامک قالبی کاوهنگار
- وقتی مدل API خارجی SMS مینویسه
برچسبها
راهنما فرانتمتر نداره و خودکار فعال نمیشه؛ از CLAUDE.md یا AGENTS.md به آن لینک بدید تا مدل در هر جلسه بخوندش.
Kavenegar SMS / OTP (implementation guide)
Kavenegar is an Iranian SMS gateway. The Verify Lookup endpoint sends a pre-approved template SMS containing one or more dynamic tokens (for example OTP codes). All calls are plain GET requests authenticated via an API key embedded in the URL path.
Hand this document to an AI or developer to wire OTP / verify SMS into any Node.js or Next.js project.
Environment variables
Add these to your .env / .env.local:
1KAVENEGAR_API_KEY=<your-api-key-from-kavenegar-panel>2KAVENEGAR_VERIFY_TEMPLATE=<template-name-approved-in-kavenegar-panel>KAVENEGAR_API_KEY: found in your Kavenegar dashboard under API Keys.KAVENEGAR_VERIFY_TEMPLATE: the exact name of the template you created and got approved in the Kavenegar panel (e.g.verify).
Keep the API key server-side only. Never expose it to the browser.
API endpoint
1GET https://api.kavenegar.com/v1/{API_KEY}/verify/lookup.jsonAll parameters are passed as query string parameters.
Required query params
| Param | Type | Description |
|---|---|---|
receptor | string | Recipient phone number (see Phone format) |
token | string | The primary dynamic value (e.g. the OTP code) |
template | string | Name of the approved template in your Kavenegar account |
Optional query params
| Param | Type | Description | |
|---|---|---|---|
token2 | string | Second dynamic value in the template | |
token3 | string | Third dynamic value | |
token10 | string | Slot 10 dynamic value | |
token20 | string | Slot 20 dynamic value | |
type | `'sms' \ | 'call'` | Delivery channel; defaults to SMS |
tag | string | Custom tag for tracking |
Response shape
1{2 "return": {3 "status": 200,4 "message": "تایید شد"5 },6 "entries": [7 {8 "messageid": 123456789,9 "message": "your otp is 45123",10 "status": 5,11 "statustext": "ارسال به مخابرات",12 "sender": "10004346",13 "receptor": "09123456789",14 "date": 1715000000,15 "cost": 116 }17 ]18}return.status === 200means the request was accepted.Any other
return.statusvalue is an error;return.messagedescribes it.HTTP-level errors (non-2xx) also indicate failure.
Phone number format
Kavenegar accepts these formats for receptor:
| Input format | Expected by Kavenegar | Notes |
|---|---|---|
989123456789 (E.164 without +) | 09123456789 | Strip 98, prepend 0 |
+989123456789 | 09123456789 | Strip +98, prepend 0 |
09123456789 | 09123456789 | Already correct |
| International non-IR | 00[country][number] | Prepend 00 if not already present |
TypeScript implementation
1// lib/kavenegar.ts23export interface SendOTPParams {4 receptor: string;5 token: string;6 template?: string; // overrides KAVENEGAR_VERIFY_TEMPLATE env var7 token2?: string;8 token3?: string;9 token10?: string;10 token20?: string;11 type?: "sms" | "call";12 tag?: string;13}1415export interface KavenegarResponse {16 return: {17 status: number;18 message: string;19 };20 entries?: Array<{21 messageid: number;22 message: string;23 status: number;24 statustext: string;25 sender: string;26 receptor: string;27 date: number;28 cost: number;29 }>;30}3132export async function sendOTPWithKavenegar(33 params: SendOTPParams34): Promise<KavenegarResponse> {35 const apiKey = process.env.KAVENEGAR_API_KEY;36 const defaultTemplate = process.env.KAVENEGAR_VERIFY_TEMPLATE;3738 if (!apiKey) throw new Error("KAVENEGAR_API_KEY is not set");39 if (!defaultTemplate) throw new Error("KAVENEGAR_VERIFY_TEMPLATE is not set");4041 const baseUrl = `https://api.kavenegar.com/v1/${apiKey}/verify/lookup.json`;42 const searchParams = new URLSearchParams({43 receptor: params.receptor.replace(/\s/g, ""),44 token: params.token,45 template: params.template ?? defaultTemplate,46 });4748 if (params.token2) searchParams.set("token2", params.token2);49 if (params.token3) searchParams.set("token3", params.token3);50 if (params.token10) searchParams.set("token10", params.token10);51 if (params.token20) searchParams.set("token20", params.token20);52 if (params.type) searchParams.set("type", params.type);53 if (params.tag) searchParams.set("tag", params.tag);5455 const response = await fetch(`${baseUrl}?${searchParams.toString()}`, {56 method: "GET",57 });5859 if (!response.ok) {60 throw new Error(61 `Kavenegar HTTP error: ${response.status} ${response.statusText}`62 );63 }6465 const data: KavenegarResponse = await response.json();6667 if (data.return?.status !== 200) {68 throw new Error(69 `Kavenegar rejected request: ${data.return?.message ?? "Unknown error"}`70 );71 }7273 return data;74}7576/** Normalises a phone number to the format Kavenegar expects. */77export function toKavenegarReceptor(phone: string): string {78 const cleaned = phone.replace(/\D/g, "");7980 // Iranian mobile: 989xxxxxxxxx → 09xxxxxxxxx81 if (cleaned.startsWith("98") && cleaned.length === 12) {82 return `0${cleaned.slice(2)}`;83 }8485 // Already local Iranian format86 if (cleaned.startsWith("09") && cleaned.length === 11) {87 return cleaned;88 }8990 // International: ensure 00 prefix91 if (cleaned.startsWith("00")) return cleaned;92 return `00${cleaned}`;93}Usage example
1import { sendOTPWithKavenegar, toKavenegarReceptor } from "@/lib/kavenegar";23const otp = "45123";4const phone = "989123456789";56await sendOTPWithKavenegar({7 receptor: toKavenegarReceptor(phone),8 token: otp,9 // template: "custom-template", // optional override10});Error handling notes
Always check
data.return.status === 200after a successful HTTP response. Kavenegar returns HTTP 200 even for logical errors, putting the real status inreturn.status.Common non-200 Kavenegar status codes:
401: invalid API key404: receptor invalid411: template not found417: template tokens mismatch
Security checklist
[ ]
KAVENEGAR_API_KEYis server-side only.[ ] OTP is generated and stored server-side; never accept a client-supplied code as truth.
[ ] Rate-limit send attempts per phone and per IP.
[ ] Expire OTPs (e.g. 2–5 minutes) and limit verification attempts.
[ ] Prefer the approved Verify Lookup template over raw SMS for OTP flows.
[ ] Do not log full OTP codes or API keys.
Official resources
نمونه
پرامپت: «OTP را با کاوهنگار بفرست»
بدون مهارت
SMS خام با متن آزاد و شماره به فرمت +98
با مهارت
verify/lookup با template تأییدشده و receptor به شکل ۰۹xxxxxxxxx
نمونه برای نشان دادن جهت تغییره؛ خروجی واقعی به مدل و پرامپت شما بستگی داره.
نصب
با CLI
این دستور فایل را در
docs/kavenegar-otp.mdمینویسه. CLI به init نیاز نداره؛ فقط باید داخل پوشهی پروژه باشید.$npx vibefarsi add kavenegar-otpدستی
محتوای تب kavenegar-otp.md را کپی کنید و در مسیر ابزار خودتون بگذارید:
- Claude Codeفایل را در
docs/kavenegar-otp.mdبگذارید و این خط را بهCLAUDE.mdاضافه کنید:@docs/kavenegar-otp.md - Cursorدر
.cursor/rules/kavenegar-otp.mdcباalwaysApply: trueبالای فایل - Codex و بقیهمتن را در
AGENTS.mdبگذارید یا از همانجا به فایل لینک بدید
کپی کل فایلregistry json- Claude Codeفایل را در
میخواید همهی قوانین را یکجا داشته باشید؟ قوانین فارسی برای CLAUDE.md خلاصهی همهی مهارتها در یک صفحهست و قوانین کرافت رابط طرف طراحی را پوشش میده. npx vibefarsi init هر دو را داخل پروژه مینویسه.