diff --git a/api-reference/classify.mdx b/api-reference/classify.mdx
index ac3d730..a1099ab 100644
--- a/api-reference/classify.mdx
+++ b/api-reference/classify.mdx
@@ -1,16 +1,16 @@
---
-title: "Classify Comment"
+title: "Classify comment"
sidebarTitle: "POST /v1/classify"
-description: "Classify an Arabic or English comment by intent, sentiment, dialect, and toxicity in a single API call."
+description: "Classify an Arabic or English comment by intent, sentiment, dialect, and priority in a single API call."
api: "POST https://api.trynawa.com/v1/classify"
---
-Classify any comment with a single request. Returns intent, sentiment, dialect, and toxicity analysis. Arabic comments are routed to HUMAIN's ALLaM model for dialect detection. English comments are routed to Claude for high-accuracy classification.
+Classify any comment with a single request. Returns intent, sentiment, dialect, priority, and an optional suggested reply. Arabic comments are routed to HUMAIN's ALLaM model for dialect detection. English comments are routed to Claude for high-accuracy classification.
## Request
- Cost: **$0.006** per request. Semantic cache hits are free (`X-NAWA-Cache: HIT`).
+ Cost: **$0.006** per request (6 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
### Headers
@@ -19,15 +19,20 @@ Classify any comment with a single request. Returns intent, sentiment, dialect,
|--------|----------|-------------|
| `Authorization` | Yes | `Bearer nawa_live_sk_xxx` or `Bearer nawa_test_sk_xxx` |
| `Content-Type` | Yes | `application/json` |
+| `X-Request-Id` | No | Custom request ID. Auto-generated if omitted. |
+
+### Query parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `provider` | string | No | Force a specific AI provider: `claude`, `gemini`, `allam`. Useful for A/B testing. If omitted, NAWA routes automatically based on language. |
### Body parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `text` | string | Yes | The comment text to classify. Max 5,000 characters. |
-| `platform` | string | No | Source platform: `youtube`, `instagram`, `twitter`, `facebook`. Improves accuracy with platform-specific context. |
-| `channel_id` | string | No | Your channel or account identifier for analytics grouping. |
-| `metadata` | object | No | Arbitrary key-value metadata to attach to the classification. |
+| `text` | string | Yes | The comment text to classify. Must be non-empty. |
+| `context` | object | No | Arbitrary context object to pass alongside classification. |
### Example request
@@ -38,9 +43,16 @@ curl -X POST https://api.trynawa.com/v1/classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
- "text": "متى الجزء الثاني؟",
- "platform": "youtube",
- "channel_id": "ch_123"
+ "text": "متى الجزء الثاني؟"
+ }'
+```
+
+```bash cURL (with provider override)
+curl -X POST "https://api.trynawa.com/v1/classify?provider=allam" \
+ -H "Authorization: Bearer nawa_test_sk_xxx" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "text": "متى الجزء الثاني؟"
}'
```
@@ -50,9 +62,7 @@ import { Nawa } from '@nawalabs/sdk'
const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
const { data, error } = await nawa.classify({
- text: 'متى الجزء الثاني؟',
- platform: 'youtube',
- channelId: 'ch_123'
+ text: 'متى الجزء الثاني؟'
})
```
@@ -62,9 +72,7 @@ from nawa import Nawa
nawa = Nawa(api_key="your_api_key")
result = nawa.classify(
- text="متى الجزء الثاني؟",
- platform="youtube",
- channel_id="ch_123"
+ text="متى الجزء الثاني؟"
)
```
@@ -78,22 +86,28 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "متى الجزء الثاني؟",
- "intent": "question",
- "intent_confidence": 0.97,
+ "id": "cls_nw_a1b2c3d4e5f67890abcd",
+ "object": "classification",
+ "intent": ["question"],
"sentiment": "neutral",
- "sentiment_confidence": 0.91,
+ "language": "ar",
"dialect": "gulf",
"dialect_confidence": 0.95,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "requires_response": true,
+ "priority": "high",
+ "suggested_reply": {
+ "text": "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك",
+ "direction": "rtl"
+ },
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "tokens_used": null,
+ "cost_usd": 0.006,
+ "credits_used": 6
},
"errors": [],
- "request_id": "req_abc123def456"
+ "request_id": "req_nw_a1b2c3d4e5f67890abcd"
}
```
@@ -105,33 +119,37 @@ result = nawa.classify(
| `X-RateLimit-Limit` | Rate limit ceiling for current window |
| `X-RateLimit-Remaining` | Requests remaining in current window |
| `X-RateLimit-Reset` | Window reset time (RFC 3339) |
-| `X-NAWA-Balance` | Current credit balance in USD |
-| `X-NAWA-Balance-Warning` | `low_balance` when below $5 |
-| `X-NAWA-Cache` | `HIT` if served from semantic cache (no cost) |
+| `X-NAWA-Provider` | AI provider used (`claude`, `gemini`, or `allam`) |
+| `X-NAWA-Cache` | `HIT` if served from semantic cache (no cost), `MISS` otherwise |
+| `X-NAWA-Latency` | Processing time in milliseconds |
+| `X-NAWA-Calibration-Version` | Calibration version applied to results |
### Result fields
| Field | Type | Description |
|-------|------|-------------|
-| `text` | string | The original input text |
-| `intent` | string | `question`, `complaint`, `praise`, `suggestion`, `spam`, `other` |
-| `intent_confidence` | number | Confidence score (0–1) |
+| `id` | string | Classification ID in `cls_nw_xxx` format |
+| `object` | string | Always `"classification"` |
+| `intent` | string[] | Detected intents, e.g. `["question"]` or `["question", "complaint"]` |
| `sentiment` | string | `positive`, `negative`, `neutral`, `mixed` |
-| `sentiment_confidence` | number | Confidence score (0–1) |
-| `dialect` | string \| null | `gulf`, `egyptian`, `levantine`, `msa`. Returns `null` for English text. |
+| `language` | string | `ar`, `en`, `mixed` |
+| `dialect` | string \| null | `gulf`, `egyptian`, `levantine`, `maghrebi`, `msa`. Returns `null` for English text. |
| `dialect_confidence` | number \| null | Confidence score (0-1). Returns `null` for English text. |
-| `toxicity` | string | `none`, `mild`, `moderate`, `severe` |
-| `toxicity_confidence` | number | Confidence score (0–1) |
-| `categories` | string[] | Content categories: `engagement`, `support`, `feedback`, `spam` |
-| `language` | string | Detected language code (e.g., `ar`, `en`) |
+| `requires_response` | boolean | Whether the comment needs a reply |
+| `priority` | string | `high`, `medium`, `low` |
+| `suggested_reply` | object \| null | Auto-generated reply with `text` and `direction` (`ltr` or `rtl`). May be `null`. |
+| `provider` | string | AI provider used: `claude`, `gemini`, or `allam` |
| `model` | string | Model version used for classification |
-| `cached` | boolean | Whether this was served from semantic cache |
+| `fallback_used` | boolean | Whether a fallback provider was used |
+| `tokens_used` | integer \| null | Token count (may be `null`) |
+| `cost_usd` | number | Cost in USD for this request |
+| `credits_used` | integer | Credits deducted |
### Error responses
| Status | Type | When |
|--------|------|------|
-| 400 | `invalid_request_error` | Missing `text`, invalid `platform`, text too long |
+| 400 | `invalid_request_error` | Missing `text`, invalid `provider`, text empty |
| 401 | `authentication_error` | Invalid or missing API key |
| 402 | `insufficient_credits` | No credits remaining |
| 429 | `rate_limit_error` | Rate limit exceeded |
@@ -145,7 +163,7 @@ result = nawa.classify(
curl -X POST https://api.trynawa.com/v1/classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
- -d '{"text": "This is hands down the best review I have seen on this phone. Subscribed!", "platform": "youtube"}'
+ -d '{"text": "This is hands down the best review I have seen on this phone. Subscribed!"}'
```
Response:
@@ -153,22 +171,25 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "This is hands down the best review I have seen on this phone. Subscribed!",
- "intent": "praise",
- "intent_confidence": 0.96,
+ "id": "cls_nw_en_praise_001abcdef",
+ "object": "classification",
+ "intent": ["praise"],
"sentiment": "positive",
- "sentiment_confidence": 0.98,
+ "language": "en",
"dialect": null,
"dialect_confidence": null,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "en",
- "model": "claude-v1",
- "cached": false
+ "requires_response": false,
+ "priority": "low",
+ "suggested_reply": null,
+ "provider": "claude",
+ "model": "claude-3-5-haiku-20241022",
+ "fallback_used": false,
+ "tokens_used": null,
+ "cost_usd": 0.006,
+ "credits_used": 6
},
"errors": [],
- "request_id": "req_en_praise_001"
+ "request_id": "req_nw_en_praise_001abcdef"
}
```
@@ -178,7 +199,7 @@ result = nawa.classify(
curl -X POST https://api.trynawa.com/v1/classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
- -d '{"text": "The audio quality is terrible in this one. Can barely hear anything after the 5 minute mark.", "platform": "youtube"}'
+ -d '{"text": "The audio quality is terrible in this one. Can barely hear anything after the 5 minute mark."}'
```
Response:
@@ -186,65 +207,38 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "The audio quality is terrible in this one. Can barely hear anything after the 5 minute mark.",
- "intent": "complaint",
- "intent_confidence": 0.94,
+ "id": "cls_nw_en_complaint_001abcd",
+ "object": "classification",
+ "intent": ["complaint"],
"sentiment": "negative",
- "sentiment_confidence": 0.96,
- "dialect": null,
- "dialect_confidence": null,
- "toxicity": "none",
- "toxicity_confidence": 0.97,
- "categories": ["feedback"],
"language": "en",
- "model": "claude-v1",
- "cached": false
- },
- "errors": [],
- "request_id": "req_en_complaint_001"
- }
- ```
-
-
-
- ```bash
- curl -X POST https://api.trynawa.com/v1/classify \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "What camera and lens setup are you using for these shots?", "platform": "youtube"}'
- ```
-
- Response:
- ```json
- {
- "success": true,
- "result": {
- "text": "What camera and lens setup are you using for these shots?",
- "intent": "question",
- "intent_confidence": 0.97,
- "sentiment": "neutral",
- "sentiment_confidence": 0.92,
"dialect": null,
"dialect_confidence": null,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "en",
- "model": "claude-v1",
- "cached": false
+ "requires_response": true,
+ "priority": "high",
+ "suggested_reply": {
+ "text": "Sorry about the audio issues! We're looking into it for the next video.",
+ "direction": "ltr"
+ },
+ "provider": "claude",
+ "model": "claude-3-5-haiku-20241022",
+ "fallback_used": false,
+ "tokens_used": null,
+ "cost_usd": 0.006,
+ "credits_used": 6
},
"errors": [],
- "request_id": "req_en_question_001"
+ "request_id": "req_nw_en_complaint_001abcd"
}
```
-
+
```bash
curl -X POST https://api.trynawa.com/v1/classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
- -d '{"text": "ما شاء الله عليك، محتوى رهيب!", "platform": "youtube"}'
+ -d '{"text": "ما شاء الله عليك، محتوى رهيب!"}'
```
Response:
@@ -252,32 +246,35 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "ما شاء الله عليك، محتوى رهيب!",
- "intent": "praise",
- "intent_confidence": 0.98,
+ "id": "cls_nw_gf_praise_001abcdef",
+ "object": "classification",
+ "intent": ["praise"],
"sentiment": "positive",
- "sentiment_confidence": 0.97,
+ "language": "ar",
"dialect": "gulf",
"dialect_confidence": 0.93,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "requires_response": false,
+ "priority": "low",
+ "suggested_reply": null,
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "tokens_used": null,
+ "cost_usd": 0.006,
+ "credits_used": 6
},
"errors": [],
- "request_id": "req_ghi789jkl012"
+ "request_id": "req_nw_gf_praise_001abcdef"
}
```
-
+
```bash
curl -X POST https://api.trynawa.com/v1/classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
- -d '{"text": "الصوت وحش أوي في الفيديو ده", "platform": "youtube"}'
+ -d '{"text": "الصوت وحش أوي في الفيديو ده"}'
```
Response:
@@ -285,32 +282,38 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "الصوت وحش أوي في الفيديو ده",
- "intent": "complaint",
- "intent_confidence": 0.94,
+ "id": "cls_nw_eg_complaint_001abcd",
+ "object": "classification",
+ "intent": ["complaint"],
"sentiment": "negative",
- "sentiment_confidence": 0.96,
+ "language": "ar",
"dialect": "egyptian",
"dialect_confidence": 0.97,
- "toxicity": "none",
- "toxicity_confidence": 0.95,
- "categories": ["feedback"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "requires_response": true,
+ "priority": "high",
+ "suggested_reply": {
+ "text": "معلش، هنحاول نحسن الصوت في الفيديو الجاي إن شاء الله",
+ "direction": "rtl"
+ },
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "tokens_used": null,
+ "cost_usd": 0.006,
+ "credits_used": 6
},
"errors": [],
- "request_id": "req_mno345pqr678"
+ "request_id": "req_nw_eg_complaint_001abcd"
}
```
-
+
```bash
curl -X POST https://api.trynawa.com/v1/classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
- -d '{"text": "لو سمحت حاول تحكي عن المطاعم بلبنان", "platform": "instagram"}'
+ -d '{"text": "لو سمحت حاول تحكي عن المطاعم بلبنان"}'
```
Response:
@@ -318,22 +321,25 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "لو سمحت حاول تحكي عن المطاعم بلبنان",
- "intent": "suggestion",
- "intent_confidence": 0.92,
+ "id": "cls_nw_lv_suggest_001abcde",
+ "object": "classification",
+ "intent": ["suggestion"],
"sentiment": "neutral",
- "sentiment_confidence": 0.88,
+ "language": "ar",
"dialect": "levantine",
"dialect_confidence": 0.96,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "requires_response": false,
+ "priority": "medium",
+ "suggested_reply": null,
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "tokens_used": null,
+ "cost_usd": 0.006,
+ "credits_used": 6
},
"errors": [],
- "request_id": "req_stu901vwx234"
+ "request_id": "req_nw_lv_suggest_001abcde"
}
```
diff --git a/api-reference/comments-reply.mdx b/api-reference/comments-reply.mdx
index d6321d7..f34f1d8 100644
--- a/api-reference/comments-reply.mdx
+++ b/api-reference/comments-reply.mdx
@@ -1,59 +1,78 @@
---
-title: "Reply to Comment"
-sidebarTitle: "POST /v1/comments/:id/reply"
-description: "Generate a context-aware reply to a comment in Arabic or English."
-api: "POST https://api.trynawa.com/v1/comments/{id}/reply"
+title: "Reply to comment"
+sidebarTitle: "POST /v1/comments/reply"
+description: "Classify a comment and generate a context-aware reply in Arabic or English."
+api: "POST https://api.trynawa.com/v1/comments/reply"
---
-Generate an AI-powered reply that matches the commenter's language and cultural context. For Arabic comments, replies match the detected dialect (Gulf, Egyptian, Levantine, MSA). For English comments, replies are natural and platform-appropriate. Language is auto-detected unless overridden.
+Classify a comment and generate an AI-powered reply in a single call. For Arabic comments, replies match the detected dialect (Gulf, Egyptian, Levantine, Maghrebi, MSA). For English comments, replies are natural and platform-appropriate.
- Cost: **$0.008** per request (8 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
+ Cost: **$0.008** per request (8 credits).
## Request
-### Path parameters
+### Query parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `id` | string | Yes | The comment ID to reply to. |
+| `provider` | string | No | Force a specific AI provider: `claude`, `gemini`, `allam`. If omitted, NAWA routes automatically. |
### Body parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `tone` | string | No | Reply tone: `friendly`, `professional`, `casual`, `formal`. Default: `friendly`. |
-| `max_length` | integer | No | Maximum reply length in characters. Default: 500. |
-| `context` | string | No | Additional context about the channel or video to improve reply relevance. |
-| `language` | string | No | Force reply language: `ar`, `en`, `auto`. Default: `auto` (matches commenter's language). |
+| `text` | string | Yes | The comment text to classify and reply to. Must be non-empty. |
+| `tone` | string | No | Reply tone: `friendly`, `professional`, `casual`, `formal`. Default: `professional`. |
+| `max_length` | integer | No | Maximum reply length in characters (1-2000). Default: `500`. |
+| `context` | object | No | Additional context with optional `platform` (string) and `brand_voice` (string) fields. |
### Example request
```bash cURL
-curl -X POST https://api.trynawa.com/v1/comments/cmt_abc123/reply \
+curl -X POST https://api.trynawa.com/v1/comments/reply \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
+ "text": "متى الجزء الثاني؟",
"tone": "friendly",
- "context": "Tech review channel focused on smartphones"
+ "context": {
+ "platform": "youtube",
+ "brand_voice": "Tech review channel focused on smartphones"
+ }
}'
```
```typescript TypeScript
-const { data, error } = await nawa.comments.reply('cmt_abc123', {
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.comments.reply({
+ text: 'متى الجزء الثاني؟',
tone: 'friendly',
- context: 'Tech review channel focused on smartphones'
+ context: {
+ platform: 'youtube',
+ brandVoice: 'Tech review channel focused on smartphones'
+ }
})
```
```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
result = nawa.comments.reply(
- comment_id="cmt_abc123",
+ text="متى الجزء الثاني؟",
tone="friendly",
- context="Tech review channel focused on smartphones"
+ context={
+ "platform": "youtube",
+ "brand_voice": "Tech review channel focused on smartphones"
+ }
)
```
@@ -67,25 +86,63 @@ result = nawa.comments.reply(
{
"success": true,
"result": {
- "comment_id": "cmt_abc123",
- "reply_text": "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔",
- "reply_dialect": "gulf",
- "tone": "friendly",
- "original_intent": "question",
- "original_dialect": "gulf"
+ "id": "rpl_nw_a1b2c3d4e5f67890",
+ "object": "comment_reply",
+ "classification": {
+ "intent": "question",
+ "sentiment": "neutral",
+ "priority": "high",
+ "requires_response": true
+ },
+ "reply": {
+ "text": "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔",
+ "direction": "rtl",
+ "tone": "friendly"
+ },
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "cost_usd": 0.008,
+ "credits_used": 8
},
"errors": [],
- "request_id": "req_rep789xyz012"
+ "request_id": "req_nw_rpl_a1b2c3d4e5f67890"
}
```
+### Response headers
+
+| Header | Description |
+|--------|-------------|
+| `X-Request-Id` | Unique request identifier |
+| `X-NAWA-Provider` | AI provider used (`claude`, `gemini`, or `allam`) |
+| `X-NAWA-Latency` | Processing time in milliseconds |
+
### Result fields
| Field | Type | Description |
|-------|------|-------------|
-| `comment_id` | string | The comment that was replied to |
-| `reply_text` | string | The generated reply text |
-| `reply_dialect` | string \| null | Dialect used in the reply (matches original). `null` for English replies. |
-| `tone` | string | The tone used for the reply |
-| `original_intent` | string | Detected intent of the original comment |
-| `original_dialect` | string | Detected dialect of the original comment |
+| `id` | string | Reply ID in `rpl_nw_xxx` format |
+| `object` | string | Always `"comment_reply"` |
+| `classification` | object | Classification result with `intent`, `sentiment`, `priority`, and `requires_response` |
+| `classification.intent` | string | Detected intent of the comment |
+| `classification.sentiment` | string | `positive`, `negative`, `neutral`, `mixed` |
+| `classification.priority` | string | `high`, `medium`, `low` |
+| `classification.requires_response` | boolean | Whether the comment needs a reply |
+| `reply` | object | Generated reply with `text`, `direction`, and `tone` |
+| `reply.text` | string | The generated reply text |
+| `reply.direction` | string | Text direction: `rtl` for Arabic, `ltr` for English |
+| `reply.tone` | string | The tone used: `friendly`, `professional`, `casual`, `formal` |
+| `provider` | string | AI provider used |
+| `model` | string | Model version used |
+| `cost_usd` | number | Cost in USD |
+| `credits_used` | integer | Credits deducted |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `text`, invalid `tone`, text empty |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 402 | `insufficient_credits` | No credits remaining |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal or provider error |
diff --git a/api-reference/feedback.mdx b/api-reference/feedback.mdx
index 4cddf8b..e963f47 100644
--- a/api-reference/feedback.mdx
+++ b/api-reference/feedback.mdx
@@ -1,7 +1,7 @@
---
-title: "Submit Feedback"
+title: "Submit feedback"
sidebarTitle: "POST /v1/feedback"
-description: "Submit RLHF feedback to improve classification accuracy over time."
+description: "Submit RLHF feedback on a classification to improve accuracy over time."
api: "POST https://api.trynawa.com/v1/feedback"
---
@@ -21,10 +21,11 @@ Submit reinforcement learning from human feedback (RLHF) to continuously improve
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `request_id` | string | Yes | The `request_id` from the original classification response. |
-| `field` | string | Yes | The field to correct: `intent`, `sentiment`, `dialect`, `toxicity`, `category`. |
-| `expected_value` | string | Yes | The correct value the model should have returned. |
-| `comment` | string | No | Optional free-text explanation of why this correction is needed. |
+| `classification_id` | string | Yes | The `id` (e.g. `cls_nw_xxx`) from the original classification response. |
+| `rating` | string | Yes | Your assessment: `correct`, `incorrect`, or `partial`. |
+| `corrected_intent` | string[] | No | If the intent was wrong, provide the correct intent(s) as an array. |
+| `corrected_sentiment` | string | No | If the sentiment was wrong, provide the correct value: `positive`, `negative`, `neutral`, `mixed`. |
+| `comment` | string | No | Free-text explanation of why this correction is needed. |
### Example request
@@ -35,28 +36,39 @@ curl -X POST https://api.trynawa.com/v1/feedback \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
- "request_id": "req_abc123def456",
- "field": "dialect",
- "expected_value": "levantine",
- "comment": "This is Lebanese Arabic, not Gulf"
+ "classification_id": "cls_nw_a1b2c3d4e5f67890",
+ "rating": "incorrect",
+ "corrected_intent": ["question"],
+ "corrected_sentiment": "neutral",
+ "comment": "This is a question, not a complaint"
}'
```
```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
const { data, error } = await nawa.feedback.submit({
- requestId: 'req_abc123def456',
- field: 'dialect',
- expectedValue: 'levantine',
- comment: 'This is Lebanese Arabic, not Gulf'
+ classificationId: 'cls_nw_a1b2c3d4e5f67890',
+ rating: 'incorrect',
+ correctedIntent: ['question'],
+ correctedSentiment: 'neutral',
+ comment: 'This is a question, not a complaint'
})
```
```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
result = nawa.feedback.submit(
- request_id="req_abc123def456",
- field="dialect",
- expected_value="levantine",
- comment="This is Lebanese Arabic, not Gulf"
+ classification_id="cls_nw_a1b2c3d4e5f67890",
+ rating="incorrect",
+ corrected_intent=["question"],
+ corrected_sentiment="neutral",
+ comment="This is a question, not a complaint"
)
```
@@ -70,11 +82,23 @@ result = nawa.feedback.submit(
{
"success": true,
"result": {
- "feedback_id": "fb_xyz789",
- "status": "accepted",
- "message": "Thank you! Your feedback helps improve NAWA's accuracy."
+ "id": "fb_nw_x9y8z7w6v5u43210",
+ "object": "feedback",
+ "classification_id": "cls_nw_a1b2c3d4e5f67890",
+ "rating": "incorrect",
+ "acknowledged": true
},
"errors": [],
- "request_id": "req_fb_abc123"
+ "request_id": "req_nw_fb_a1b2c3d4e5f67890"
}
```
+
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Feedback ID in `fb_nw_xxx` format |
+| `object` | string | Always `"feedback"` |
+| `classification_id` | string | The classification this feedback references |
+| `rating` | string | The rating you submitted: `correct`, `incorrect`, or `partial` |
+| `acknowledged` | boolean | Always `true` on success |
diff --git a/api-reference/health.mdx b/api-reference/health.mdx
index ca110e8..66fae8e 100644
--- a/api-reference/health.mdx
+++ b/api-reference/health.mdx
@@ -1,5 +1,5 @@
---
-title: "Health Check"
+title: "Health check"
sidebarTitle: "GET /v1/health"
description: "Check the operational status of the NAWA API and its dependencies."
api: "GET https://api.trynawa.com/v1/health"
@@ -9,10 +9,26 @@ Check the operational status of the NAWA API. This endpoint is **free** and does
## Request
-```bash
+
+
+```bash cURL
curl https://api.trynawa.com/v1/health
```
+```typescript TypeScript
+const response = await fetch('https://api.trynawa.com/v1/health')
+const data = await response.json()
+```
+
+```python Python
+import requests
+
+response = requests.get("https://api.trynawa.com/v1/health")
+data = response.json()
+```
+
+
+
## Response
### Healthy response (200)
@@ -22,41 +38,56 @@ curl https://api.trynawa.com/v1/health
"success": true,
"result": {
"status": "healthy",
- "version": "1.0.0",
+ "version": "v1",
"services": {
- "api": "operational",
- "classification": "operational",
- "database": "operational",
- "cache": "operational"
+ "database": { "status": "healthy", "latency_ms": 12 },
+ "redis": { "status": "healthy", "latency_ms": 3 },
+ "nagl": { "status": "healthy", "latency_ms": 45 }
},
- "timestamp": "2025-01-15T12:00:00Z"
+ "timestamp": "2025-01-15T12:00:00Z",
+ "latency_ms": 48
},
"errors": [],
- "request_id": "req_hlt_abc123"
+ "request_id": "req_nw_hlt_a1b2c3d4e5f67890"
}
```
-### Degraded response (200)
+### Degraded response (503)
```json
{
"success": true,
"result": {
"status": "degraded",
- "version": "1.0.0",
+ "version": "v1",
"services": {
- "api": "operational",
- "classification": "degraded",
- "database": "operational",
- "cache": "operational"
+ "database": { "status": "healthy", "latency_ms": 15 },
+ "redis": { "status": "degraded", "latency_ms": 250 },
+ "nagl": { "status": "healthy", "latency_ms": 50 }
},
- "timestamp": "2025-01-15T12:00:00Z"
+ "timestamp": "2025-01-15T12:00:00Z",
+ "latency_ms": 255
},
"errors": [],
- "request_id": "req_hlt_def456"
+ "request_id": "req_nw_hlt_d4e5f67890a1b2c3"
}
```
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `status` | string | Overall health: `healthy` or `degraded` |
+| `version` | string | API version (always `v1`) |
+| `services` | object | Health status of each dependency |
+| `services.database` | object | Database status with `status` and `latency_ms` |
+| `services.redis` | object | Redis cache status with `status` and `latency_ms` |
+| `services.nagl` | object | NAGL classification engine status with `status` and `latency_ms` |
+| `timestamp` | string | ISO 8601 timestamp of the check |
+| `latency_ms` | integer | Total health check latency in milliseconds |
+
+Each service reports one of: `healthy`, `degraded`, `down`.
+
For real-time status monitoring, visit [status.trynawa.com](https://status.trynawa.com).
diff --git a/api-reference/rubric-classify.mdx b/api-reference/rubric-classify.mdx
index 4489175..3324774 100644
--- a/api-reference/rubric-classify.mdx
+++ b/api-reference/rubric-classify.mdx
@@ -1,80 +1,94 @@
---
-title: "Rubric Classify"
-sidebarTitle: "POST /v1/rubric/classify"
-description: "Classify a comment against a custom rubric with predefined scoring criteria."
-api: "POST https://api.trynawa.com/v1/rubric/classify"
+title: "Rubric classify"
+sidebarTitle: "POST /v1/rubric-classify"
+description: "Classify a comment against a custom rubric with user-defined categories and scoring criteria."
+api: "POST https://api.trynawa.com/v1/rubric-classify"
---
-Classify a comment against a custom rubric. Define your own categories and scoring criteria for domain-specific classification.
+Classify a comment against a custom rubric. Define your own categories and scoring criteria for domain-specific classification. This endpoint is **free** for all tiers.
- Cost: **$0.003** per request (3 credits). **500 free requests/month** on this endpoint -- no credit card required. Semantic cache hits are free (`X-NAWA-Cache: HIT`).
+ Cost: **$0.003** per request (3 credits). This endpoint is free for all API key tiers.
## Request
+### Query parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `provider` | string | No | Force a specific AI provider: `claude`, `gemini`, `allam`. |
+
### Body parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `text` | string | Yes | The comment text to classify. Max 5,000 characters. |
-| `rubric` | object | Yes | The rubric definition with categories and criteria. |
-| `rubric.categories` | string[] | Yes | List of category names to classify against. |
-| `rubric.descriptions` | object | No | Category descriptions to guide the model. Keys are category names, values are description strings. |
-| `platform` | string | No | Source platform for context. |
+| `text` | string | Yes | The comment text to classify. Must be non-empty. |
+| `rubric` | object | Yes | The rubric definition. |
+| `rubric.categories` | object[] | Yes | Array of category objects (1-20 items). Each object has a required `name` (string) and optional `description` (string). |
+| `rubric.multi_label` | boolean | No | Allow multiple categories to match. Default: `false`. |
+| `rubric.confidence_threshold` | number | No | Minimum confidence score (0-1) for a category match. Default: `0.5`. |
### Example request
```bash cURL
-curl -X POST https://api.trynawa.com/v1/rubric/classify \
+curl -X POST https://api.trynawa.com/v1/rubric-classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
"text": "وين الترجمة العربية؟ مافهمت شي",
"rubric": {
- "categories": ["translation_request", "technical_issue", "content_feedback", "off_topic"],
- "descriptions": {
- "translation_request": "User is asking for subtitles or translation",
- "technical_issue": "Audio, video, or playback problems",
- "content_feedback": "Comments about the content quality",
- "off_topic": "Not related to the video"
- }
- },
- "platform": "youtube"
+ "categories": [
+ { "name": "translation_request", "description": "User is asking for subtitles or translation" },
+ { "name": "technical_issue", "description": "Audio, video, or playback problems" },
+ { "name": "content_feedback", "description": "Comments about the content quality" },
+ { "name": "off_topic", "description": "Not related to the video" }
+ ],
+ "multi_label": false,
+ "confidence_threshold": 0.5
+ }
}'
```
```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
const { data, error } = await nawa.rubric.classify({
text: 'وين الترجمة العربية؟ مافهمت شي',
rubric: {
- categories: ['translation_request', 'technical_issue', 'content_feedback', 'off_topic'],
- descriptions: {
- translation_request: 'User is asking for subtitles or translation',
- technical_issue: 'Audio, video, or playback problems',
- content_feedback: 'Comments about the content quality',
- off_topic: 'Not related to the video'
- }
- },
- platform: 'youtube'
+ categories: [
+ { name: 'translation_request', description: 'User is asking for subtitles or translation' },
+ { name: 'technical_issue', description: 'Audio, video, or playback problems' },
+ { name: 'content_feedback', description: 'Comments about the content quality' },
+ { name: 'off_topic', description: 'Not related to the video' }
+ ],
+ multiLabel: false,
+ confidenceThreshold: 0.5
+ }
})
```
```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
result = nawa.rubric.classify(
text="وين الترجمة العربية؟ مافهمت شي",
rubric={
- "categories": ["translation_request", "technical_issue", "content_feedback", "off_topic"],
- "descriptions": {
- "translation_request": "User is asking for subtitles or translation",
- "technical_issue": "Audio, video, or playback problems",
- "content_feedback": "Comments about the content quality",
- "off_topic": "Not related to the video",
- },
+ "categories": [
+ {"name": "translation_request", "description": "User is asking for subtitles or translation"},
+ {"name": "technical_issue", "description": "Audio, video, or playback problems"},
+ {"name": "content_feedback", "description": "Comments about the content quality"},
+ {"name": "off_topic", "description": "Not related to the video"},
+ ],
+ "multi_label": False,
+ "confidence_threshold": 0.5,
},
- platform="youtube",
)
```
@@ -88,34 +102,56 @@ result = nawa.rubric.classify(
{
"success": true,
"result": {
- "text": "وين الترجمة العربية؟ مافهمت شي",
- "category": "translation_request",
- "category_confidence": 0.94,
- "scores": {
- "translation_request": 0.94,
- "technical_issue": 0.03,
- "content_feedback": 0.02,
- "off_topic": 0.01
- },
- "dialect": "gulf",
- "dialect_confidence": 0.91,
+ "id": "rcl_nw_a1b2c3d4e5f67890",
+ "object": "rubric_classification",
+ "categories": [
+ { "name": "translation_request", "confidence": 0.94 },
+ { "name": "technical_issue", "confidence": 0.03 },
+ { "name": "content_feedback", "confidence": 0.02 },
+ { "name": "off_topic", "confidence": 0.01 }
+ ],
+ "multi_label": false,
"language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "cost": 0.003
},
"errors": [],
- "request_id": "req_rub123abc456"
+ "request_id": "req_nw_rcl_a1b2c3d4e5f67890"
}
```
+### Response headers
+
+| Header | Description |
+|--------|-------------|
+| `X-Request-Id` | Unique request identifier |
+| `X-NAWA-Provider` | AI provider used (`claude`, `gemini`, or `allam`) |
+| `X-RateLimit-Limit` | Rate limit ceiling for current window |
+| `X-RateLimit-Remaining` | Requests remaining in current window |
+| `X-RateLimit-Reset` | Window reset time (RFC 3339) |
+
### Result fields
| Field | Type | Description |
|-------|------|-------------|
-| `category` | string | The top matching category from your rubric |
-| `category_confidence` | number | Confidence score (0–1) for the top category |
-| `scores` | object | Confidence scores for all rubric categories |
-| `dialect` | string | Detected Arabic dialect |
-| `dialect_confidence` | number | Dialect confidence score (0–1) |
-| `language` | string | Detected language code |
-| `cached` | boolean | Whether served from semantic cache |
+| `id` | string | Classification ID in `rcl_nw_xxx` format |
+| `object` | string | Always `"rubric_classification"` |
+| `categories` | object[] | Array of `{ name, confidence }` objects, sorted by confidence descending |
+| `multi_label` | boolean | Whether multi-label classification was used |
+| `language` | string | Detected language: `ar`, `en`, `mixed` |
+| `provider` | string | AI provider used |
+| `model` | string | Model version used |
+| `fallback_used` | boolean | Whether a fallback provider was used |
+| `cost` | number | Cost in USD |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `text` or `rubric`, invalid categories |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 402 | `insufficient_credits` | No credits remaining |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal or provider error |
diff --git a/billing.mdx b/billing.mdx
index e28bb54..d65ee2a 100644
--- a/billing.mdx
+++ b/billing.mdx
@@ -47,8 +47,8 @@ NAWA uses a prepaid credit system.
| `POST /v1/translate` | $0.005 | 5 credits |
| `POST /v1/detect` | $0.002 | 2 credits |
| `POST /v1/moderate` | $0.004 | 4 credits |
-| `POST /v1/rubric/classify` | $0.003 | 3 credits |
-| `POST /v1/comments/:id/reply` | $0.008 | 8 credits |
+| `POST /v1/rubric-classify` | $0.003 | 3 credits |
+| `POST /v1/comments/reply` | $0.008 | 8 credits |
| `POST /v1/report` (Basic) | $0.50 | 500 credits |
| `POST /v1/report` (Pro) | $1.50 | 1,500 credits |
| `POST /v1/feedback` | **Free** | 0 credits |
@@ -97,8 +97,8 @@ When your credit balance reaches 0:
| `/v1/translate` | $5 | $50 | $500 |
| `/v1/detect` | $2 | $20 | $200 |
| `/v1/moderate` | $4 | $40 | $400 |
-| `/v1/rubric/classify` | $3 | $30 | $300 |
-| `/v1/comments/:id/reply` | $8 | $80 | $800 |
+| `/v1/rubric-classify` | $3 | $30 | $300 |
+| `/v1/comments/reply` | $8 | $80 | $800 |
With typical semantic cache hit rates of 20-30%, actual costs are 20-30% lower than these estimates.
diff --git a/errors.mdx b/errors.mdx
index fdcf365..531d14b 100644
--- a/errors.mdx
+++ b/errors.mdx
@@ -10,20 +10,17 @@ Every NAWA API error returns a consistent JSON envelope with machine-readable co
```json
{
- "success": false,
- "result": null,
- "errors": [
- {
- "type": "invalid_request_error",
- "code": "missing_required_param",
- "message": "The 'text' parameter is required.",
- "display_message": "Please provide the comment text to classify.",
- "param": "text",
- "doc_url": "https://developers.trynawa.com/errors#missing_required_param",
- "suggested_action": "Include the 'text' field in your request body."
- }
- ],
- "request_id": "req_abc123def456"
+ "type": "error",
+ "error": {
+ "type": "invalid_request_error",
+ "code": "missing_field",
+ "message": "`text` is required",
+ "display_message": "The required field `text` is missing.",
+ "param": "text",
+ "doc_url": "https://developers.trynawa.com/errors#missing-field",
+ "suggested_action": "Include `text` in your request body."
+ },
+ "request_id": "req_nw_a1b2c3d4e5f67890abcd"
}
```
diff --git a/introduction.mdx b/introduction.mdx
index 5618464..00b854f 100644
--- a/introduction.mdx
+++ b/introduction.mdx
@@ -49,8 +49,8 @@ NAWA is the language intelligence platform for Arabic and English. Classify comm
| Metric | Value |
|--------|-------|
-| Languages | Arabic (4 dialects) + English |
-| Arabic dialect accuracy | 100% (Gulf, Egyptian, Levantine, MSA) |
+| Languages | Arabic (5 dialects) + English |
+| Arabic dialect accuracy | 100% (Gulf, Egyptian, Levantine, Maghrebi, MSA) |
| Live endpoints | 12 (classify, translate, detect, moderate, rubric, reply, report, feedback, comments, analytics, health, usage) |
| p95 latency | <2 seconds |
| Cost per classify | $0.006 |
@@ -72,7 +72,7 @@ Make your first classification in seconds:
curl -X POST https://api.trynawa.com/v1/classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
- -d '{"text": "متى الجزء الثاني؟", "platform": "youtube"}'
+ -d '{"text": "متى الجزء الثاني؟"}'
```
```typescript TypeScript
@@ -80,10 +80,9 @@ import { Nawa } from '@nawalabs/sdk'
const nawa = new Nawa({ apiKey: 'nawa_test_sk_xxx' })
const { data } = await nawa.classify({
- text: 'متى الجزء الثاني؟',
- platform: 'youtube'
+ text: 'متى الجزء الثاني؟'
})
-console.log(data.intent) // "question"
+console.log(data.intent) // ["question"]
```
```python Python
@@ -91,10 +90,9 @@ from nawa import Nawa
nawa = Nawa(api_key="nawa_test_sk_xxx")
result = nawa.classify(
- text="متى الجزء الثاني؟",
- platform="youtube"
+ text="متى الجزء الثاني؟"
)
-print(result.data.intent) # "question"
+print(result.data.intent) # ["question"]
```
diff --git a/openapi.yaml b/openapi.yaml
index 17706f2..13310da 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -53,12 +53,20 @@ paths:
operationId: classify
summary: Classify text
description: |
- Classify Arabic or English text. Returns intent, sentiment, dialect, toxicity,
- and categories in a single API call. Supports semantic caching.
+ Classify Arabic or English text. Returns intent, sentiment, dialect, priority,
+ and a suggested reply in a single API call. Supports semantic caching.
tags: [Classification]
x-nawa-cost: "$0.006"
x-nawa-cache: true
x-nawa-free: false
+ parameters:
+ - name: provider
+ in: query
+ required: false
+ schema:
+ type: string
+ enum: [claude, gemini, allam]
+ description: Force a specific AI provider for A/B testing
requestBody:
required: true
content:
@@ -70,25 +78,15 @@ paths:
text:
type: string
minLength: 1
- maxLength: 5000
description: The comment text to classify
examples:
- "متى الجزء الثاني؟"
- "ما شاء الله عليك، محتوى رهيب!"
- platform:
- type: string
- enum: [youtube, instagram, twitter, facebook]
- description: Source platform for context-aware classification
- channel_id:
- type: string
- description: Your channel or account identifier for analytics grouping
- metadata:
+ context:
type: object
- description: Arbitrary key-value metadata to attach to the classification
+ description: Arbitrary context object to pass alongside classification
example:
text: "متى الجزء الثاني؟"
- platform: "youtube"
- channel_id: "ch_123"
responses:
"200":
description: Classification result
@@ -99,8 +97,6 @@ paths:
$ref: "#/components/headers/X-NAWA-Provider"
X-NAWA-Cache:
$ref: "#/components/headers/X-NAWA-Cache"
- X-NAWA-Balance:
- $ref: "#/components/headers/X-NAWA-Balance"
X-RateLimit-Limit:
$ref: "#/components/headers/X-RateLimit-Limit"
X-RateLimit-Remaining:
@@ -117,21 +113,26 @@ paths:
value:
success: true
result:
- text: "متى الجزء الثاني؟"
- intent: question
- intent_confidence: 0.97
+ id: cls_nw_a1b2c3d4e5f67890abcd
+ object: classification
+ intent: ["question"]
sentiment: neutral
- sentiment_confidence: 0.91
+ language: ar
dialect: gulf
dialect_confidence: 0.95
- toxicity: none
- toxicity_confidence: 0.99
- categories: ["engagement"]
- language: ar
- model: nagl-v1
- cached: false
+ requires_response: true
+ priority: high
+ suggested_reply:
+ text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك"
+ direction: rtl
+ provider: allam
+ model: sdaia/allam-1-13b-instruct
+ fallback_used: false
+ tokens_used: null
+ cost_usd: 0.006
+ credits_used: 6
errors: []
- request_id: req_nw_a1b2c3d4e5f67890
+ request_id: req_nw_a1b2c3d4e5f67890abcd
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -182,7 +183,7 @@ paths:
description: Target language
dialect:
type: string
- enum: [gulf, egyptian, levantine, msa]
+ enum: [gulf, egyptian, levantine, maghrebi, msa]
default: msa
description: "Target dialect for Arabic output"
tone:
@@ -457,17 +458,25 @@ paths:
"500":
$ref: "#/components/responses/InternalError"
- /rubric/classify:
+ /rubric-classify:
post:
operationId: rubricClassify
summary: Classify with custom rubric
description: |
Classify text against a user-defined rubric. Define your own categories and
- scoring criteria for domain-specific classification.
+ scoring criteria for domain-specific classification. Free for all API key tiers.
tags: [Classification]
x-nawa-cost: "$0.003"
- x-nawa-cache: true
- x-nawa-free: false
+ x-nawa-cache: false
+ x-nawa-free: true
+ parameters:
+ - name: provider
+ in: query
+ required: false
+ schema:
+ type: string
+ enum: [claude, gemini, allam]
+ description: Force a specific AI provider
requestBody:
required: true
content:
@@ -479,40 +488,25 @@ paths:
text:
type: string
minLength: 1
- maxLength: 5000
description: The comment text to classify
examples:
- "وين الترجمة العربية؟ مافهمت شي"
rubric:
- type: object
- required: [categories]
- properties:
- categories:
- type: array
- minItems: 1
- maxItems: 20
- items:
- type: string
- description: List of category names to classify against
- descriptions:
- type: object
- additionalProperties:
- type: string
- description: Category descriptions to guide the model
- platform:
- type: string
- enum: [youtube, instagram, twitter, facebook]
- description: Source platform for context
+ $ref: "#/components/schemas/Rubric"
example:
text: "وين الترجمة العربية؟ مافهمت شي"
rubric:
- categories: ["translation_request", "technical_issue", "content_feedback", "off_topic"]
- descriptions:
- translation_request: "User is asking for subtitles or translation"
- technical_issue: "Audio, video, or playback problems"
- content_feedback: "Comments about the content quality"
- off_topic: "Not related to the video"
- platform: "youtube"
+ categories:
+ - name: "translation_request"
+ description: "User is asking for subtitles or translation"
+ - name: "technical_issue"
+ description: "Audio, video, or playback problems"
+ - name: "content_feedback"
+ description: "Comments about the content quality"
+ - name: "off_topic"
+ description: "Not related to the video"
+ multi_label: false
+ confidence_threshold: 0.5
responses:
"200":
description: Rubric classification result
@@ -521,10 +515,6 @@ paths:
$ref: "#/components/headers/X-Request-Id"
X-NAWA-Provider:
$ref: "#/components/headers/X-NAWA-Provider"
- X-NAWA-Cache:
- $ref: "#/components/headers/X-NAWA-Cache"
- X-NAWA-Balance:
- $ref: "#/components/headers/X-NAWA-Balance"
X-RateLimit-Limit:
$ref: "#/components/headers/X-RateLimit-Limit"
X-RateLimit-Remaining:
@@ -541,21 +531,25 @@ paths:
value:
success: true
result:
- text: "وين الترجمة العربية؟ مافهمت شي"
- category: translation_request
- category_confidence: 0.94
- scores:
- translation_request: 0.94
- technical_issue: 0.03
- content_feedback: 0.02
- off_topic: 0.01
- dialect: gulf
- dialect_confidence: 0.91
+ id: rcl_nw_a1b2c3d4e5f67890
+ object: rubric_classification
+ categories:
+ - name: translation_request
+ confidence: 0.94
+ - name: technical_issue
+ confidence: 0.03
+ - name: content_feedback
+ confidence: 0.02
+ - name: off_topic
+ confidence: 0.01
+ multi_label: false
language: ar
- model: nagl-v1
- cached: false
+ provider: allam
+ model: sdaia/allam-1-13b-instruct
+ fallback_used: false
+ cost: 0.003
errors: []
- request_id: req_rub123abc456
+ request_id: req_nw_rcl_a1b2c3d4e5f67890
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -567,37 +561,41 @@ paths:
"500":
$ref: "#/components/responses/InternalError"
- /comments/{id}/reply:
+ /comments/reply:
post:
operationId: generateReply
- summary: Generate reply to comment
+ summary: Classify and generate reply
description: |
- Generate a culturally-aware, dialect-matched reply to an Arabic comment.
- Replies match the commenter's dialect and are contextually appropriate.
+ Classify a comment and generate a culturally-aware, dialect-matched reply in a single call.
+ Replies match the detected dialect and are contextually appropriate.
tags: [Reply]
x-nawa-cost: "$0.008"
- x-nawa-cache: true
+ x-nawa-cache: false
x-nawa-free: false
parameters:
- - name: id
- in: path
- required: true
- description: The comment ID to reply to
+ - name: provider
+ in: query
+ required: false
schema:
type: string
- examples:
- - "cmt_abc123"
+ enum: [claude, gemini, allam]
+ description: Force a specific AI provider
requestBody:
- required: false
+ required: true
content:
application/json:
schema:
type: object
+ required: [text]
properties:
+ text:
+ type: string
+ minLength: 1
+ description: The comment text to classify and reply to
tone:
type: string
enum: [friendly, professional, casual, formal]
- default: friendly
+ default: professional
description: Reply tone
max_length:
type: integer
@@ -606,34 +604,27 @@ paths:
default: 500
description: Maximum reply length in characters
context:
- type: string
- description: Additional context about the channel or video
- language:
- type: string
- enum: [ar, en, auto]
- default: auto
- description: "Force reply language. Default: auto (matches commenter's language)."
+ type: object
+ properties:
+ platform:
+ type: string
+ brand_voice:
+ type: string
+ description: Additional context with platform and brand voice
example:
+ text: "متى الجزء الثاني؟"
tone: "friendly"
- context: "Tech review channel focused on smartphones"
+ context:
+ platform: "youtube"
+ brand_voice: "Tech review channel focused on smartphones"
responses:
"200":
- description: Generated reply
+ description: Classification and generated reply
headers:
X-Request-Id:
$ref: "#/components/headers/X-Request-Id"
X-NAWA-Provider:
$ref: "#/components/headers/X-NAWA-Provider"
- X-NAWA-Cache:
- $ref: "#/components/headers/X-NAWA-Cache"
- X-NAWA-Balance:
- $ref: "#/components/headers/X-NAWA-Balance"
- X-RateLimit-Limit:
- $ref: "#/components/headers/X-RateLimit-Limit"
- X-RateLimit-Remaining:
- $ref: "#/components/headers/X-RateLimit-Remaining"
- X-RateLimit-Reset:
- $ref: "#/components/headers/X-RateLimit-Reset"
content:
application/json:
schema:
@@ -644,14 +635,23 @@ paths:
value:
success: true
result:
- comment_id: cmt_abc123
- reply_text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔"
- reply_dialect: gulf
- tone: friendly
- original_intent: question
- original_dialect: gulf
+ id: rpl_nw_a1b2c3d4e5f67890
+ object: comment_reply
+ classification:
+ intent: question
+ sentiment: neutral
+ priority: high
+ requires_response: true
+ reply:
+ text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔"
+ direction: rtl
+ tone: friendly
+ provider: allam
+ model: sdaia/allam-1-13b-instruct
+ cost_usd: 0.008
+ credits_used: 8
errors: []
- request_id: req_rep789xyz012
+ request_id: req_nw_rpl_a1b2c3d4e5f67890
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -681,10 +681,11 @@ paths:
schema:
$ref: "#/components/schemas/FeedbackInput"
example:
- request_id: "req_abc123def456"
- field: "dialect"
- expected_value: "levantine"
- comment: "This is Lebanese Arabic, not Gulf"
+ classification_id: "cls_nw_a1b2c3d4e5f67890"
+ rating: "incorrect"
+ corrected_intent: ["question"]
+ corrected_sentiment: "neutral"
+ comment: "This is a question, not a complaint"
responses:
"200":
description: Feedback accepted
@@ -701,11 +702,13 @@ paths:
value:
success: true
result:
- feedback_id: fb_xyz789
- status: accepted
- message: "Thank you! Your feedback helps improve NAWA's accuracy."
+ id: fb_nw_x9y8z7w6v5u43210
+ object: feedback
+ classification_id: cls_nw_a1b2c3d4e5f67890
+ rating: incorrect
+ acknowledged: true
errors: []
- request_id: req_fb_abc123
+ request_id: req_nw_fb_a1b2c3d4e5f67890
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -758,7 +761,7 @@ paths:
required: false
schema:
type: string
- enum: [gulf, egyptian, levantine, msa]
+ enum: [gulf, egyptian, levantine, maghrebi, msa]
description: Filter by dialect
- name: toxicity
in: query
@@ -965,15 +968,21 @@ paths:
success: true
result:
status: healthy
- version: "1.0.0"
+ version: v1
services:
- api: operational
- classification: operational
- database: operational
- cache: operational
+ database:
+ status: healthy
+ latency_ms: 12
+ redis:
+ status: healthy
+ latency_ms: 3
+ nagl:
+ status: healthy
+ latency_ms: 45
timestamp: "2025-01-15T12:00:00Z"
+ latency_ms: 48
errors: []
- request_id: req_hlt_abc123
+ request_id: req_nw_hlt_a1b2c3d4e5f67890
"503":
description: One or more services degraded
content:
@@ -999,14 +1008,14 @@ paths:
schema:
type: string
format: date-time
- description: Start date (ISO 8601). Default: start of current month.
+ description: "Start date (ISO 8601). Default: start of current month."
- name: to
in: query
required: false
schema:
type: string
format: date-time
- description: End date (ISO 8601). Default: now.
+ description: "End date (ISO 8601). Default: now."
- name: group_by
in: query
required: false
@@ -1170,7 +1179,7 @@ components:
description: AI provider used for this request
schema:
type: string
- enum: [allam, claude]
+ enum: [claude, gemini, allam]
X-NAWA-Cache:
description: Whether the response was served from semantic cache
schema:
@@ -1424,51 +1433,67 @@ components:
ClassificationResult:
type: object
+ required: [id, object, intent, sentiment, language, requires_response, priority, provider, model, cost_usd, credits_used]
properties:
- text:
+ id:
type: string
- description: The original input text
- intent:
+ description: Classification ID in cls_nw_xxx format
+ object:
type: string
- enum: [question, complaint, praise, suggestion, spam, other]
- description: Detected intent
- intent_confidence:
- type: number
- minimum: 0
- maximum: 1
+ const: classification
+ intent:
+ type: array
+ items:
+ type: string
+ description: Detected intents, e.g. ["question"] or ["question", "complaint"]
sentiment:
type: string
enum: [positive, negative, neutral, mixed]
- sentiment_confidence:
- type: number
- minimum: 0
- maximum: 1
+ language:
+ type: string
+ enum: [ar, en, mixed]
dialect:
type: string
- enum: [gulf, egyptian, levantine, msa]
+ nullable: true
+ enum: [gulf, egyptian, levantine, maghrebi, msa]
+ description: Detected dialect. Null for English text.
dialect_confidence:
type: number
+ nullable: true
minimum: 0
maximum: 1
- toxicity:
+ description: Confidence score (0-1). Null for English text.
+ requires_response:
+ type: boolean
+ description: Whether the comment needs a reply
+ priority:
type: string
- enum: [none, mild, moderate, severe]
- toxicity_confidence:
- type: number
- minimum: 0
- maximum: 1
- categories:
- type: array
- items:
- type: string
- description: "Content categories: engagement, support, feedback, spam"
- language:
+ enum: [high, medium, low]
+ suggested_reply:
+ type: object
+ nullable: true
+ properties:
+ text:
+ type: string
+ direction:
+ type: string
+ enum: [ltr, rtl]
+ description: Auto-generated reply suggestion. May be null.
+ provider:
type: string
- enum: [ar, en, mixed]
+ enum: [claude, gemini, allam]
model:
type: string
- cached:
+ fallback_used:
type: boolean
+ tokens_used:
+ type: integer
+ nullable: true
+ cost_usd:
+ type: number
+ description: Cost in USD
+ credits_used:
+ type: integer
TranslateResult:
type: object
@@ -1495,12 +1520,12 @@ components:
source_dialect:
type: string
nullable: true
- enum: [gulf, egyptian, levantine, msa]
+ enum: [gulf, egyptian, levantine, maghrebi, msa]
description: Detected dialect if source is Arabic
target_dialect:
type: string
nullable: true
- enum: [gulf, egyptian, levantine, msa]
+ enum: [gulf, egyptian, levantine, maghrebi, msa]
description: Target dialect if target is Arabic
tone:
type: string
@@ -1540,7 +1565,7 @@ components:
dialect:
type: string
nullable: true
- enum: [gulf, egyptian, levantine, msa]
+ enum: [gulf, egyptian, levantine, maghrebi, msa]
description: Detected Arabic dialect. Null if not Arabic.
dialect_confidence:
type: number
@@ -1606,7 +1631,7 @@ components:
dialect:
type: string
nullable: true
- enum: [gulf, egyptian, levantine, msa]
+ enum: [gulf, egyptian, levantine, maghrebi, msa]
categories:
$ref: "#/components/schemas/ModerationCategories"
category_scores:
@@ -1687,91 +1712,130 @@ components:
RubricResult:
type: object
+ required: [id, object, categories, multi_label, language, provider, model, cost]
properties:
- text:
- type: string
- description: The original input text
- category:
+ id:
type: string
- description: The top matching category from the rubric
- category_confidence:
- type: number
- minimum: 0
- maximum: 1
- scores:
- type: object
- additionalProperties:
- type: number
- description: Confidence scores for all rubric categories
- dialect:
+ description: Classification ID in rcl_nw_xxx format
+ object:
type: string
- enum: [gulf, egyptian, levantine, msa]
- dialect_confidence:
- type: number
- minimum: 0
- maximum: 1
+ const: rubric_classification
+ categories:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ confidence:
+ type: number
+ minimum: 0
+ maximum: 1
+ description: Category matches sorted by confidence descending
+ multi_label:
+ type: boolean
language:
type: string
enum: [ar, en, mixed]
+ provider:
+ type: string
+ enum: [claude, gemini, allam]
model:
type: string
- cached:
+ fallback_used:
type: boolean
+ cost:
+ type: number
+ description: Cost in USD
ReplyResult:
type: object
+ required: [id, object, classification, reply, provider, model, cost_usd, credits_used]
properties:
- comment_id:
- type: string
- description: The comment that was replied to
- reply_text:
- type: string
- description: The generated reply text
- reply_dialect:
+ id:
type: string
- enum: [gulf, egyptian, levantine, msa]
- description: Dialect used in the reply
- tone:
+ description: Reply ID in rpl_nw_xxx format
+ object:
type: string
- enum: [friendly, professional, casual, formal]
- description: The tone used for the reply
- original_intent:
+ const: comment_reply
+ classification:
+ type: object
+ properties:
+ intent:
+ type: string
+ sentiment:
+ type: string
+ enum: [positive, negative, neutral, mixed]
+ priority:
+ type: string
+ enum: [high, medium, low]
+ requires_response:
+ type: boolean
+ reply:
+ type: object
+ properties:
+ text:
+ type: string
+ description: Generated reply text
+ direction:
+ type: string
+ enum: [ltr, rtl]
+ description: Text direction (rtl for Arabic, ltr for English)
+ tone:
+ type: string
+ enum: [friendly, professional, casual, formal]
+ provider:
type: string
- enum: [question, complaint, praise, suggestion, spam, other]
- description: Detected intent of the original comment
- original_dialect:
+ enum: [claude, gemini, allam]
+ model:
type: string
- enum: [gulf, egyptian, levantine, msa]
- description: Detected dialect of the original comment
+ cost_usd:
+ type: number
+ credits_used:
+ type: integer
FeedbackInput:
type: object
- required: [request_id, field, expected_value]
+ required: [classification_id, rating]
properties:
- request_id:
+ classification_id:
type: string
- description: The request_id from the original classification response
- field:
+ description: The classification ID (cls_nw_xxx) from the original response
+ rating:
type: string
- enum: [intent, sentiment, dialect, toxicity, category]
- description: The field to correct
- expected_value:
+ enum: [correct, incorrect, partial]
+ description: Your assessment of the classification
+ corrected_intent:
+ type: array
+ items:
+ type: string
+ description: If the intent was wrong, provide the correct intent(s)
+ corrected_sentiment:
type: string
- description: The correct value the model should have returned
+ enum: [positive, negative, neutral, mixed]
+ description: If the sentiment was wrong, provide the correct value
comment:
type: string
description: Optional explanation of why this correction is needed
FeedbackResult:
type: object
+ required: [id, object, classification_id, rating, acknowledged]
properties:
- feedback_id:
+ id:
type: string
- status:
+ description: Feedback ID in fb_nw_xxx format
+ object:
+ type: string
+ const: feedback
+ classification_id:
type: string
- enum: [accepted]
- message:
+ rating:
type: string
+ enum: [correct, incorrect, partial]
+ acknowledged:
+ type: boolean
+ const: true
CommentItem:
type: object
@@ -1791,7 +1855,7 @@ components:
enum: [positive, negative, neutral, mixed]
dialect:
type: string
- enum: [gulf, egyptian, levantine, msa]
+ enum: [gulf, egyptian, levantine, maghrebi, msa]
toxicity:
type: string
enum: [none, mild, moderate, severe]
@@ -1861,30 +1925,37 @@ components:
HealthResult:
type: object
+ required: [status, version, services, timestamp]
properties:
status:
type: string
enum: [healthy, degraded]
version:
type: string
+ const: v1
services:
type: object
properties:
- api:
- type: string
- enum: [operational, degraded, down]
- classification:
- type: string
- enum: [operational, degraded, down]
database:
- type: string
- enum: [operational, degraded, down]
- cache:
- type: string
- enum: [operational, degraded, down]
+ $ref: "#/components/schemas/ServiceStatus"
+ redis:
+ $ref: "#/components/schemas/ServiceStatus"
+ nagl:
+ $ref: "#/components/schemas/ServiceStatus"
timestamp:
type: string
format: date-time
+ latency_ms:
+ type: integer
+
+ ServiceStatus:
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [healthy, degraded, down]
+ latency_ms:
+ type: integer
UsageResult:
type: object
@@ -1959,6 +2030,31 @@ components:
type: string
format: date-time
+ Rubric:
+ type: object
+ required: [categories]
+ properties:
+ categories:
+ type: array
+ minItems: 1
+ maxItems: 20
+ items:
+ type: object
+ required: [name]
+ properties:
+ name:
+ type: string
+ description:
+ type: string
+ multi_label:
+ type: boolean
+ default: false
+ confidence_threshold:
+ type: number
+ minimum: 0
+ maximum: 1
+ default: 0.5
+
# --- Error Envelope ---
ErrorEnvelope:
diff --git a/quickstart.mdx b/quickstart.mdx
index 8d1384c..7855575 100644
--- a/quickstart.mdx
+++ b/quickstart.mdx
@@ -27,8 +27,7 @@ Get up and running with the NAWA classification API in three steps.
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
- "text": "متى الجزء الثاني؟",
- "platform": "youtube"
+ "text": "متى الجزء الثاني؟"
}'
```
@@ -38,8 +37,7 @@ Get up and running with the NAWA classification API in three steps.
const nawa = new Nawa({ apiKey: 'nawa_test_sk_xxx' })
const { data, error } = await nawa.classify({
- text: 'متى الجزء الثاني؟',
- platform: 'youtube'
+ text: 'متى الجزء الثاني؟'
})
if (error) {
@@ -55,8 +53,7 @@ Get up and running with the NAWA classification API in three steps.
nawa = Nawa(api_key="nawa_test_sk_xxx")
result = nawa.classify(
- text="متى الجزء الثاني؟",
- platform="youtube"
+ text="متى الجزء الثاني؟"
)
if result.error:
@@ -75,22 +72,28 @@ Get up and running with the NAWA classification API in three steps.
{
"success": true,
"result": {
- "text": "متى الجزء الثاني؟",
- "intent": "question",
- "intent_confidence": 0.97,
+ "id": "cls_nw_a1b2c3d4e5f67890abcd",
+ "object": "classification",
+ "intent": ["question"],
"sentiment": "neutral",
- "sentiment_confidence": 0.91,
+ "language": "ar",
"dialect": "gulf",
"dialect_confidence": 0.95,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "requires_response": true,
+ "priority": "high",
+ "suggested_reply": {
+ "text": "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك",
+ "direction": "rtl"
+ },
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "tokens_used": null,
+ "cost_usd": 0.006,
+ "credits_used": 6
},
"errors": [],
- "request_id": "req_abc123def456"
+ "request_id": "req_nw_a1b2c3d4e5f67890abcd"
}
```
@@ -98,14 +101,16 @@ Get up and running with the NAWA classification API in three steps.
| Field | Type | Description |
|-------|------|-------------|
- | `intent` | string | One of: `question`, `complaint`, `praise`, `suggestion`, `spam`, `other` |
- | `intent_confidence` | number | Confidence score between 0 and 1 |
+ | `id` | string | Classification ID in `cls_nw_xxx` format |
+ | `intent` | string[] | Detected intents, e.g. `["question"]` or `["question", "complaint"]` |
| `sentiment` | string | One of: `positive`, `negative`, `neutral`, `mixed` |
- | `dialect` | string | One of: `gulf`, `egyptian`, `levantine`, `msa` |
- | `dialect_confidence` | number | Confidence score between 0 and 1 |
- | `toxicity` | string | One of: `none`, `mild`, `moderate`, `severe` |
- | `categories` | array | Content categories: `engagement`, `support`, `feedback`, `spam` |
- | `cached` | boolean | `true` if served from semantic cache (no cost) |
+ | `dialect` | string \| null | One of: `gulf`, `egyptian`, `levantine`, `maghrebi`, `msa`. Null for English. |
+ | `dialect_confidence` | number \| null | Confidence score between 0 and 1. Null for English. |
+ | `requires_response` | boolean | Whether the comment needs a reply |
+ | `priority` | string | One of: `high`, `medium`, `low` |
+ | `suggested_reply` | object \| null | Auto-generated reply with `text` and `direction` |
+ | `provider` | string | AI provider used: `claude`, `gemini`, or `allam` |
+ | `cost_usd` | number | Cost in USD for this request |
diff --git a/rate-limits.mdx b/rate-limits.mdx
index 26c3d15..bd39d54 100644
--- a/rate-limits.mdx
+++ b/rate-limits.mdx
@@ -1,5 +1,5 @@
---
-title: "Rate Limits"
+title: "Rate limits"
sidebarTitle: "Rate Limits"
description: "Rate limit tiers, response headers, and strategies for handling 429 errors gracefully."
---
@@ -10,15 +10,22 @@ NAWA enforces per-minute rate limits on each API key to ensure fair usage and pl
| Tier | Requests/min | How you get it |
|------|-------------|----------------|
-| **Free** | 10 | Free keys (`nawa_test_sk_`) |
+| **Sandbox** | 10 | Free keys (`nawa_test_sk_`) |
+| **Trial** | 60 | Live keys (`nawa_live_sk_`) on trial |
| **Growth** | 120 | Live keys (`nawa_live_sk_`) with credits |
| **Enterprise** | 300 | Contact [sales@trynawa.com](mailto:sales@trynawa.com) |
| **Enterprise+** | 1,000 | Contact [sales@trynawa.com](mailto:sales@trynawa.com) |
- Free keys are rate-limited to 10 requests/minute and have a hard cap of 100 lifetime requests. Live keys start at the Growth tier (120/min). Enterprise tiers are available on request -- contact [sales@trynawa.com](mailto:sales@trynawa.com).
+ Free keys are rate-limited to 10 requests/minute and have a hard cap of 100 lifetime requests. Live keys on a trial start at 60/min, then move to the Growth tier (120/min) once you purchase credits. Enterprise tiers are available on request -- contact [sales@trynawa.com](mailto:sales@trynawa.com).
+## Global AI capacity limit
+
+NAWA enforces a global AI capacity limit across all users to stay within upstream provider ceilings. The current ceiling is **900 requests/minute** (based on Anthropic Tier 2). If the global limit is reached, you receive a `429` response with a `Retry-After` header indicating how long to wait.
+
+This limit is transparent to most users and only triggers during extreme traffic spikes. It adjusts automatically as NAWA upgrades provider tiers.
+
## Rate limit headers
Every API response includes rate limit headers:
@@ -39,7 +46,7 @@ On `429` responses, additional headers are included:
## Semantic cache and rate limits
- Semantic cache hits (`X-NAWA-Cache: HIT`) do **not** count toward your rate limits. If you're classifying similar comments repeatedly, caching effectively increases your throughput.
+ Semantic cache hits (`X-NAWA-Cache: HIT`) do **not** count toward your rate limits. If you are classifying similar comments repeatedly, caching effectively increases your throughput.
## Handling 429 errors