> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wave.sa/llms.txt
> Use this file to discover all available pages before exploring further.

# التحقّق و OTP

> أكّد امتلاك الهاتف — ترسل Wave رمزًا عبر WhatsApp، يُدخله المستخدم، تصلك نتيجة موقّعة.

**التحقّق** هو أداة أساسية قائمة على الرسائل لـ *رمز الاستخدام الواحد*. يطلب نظامك من Wave
إرسال رمز تحقّق إلى رقم هاتف عبر WhatsApp؛ يقرأه المستخدم ويُدخله؛ وتتحقّق من الرمز
وتحصل على نتيجة موثوقة قابلة للقراءة آليًا عبر webhook موقّع (وواجهة استعلام عن الحالة).

هو عامل تأكيد امتلاك الهاتف لإنشاء حساب والمصادقة المتقدمة وإعادة تعيين كلمة المرور —
أداة أمان، وليست قائمة رد صوتي. ولأن النتيجة تحكم الوصول، فهي **مصمّمة لتفشل بأمان**: الرمز
الدقيق في الوقت المناسب فقط في حدود المحاولات يمنح الوصول، والرمز الخاطئ أو المنتهي لا يفعل.

> [!NOTE]
>
> هذا مسار يبدأ عبر الواجهة (Wave ترسل رمزًا للمستخدم). وهو يختلف عن
> [المصادقة الصوتية](https://docs.wave.sa/ar/voice/voice-auth.md) (أداة اتصال-للموافقة).
> التحقّق مُقيّد — اطلب من جهة تواصلك في Wave تفعيله لحسابك.

## المصادقة والفئات

كل مسار ضمن `/v1/verify` يتطلّب مفتاح **إنتاج** (`sk_live_`) مع صلاحية
**`verify:write`** (أو `verify:read` للاستعلام). لا تستطيع المفاتيح التجريبية
إرسال رموز تحقّق حقيقية.

## إنشاء تحقّق

```bash cURL theme={null}
curl https://api.wave.sa/v1/verify \
  -H "Authorization: Bearer $WAVE_API_KEY" \
  -H "Idempotency-Key: 4e1a…" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+9665XXXXXXXX",
    "channel": "whatsapp",
    "locale": "ar",
    "ttl_seconds": 300,
    "metadata": { "ref": "REF-8842" }
  }'
```

```javascript Node.js theme={null}
const res = await fetch("https://api.wave.sa/v1/verify", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WAVE_API_KEY}`,
    "Idempotency-Key": "4e1a…",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phone_number: "+9665XXXXXXXX",
    channel: "whatsapp",
    locale: "ar",
    ttl_seconds: 300,
    metadata: {
      ref: "REF-8842",
    },
  }),
});
console.log(await res.json());
```

```python Python theme={null}
import os

import requests

res = requests.post(
    "https://api.wave.sa/v1/verify",
    headers={
        "Authorization": f"Bearer {os.environ['WAVE_API_KEY']}",
        "Idempotency-Key": "4e1a…",
    },
    json={
        "phone_number": "+9665XXXXXXXX",
        "channel": "whatsapp",
        "locale": "ar",
        "ttl_seconds": 300,
        "metadata": {
            "ref": "REF-8842",
        },
    },
)
print(res.json())
```

| الحقل | مطلوب | ملاحظات |
| - | - | - |
| `phone_number` | ✅ | بصيغة E.164 (`+9665…`). الرقم الذي ترسل Wave الرمز إليه. |
| `channel` | — | `whatsapp` (افتراضي). قناة التسليم. |
| `locale` | — | `ar` (افتراضي) أو `en` — لغة الرسالة. |
| `ttl_seconds` | — | من 60 إلى 900، الافتراضي 300. ينتهي الرمز بعدها. |
| `idempotency_key` | — | أرسله كترويسة `Idempotency-Key`؛ تُعيد المحاولة نفس التحقّق دون رمز ثانٍ أو رسالة ثانية. |
| `metadata` | — | بيانات معتمة؛ تُعاد في الـ webhook. |

تُعيد **`201`** معرّف التحقّق وميعاد انتهاؤه:

```json theme={null}
{ "verification_id": "ver_9f1c…", "state": "initiated", "expires_at": "2026-09-01T12:05:00Z" }
```

## النتيجة — الفشل الآمن

يصل التحقّق إلى حالة نهائية واحدة `state`:

| الحالة | المعنى | تمنح الوصول؟ |
| - | - | - |
| `verified` | أدخل المستخدم الرمز الصحيح في الوقت المناسب | ✅ **نعم** |
| `failed` | الرمز خاطئ (انتهت حدود المحاولات) | لا |
| `expired` | لا توجد فحوصات صحيحة قبل `expires_at` | لا |
| `max_attempts` | تم الوصول إلى خمس محاولات خاطئة | لا |
| `canceled` | استُبدل برمز جديد لنفس الرقم | لا |

> [!WARNING]
>
> `verified` فقط يمنح الوصول. عامِل **كل حالة أخرى كعدم منح** وافشل بأمان.
> وتحقّق دائمًا من النتيجة عبر واجهة الحالة قبل اعتماد الموافقة — دفاعًا في
> العمق.

## فحص الرمز

```bash cURL theme={null}
curl https://api.wave.sa/v1/verify/check \
  -H "Authorization: Bearer $WAVE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "verification_id": "ver_9f1c…",
    "code": "123456"
  }'
