> ## 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.

# المصادقة الصوتية

> تحقّق من هوية شخص عبر الهاتف — تتّصل Wave به، فيضغط للموافقة، وتصلك نتيجة موقّعة.

**المصادقة الصوتية** هي أداة *اتصال-للموافقة* صادرة. يطلب نظامك من Wave التحقّق من شخص؛
فتتّصل Wave بذلك الرقم، وتُشغّل رسالة قصيرة ثنائية اللغة تصف ما تجري الموافقة عليه، ثم
تلتقط ضغطة المفتاح وتُعيد نتيجة موثوقة قابلة للقراءة آليًا عبر webhook موقّع (وواجهة
استعلام عن الحالة).

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

<Note>
  هذا مسار صادر يبدأ عبر الواجهة (Wave تتّصل بالمستخدم). وهو يختلف عن
  [قوائم الرد الصوتي](/ar/voice/ivr) (حيث يتّصل المتصل بك). المصادقة الصوتية مُقيّدة —
  اطلب من جهة تواصلك في Wave تفعيلها لحسابك.
</Note>

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

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

## إنشاء طلب تحقّق (challenge)

```bash theme={null}
curl https://api.wave.sa/v1/voice-auth/challenges \
  -H "Authorization: Bearer sk_live_…" \
  -H "Idempotency-Key: 4e1a…" \
  -H "Content-Type: application/json" \
  -d '{
    "phone_number": "+9665XXXXXXXX",
    "locale": "ar",
    "context": { "action": "login", "app_name": "Wave Secure" },
    "assurance": "standard",
    "ttl_seconds": 90,
    "retry_on_no_answer": 0,
    "metadata": { "ref": "REF-8842" }
  }'
```

| الحقل                | مطلوب | ملاحظات                                                                                          |
| -------------------- | ----- | ------------------------------------------------------------------------------------------------ |
| `phone_number`       | ✅     | بصيغة E.164 (`+9665…`). الرقم الذي تتّصل به Wave.                                                |
| `locale`             | —     | `ar` (افتراضي) أو `en` — لغة الرسالة.                                                            |
| `context`            | —     | `{ action, app_name, reference }` تُدرَج في الرسالة. **بلا أسرار** — لا كلمة مرور/OTP/رقم بطاقة. |
| `assurance`          | —     | `standard` (اضغط 1) أو `number_match` (انظر أدناه). الافتراضي `standard`.                        |
| `ttl_seconds`        | —     | من 30 إلى 180، الافتراضي 90. ينتهي الطلب بعدها.                                                  |
| `retry_on_no_answer` | —     | من 0 إلى 2، الافتراضي 0 — إعادة اتصال تلقائية عند عدم الردّ/الانشغال، ضمن مدّة الصلاحية.         |
| `idempotency_key`    | —     | أرسله كترويسة `Idempotency-Key`؛ تُعيد المحاولة نفس الطلب دون مكالمة ثانية.                      |
| `metadata`           | —     | بيانات معتمة؛ تُعاد في الـ webhook.                                                              |

تُعيد **`201`** معرّف الطلب و(في وضع `number_match`) الأرقام المطلوب عرضها:

```json theme={null}
{ "challenge_id": "9f1c…", "state": "initiated", "expires_at": "2026-09-01T12:01:30Z" }
```

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

يصل الطلب إلى حالة نهائية واحدة `state`:

| الحالة                          | المعنى                                        | تمنح الوصول؟ |
| ------------------------------- | --------------------------------------------- | ------------ |
| `approved`                      | وافق المستخدم (ضغط 1، أو أدخل أرقام المطابقة) | ✅ **نعم**    |
| `declined`                      | رفض المستخدم (ضغط 2)                          | لا           |
| `timeout`                       | ردّ لكن دون إدخال صالح بعد إعادة المحاولة     | لا           |
| `no_answer` / `busy` / `failed` | لم تتّصل المكالمة                             | لا           |
| `machine_detected`              | ردّ بريد صوتي/آلة                             | لا           |
| `expired`                       | لا قرار قبل `expires_at`                      | لا           |

<Warning>
  `approved` و`declined` فقط قراران صريحان من المستخدم. عامِل **كل حالة أخرى كعدم منح**
  وافشل بأمان. وتحقّق دائمًا من النتيجة عبر واجهة الحالة قبل اعتماد الموافقة — دفاعًا في
  العمق.
</Warning>

### كشف الردّ الآلي (AMD)

قبل أي رسالة، تُصنّف Wave من ردّ على المكالمة. ردّ **الآلة أو البريد الصوتي** ينتهي إلى
`machine_detected` ولا تُشغَّل رسالة الموافقة **أبدًا** — فلا يمكن لبريد صوتي التقاط ضغطة
أو ترك موافقة. هذا مُطبَّق، وليس بأفضل جهد.

## المطابقة الرقمية عالية التوكيد

للإجراءات عالية القيمة (المدفوعات، تغييرات المشرف)، اضبط `assurance: "number_match"`.
تتضمّن استجابة `201` قيمة `match_digits` (مثل `"47"`) — اعرضها لمستخدمك. تطلب المكالمة
حينها من المتّصل **إدخال تلك الأرقام بالضبط للموافقة**. أي إدخال خاطئ لا يوافق — وهذا
يُبطل إرهاق المصادقة ("فقط اضغط 1") والموافقات العرضية.

## webhook النتيجة

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

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

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

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

```bash theme={null}
curl https://api.wave.sa/v1/voice-auth/challenges/9f1c… \
  -H "Authorization: Bearer sk_live_…"
```

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

## المعرّف الفريد للطلب، حدود المعدّل، ومكافحة الإساءة

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

## الأخطاء

| الرمز                     | HTTP | متى                                             |
| ------------------------- | ---- | ----------------------------------------------- |
| `VOICE_AUTH_DISABLED`     | 503  | غير مُفعّل لهذا الحساب                          |
| `SANDBOX_KEY_NOT_ALLOWED` | 400  | استُخدم مفتاح تجريبي                            |
| `VALIDATION_ERROR`        | 400  | رقم/لغة/مدّة غير صالحة، أو `context` يحوي سرًّا |
| `VOICE_AUTH_BLOCKED`      | 403  | الرقم الهدف محظور                               |
| `VOICE_AUTH_RATE_LIMITED` | 429  | تجاوُز حدّ لكل رقم/جهة إصدار                    |
| `VOICE_AUTH_NOT_FOUND`    | 404  | لا يوجد طلب بهذا المعرّف لمؤسّستك               |