```

```javascript Node.js theme={null}
const res = await fetch("https://api.wave.sa/v1/verify/check", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WAVE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    verification_id: "ver_9f1c…",
    code: "123456",
  }),
});
console.log(await res.json());
```

```python Python theme={null}
import os

import requests

res = requests.post(
    "https://api.wave.sa/v1/verify/check",
    headers={
        "Authorization": f"Bearer {os.environ['WAVE_API_KEY']}",
    },
    json={
        "verification_id": "ver_9f1c…",
        "code": "123456",
    },
)
print(res.json())
```

| الحقل | مطلوب | ملاحظات |
| - | - | - |
| `verification_id` | ✅ | من استجابة الإنشاء. |
| `code` | ✅ | رمز من 6 أرقام أدخله المستخدم. |

**`200`** تُعيد حالة التحقّق وقيمة منطقية:

```json theme={null}
{ "verification_id": "ver_9f1c…", "state": "verified", "verified": true }
```

على رمز خاطئ، تتضمّن الاستجابة `state: "awaiting_check"` و`verified: false`؛ يزداد العداد.
بعد خمس محاولات خاطئة، تصبح `state` هي `max_attempts` وتفشل الفحوصات اللاحقة.

## webhook النتيجة

```json theme={null}
{
  "event": "verify.completed",
  "data": {
    "verification_id": "ver_9f1c…",
    "state": "verified",
    "decided_at": "2026-09-01T12:02:10Z",
    "attempts": 1,
    "channel": "whatsapp"
  }
}
```

عند بلوغ حالة نهائية ترسل Wave حدث **`verify.completed`** موقّعًا إلى نقطة webhook
الخاصّة بمشروعك. تحقّق من كل تسليم:

* **`X-Wave-Signature`** — `sha256=…`، توقيع HMAC-SHA256 للمحتوى الخام بسرّ التوقيع
  الخاصّ بنقطتك. ارفض أي عدم تطابق.
* **`X-Wave-Timestamp`** — بالثواني (unix)؛ ارفض التسليمات الأقدم من نافذتك (مثلًا 5 دقائق).
* **`X-Wave-Event-Id`** — ثابت عبر إعادات الإرسال؛ استخدمه لمنع التكرار.

الـ webhook إشعار — أعد التأكيد عبر واجهة الحالة قبل المنح.

## الاستعلام عن الحالة

```bash cURL theme={null}
curl https://api.wave.sa/v1/verify/ver_9f1c… \
  -H "Authorization: Bearer $WAVE_API_KEY"
```

```javascript Node.js theme={null}
const res = await fetch("https://api.wave.sa/v1/verify/ver_9f1c…", {
  headers: {
    Authorization: `Bearer ${process.env.WAVE_API_KEY}`,
  },
});
console.log(await res.json());
```

```python Python theme={null}
import os

import requests

res = requests.get(
    "https://api.wave.sa/v1/verify/ver_9f1c…",
    headers={
        "Authorization": f"Bearer {os.environ['WAVE_API_KEY']}",
    },
)
print(res.json())
```

تُعيد الحالة الحالية `state` وعدد المحاولات والقناة والطوابع الزمنية و(عند الانتهاء)
القرار. مقصورة على مؤسّستك فقط.

## رمز واحد لكل رقم وانتهاء الرمز والمعرّف الفريد

* **رمز واحد لكل رقم** — `POST /v1/verify` جديد لنفس الرقم يستبدل
  الرمز السابق ويلغي التحقّق القديم.
* **انتهاء الرمز** — ينتهي الرمز في `expires_at`. فحص بعدها يُعيد
  `state: "expired"` دون زيادة المحاولات.
* **Idempotency** — أرسل `Idempotency-Key`؛ تُعيد المحاولة بنفس المفتاح نفس التحقّق
  و**لا ترسل رمز ثانٍ أو رسالة ثانية**.

## حدود المعدّل ومكافحة الإساءة

* **حدود لكل رقم** — تحدّد Wave التحقّقات لرقم واحد لكل مفتاح، وتحجب
  الأرقام/البادئات المُسيئة، لمنع قصف الرموز والاحتيال. تجاوز → **`429`**.
* **حد محاولات لكل تحقّق** — خمس محاولات خاطئة → `max_attempts`. لا فحوصات لاحقة.
* **حدود الحجم العام** — تتبع Wave الحجم الإجمالي لكل مفتاح.
  الزيادة المستدامة → **`429`**.

## الأخطاء

| الرمز | HTTP | متى |
| - | - | - |
| `VERIFY_DISABLED` | 503 | غير مُفعّل لهذا الحساب |
| `SANDBOX_KEY_NOT_ALLOWED` | 400 | استُخدم مفتاح تجريبي |
| `VALIDATION_ERROR` | 400 | رقم/لغة/مدّة غير صالحة |
| `VERIFY_BLOCKED` | 403 | الرقم الهدف محظور |
| `VERIFY_RATE_LIMITED` | 429 | تجاوُز حدّ لكل رقم/جهة إصدار |
| `VERIFY_NOT_FOUND` | 404 | لا يوجد تحقّق بهذا المعرّف |
| `INVALID_CODE` | 400 | الرمز بصيغة خاطئة أو خاطئ |
| `MAX_ATTEMPTS_EXCEEDED` | 400 | تجاوز التحقّق حد المحاولات |
| `VERIFICATION_EXPIRED` | 400 | انتهى صلاح التحقّق |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.