diff --git a/api-reference/analytics.mdx b/api-reference/analytics.mdx
index c2879c7..399f0e3 100644
--- a/api-reference/analytics.mdx
+++ b/api-reference/analytics.mdx
@@ -1,11 +1,11 @@
---
title: "Analytics"
sidebarTitle: "GET /v1/analytics"
-description: "Aggregated analytics on comment trends, sentiment shifts, and engagement patterns."
+description: "Aggregated analytics on API usage patterns, costs, cache performance, and latency."
api: "GET https://api.trynawa.com/v1/analytics"
---
-Retrieve aggregated analytics across your classified comments. This endpoint is **free**.
+Retrieve aggregated analytics on your API usage including request counts, costs, cache hits, latency, and error rates. This endpoint is **free**.
## Request
@@ -13,19 +13,40 @@ Retrieve aggregated analytics across your classified comments. This endpoint is
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `channel_id` | string | No | Filter by channel ID |
-| `platform` | string | No | Filter by platform |
| `from` | string | No | Start date (ISO 8601) |
| `to` | string | No | End date (ISO 8601) |
-| `group_by` | string | No | Group results by: `day`, `week`, `month`. Default: `day`. |
+| `group_by` | string | No | Group breakdown by: `endpoint`, `day`, or `provider`. Default: `endpoint`. |
### Example request
-```bash
-curl "https://api.trynawa.com/v1/analytics?platform=youtube&from=2025-01-01T00:00:00Z&to=2025-01-31T23:59:59Z&group_by=week" \
+
+
+```bash cURL
+curl "https://api.trynawa.com/v1/analytics?from=2025-01-01T00:00:00Z&to=2025-01-31T23:59:59Z&group_by=endpoint" \
-H "Authorization: Bearer nawa_test_sk_xxx"
```
+```python Python
+import requests
+
+response = requests.get(
+ "https://api.trynawa.com/v1/analytics",
+ params={"from": "2025-01-01T00:00:00Z", "group_by": "endpoint"},
+ headers={"Authorization": "Bearer nawa_test_sk_xxx"}
+)
+data = response.json()
+```
+
+```typescript TypeScript
+const response = await fetch(
+ 'https://api.trynawa.com/v1/analytics?from=2025-01-01T00:00:00Z&group_by=endpoint',
+ { headers: { Authorization: 'Bearer nawa_test_sk_xxx' } }
+)
+const data = await response.json()
+```
+
+
+
## Response
### Success response (200)
@@ -34,49 +55,72 @@ curl "https://api.trynawa.com/v1/analytics?platform=youtube&from=2025-01-01T00:0
{
"success": true,
"result": {
- "period": {
- "from": "2025-01-01T00:00:00Z",
- "to": "2025-01-31T23:59:59Z"
- },
- "total_comments": 4521,
- "summary": {
- "intent": {
- "question": 1205,
- "praise": 1890,
- "complaint": 678,
- "suggestion": 412,
- "spam": 198,
- "other": 138
- },
- "sentiment": {
- "positive": 2100,
- "negative": 890,
- "neutral": 1231,
- "mixed": 300
+ "analytics": {
+ "total_requests": 12450,
+ "total_cost": 68.70,
+ "cache_hits": 2231,
+ "cache_hit_rate": 0.18,
+ "avg_latency_ms": 920,
+ "success_rate": 0.99,
+ "error_count": 125,
+ "endpoint_distribution": {
+ "/v1/classify": 8500,
+ "/v1/translate": 1200,
+ "/v1/comments/reply": 1750,
+ "/v1/detect": 1000
},
- "dialect": {
- "gulf": 2015,
- "egyptian": 1302,
- "levantine": 789,
- "msa": 415
+ "provider_distribution": {
+ "allam": 9200,
+ "claude": 3250
},
- "toxicity": {
- "none": 3950,
- "mild": 320,
- "moderate": 180,
- "severe": 71
+ "period": {
+ "from": "2025-01-01T00:00:00Z",
+ "to": "2025-01-31T23:59:59Z"
}
},
- "trends": [
+ "by_endpoint": [
{
- "period": "2025-01-06/2025-01-12",
- "total": 1123,
- "sentiment_score": 0.72,
- "top_intent": "praise"
+ "endpoint": "/v1/classify",
+ "requests": 8500,
+ "cost": 51.00,
+ "avg_latency_ms": 820
+ },
+ {
+ "endpoint": "/v1/translate",
+ "requests": 1200,
+ "cost": 6.00,
+ "avg_latency_ms": 1100
}
]
},
"errors": [],
- "request_id": "req_ana_abc123"
+ "request_id": "req_nw_ana1a2b3c4d5e"
}
```
+
+### Analytics summary fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `total_requests` | integer | Total API requests in the period |
+| `total_cost` | number | Total cost in USD |
+| `cache_hits` | integer | Number of requests served from cache |
+| `cache_hit_rate` | number | Cache hit rate as a decimal (0-1) |
+| `avg_latency_ms` | number | Average response latency in milliseconds |
+| `success_rate` | number | Success rate as a decimal (0-1) |
+| `error_count` | integer | Number of requests with status >= 400 |
+| `endpoint_distribution` | object | Request counts per endpoint |
+| `provider_distribution` | object | Request counts per AI provider |
+| `period` | object | The `from` and `to` dates (null if not specified) |
+
+### Breakdown items
+
+The breakdown key changes based on `group_by`:
+
+| `group_by` | Key | Breakdown fields |
+|------------|-----|-----------------|
+| `endpoint` | `by_endpoint` | `endpoint`, `requests`, `cost`, `avg_latency_ms` |
+| `day` | `by_day` | `day` (YYYY-MM-DD), `requests`, `cost`, `avg_latency_ms` |
+| `provider` | `by_provider` | `provider`, `requests`, `cost`, `avg_latency_ms` |
+
+Breakdowns are sorted by `requests` descending.
diff --git a/api-reference/classify.mdx b/api-reference/classify.mdx
index ac3d730..6c17100 100644
--- a/api-reference/classify.mdx
+++ b/api-reference/classify.mdx
@@ -1,18 +1,18 @@
---
-title: "Classify Comment"
+title: "Classify text"
sidebarTitle: "POST /v1/classify"
-description: "Classify an Arabic or English comment by intent, sentiment, dialect, and toxicity in a single API call."
+description: "Classify Arabic or English text 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.
-
-## Request
+Classify any comment with a single request. Returns intent (as an array), sentiment, dialect, priority, and a suggested reply direction. Arabic text is routed to HUMAIN's ALLaM model for dialect-aware classification. English text is routed to Claude. Semantic caching reduces cost for repeated or similar inputs.
- 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`).
+## Request
+
### Headers
| Header | Required | Description |
@@ -20,14 +20,18 @@ 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` |
+### Query parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `provider` | string | No | Force a specific AI provider for A/B testing: `claude`, `gemini`, or `allam`. |
+
### 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 text to classify. |
+| `context` | object | No | Optional context object (reserved for future use). |
### Example request
@@ -38,36 +42,28 @@ 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": "متى الجزء الثاني؟"
}'
```
+```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.classify(text="متى الجزء الثاني؟")
+```
+
```typescript TypeScript
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: 'متى الجزء الثاني؟'
})
```
-```python Python
-from nawa import Nawa
-
-nawa = Nawa(api_key="your_api_key")
-
-result = nawa.classify(
- text="متى الجزء الثاني؟",
- platform="youtube",
- channel_id="ch_123"
-)
-```
-
## Response
@@ -78,22 +74,28 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "متى الجزء الثاني؟",
- "intent": "question",
- "intent_confidence": 0.97,
+ "id": "cls_nw_a1b2c3d4e5f6",
+ "object": "classification",
+ "intent": ["question"],
"sentiment": "neutral",
- "sentiment_confidence": 0.91,
- "dialect": "gulf",
- "dialect_confidence": 0.95,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
"language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "dialect": "gulf",
+ "dialect_confidence": 0.92,
+ "requires_response": true,
+ "priority": "medium",
+ "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_a1b2c3d4e5f67890abcdef12"
}
```
@@ -105,35 +107,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-Cache` | `HIT` if served from semantic cache (no cost), `MISS` otherwise |
+| `X-NAWA-Provider` | AI provider used (`allam`, `claude`, or `gemini`) |
### 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) |
-| `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. |
-| `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`) |
-| `model` | string | Model version used for classification |
-| `cached` | boolean | Whether this was served from semantic cache |
+| `id` | string | Classification ID in `cls_nw_xxx` format |
+| `object` | string | Always `"classification"` |
+| `intent` | string[] | Detected intents as an array (e.g. `["question"]`, `["praise", "suggestion"]`) |
+| `sentiment` | string | `positive`, `negative`, `neutral`, or `mixed` |
+| `language` | string | Detected language: `ar`, `en`, or `mixed` |
+| `dialect` | string or null | Arabic dialect: `gulf`, `egyptian`, `levantine`, `msa`. Null for English text. |
+| `dialect_confidence` | number or null | Confidence score 0-1. Null for English text. |
+| `requires_response` | boolean | Whether the comment warrants a reply |
+| `priority` | string | `high`, `medium`, or `low` |
+| `suggested_reply` | object | Contains `text` (reply suggestion) and `direction` (`rtl` or `ltr`) |
+| `provider` | string | AI provider used: `allam`, `claude`, or `gemini` |
+| `model` | string | Model identifier |
+| `fallback_used` | boolean | Whether a fallback provider was used |
+| `tokens_used` | integer or null | Always `null` in the current version |
+| `cost_usd` | number | Cost in USD |
+| `credits_used` | integer | Credits deducted (6 per request) |
### Error responses
| Status | Type | When |
|--------|------|------|
-| 400 | `invalid_request_error` | Missing `text`, invalid `platform`, text too long |
+| 400 | `invalid_request_error` | Missing `text` or invalid `provider` query parameter |
| 401 | `authentication_error` | Invalid or missing API key |
-| 402 | `insufficient_credits` | No credits remaining |
+| 402 | `permission_error` | No credits remaining |
| 429 | `rate_limit_error` | Rate limit exceeded |
| 500 | `api_error` | Internal or provider error |
@@ -145,7 +149,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,98 +157,38 @@ 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_001",
+ "object": "classification",
+ "intent": ["praise"],
"sentiment": "positive",
- "sentiment_confidence": 0.98,
- "dialect": null,
- "dialect_confidence": null,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "en",
- "model": "claude-v1",
- "cached": false
- },
- "errors": [],
- "request_id": "req_en_praise_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": "The audio quality is terrible in this one. Can barely hear anything after the 5 minute mark.", "platform": "youtube"}'
- ```
-
- Response:
- ```json
- {
- "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,
- "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": false,
+ "priority": "low",
+ "suggested_reply": {
+ "text": "",
+ "direction": "ltr"
+ },
+ "provider": "claude",
+ "model": "claude-haiku-4-5-20251001",
+ "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_praise_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": "ما شاء الله عليك، محتوى رهيب!", "platform": "youtube"}'
+ -d '{"text": "ما شاء الله عليك، محتوى رهيب!"}'
```
Response:
@@ -252,32 +196,38 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "ما شاء الله عليك، محتوى رهيب!",
- "intent": "praise",
- "intent_confidence": 0.98,
+ "id": "cls_nw_ar_praise_001",
+ "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": {
+ "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_ghi789jkl012"
+ "request_id": "req_nw_ar_praise_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": "الصوت وحش أوي في الفيديو ده", "platform": "youtube"}'
+ -d '{"text": "الصوت وحش أوي في الفيديو ده"}'
```
Response:
@@ -285,55 +235,28 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "الصوت وحش أوي في الفيديو ده",
- "intent": "complaint",
- "intent_confidence": 0.94,
+ "id": "cls_nw_eg_comp_001",
+ "object": "classification",
+ "intent": ["complaint"],
"sentiment": "negative",
- "sentiment_confidence": 0.96,
- "dialect": "egyptian",
- "dialect_confidence": 0.97,
- "toxicity": "none",
- "toxicity_confidence": 0.95,
- "categories": ["feedback"],
"language": "ar",
- "model": "nagl-v1",
- "cached": false
- },
- "errors": [],
- "request_id": "req_mno345pqr678"
- }
- ```
-
-
-
- ```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"}'
- ```
-
- Response:
- ```json
- {
- "success": true,
- "result": {
- "text": "لو سمحت حاول تحكي عن المطاعم بلبنان",
- "intent": "suggestion",
- "intent_confidence": 0.92,
- "sentiment": "neutral",
- "sentiment_confidence": 0.88,
- "dialect": "levantine",
- "dialect_confidence": 0.96,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "dialect": "gulf",
+ "dialect_confidence": 0.94,
+ "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_stu901vwx234"
+ "request_id": "req_nw_eg_comp_001"
}
```
diff --git a/api-reference/comments-list.mdx b/api-reference/comments-list.mdx
index 1e18d08..f410085 100644
--- a/api-reference/comments-list.mdx
+++ b/api-reference/comments-list.mdx
@@ -1,11 +1,11 @@
---
-title: "List Comments"
+title: "List comments"
sidebarTitle: "GET /v1/comments"
-description: "Retrieve and filter classified comments with pagination."
+description: "Retrieve and filter classified comments with offset-based pagination."
api: "GET https://api.trynawa.com/v1/comments"
---
-Retrieve classified comments with filtering and pagination. This endpoint is **free**.
+Retrieve classified comments with filtering and offset-based pagination. This endpoint is **free**.
## Request
@@ -13,27 +13,43 @@ Retrieve classified comments with filtering and pagination. This endpoint is **f
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `channel_id` | string | No | Filter by channel ID |
| `platform` | string | No | Filter by platform: `youtube`, `instagram`, `twitter`, `facebook` |
| `intent` | string | No | Filter by intent: `question`, `complaint`, `praise`, `suggestion`, `spam`, `other` |
| `sentiment` | string | No | Filter by sentiment: `positive`, `negative`, `neutral`, `mixed` |
| `dialect` | string | No | Filter by dialect: `gulf`, `egyptian`, `levantine`, `msa` |
-| `toxicity` | string | No | Filter by toxicity: `none`, `mild`, `moderate`, `severe` |
-| `from` | string | No | Start date (ISO 8601): `2025-01-01T00:00:00Z` |
-| `to` | string | No | End date (ISO 8601): `2025-01-31T23:59:59Z` |
-| `limit` | integer | No | Results per page (1–100). Default: 25. |
-| `cursor` | string | No | Pagination cursor from previous response. |
+| `limit` | integer | No | Results per page (1-100). Default: `25`. |
+| `offset` | integer | No | Number of results to skip. Default: `0`. |
### Example request
```bash cURL
-curl "https://api.trynawa.com/v1/comments?platform=youtube&intent=question&limit=10" \
+curl "https://api.trynawa.com/v1/comments?platform=youtube&intent=question&limit=10&offset=0" \
-H "Authorization: Bearer nawa_test_sk_xxx"
```
+```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.comments.list(
+ platform="youtube",
+ intent="question",
+ limit=10
+)
+
+# Iterate through pages
+for comment in nawa.comments.list(platform="youtube"):
+ print(comment.text)
+```
+
```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
const { data, error } = await nawa.comments.list({
platform: 'youtube',
intent: 'question',
@@ -46,18 +62,6 @@ for await (const comment of nawa.comments.list({ platform: 'youtube' })) {
}
```
-```python Python
-result = nawa.comments.list(
- platform="youtube",
- intent="question",
- limit=10
-)
-
-# Auto-pagination
-for comment in nawa.comments.list(platform="youtube"):
- print(comment.text)
-```
-
## Response
@@ -71,23 +75,47 @@ for comment in nawa.comments.list(platform="youtube"):
"comments": [
{
"id": "cmt_abc123",
+ "request_id": "req_nw_a1b2c3d4e5f6",
"text": "متى الجزء الثاني؟",
"platform": "youtube",
"intent": "question",
"sentiment": "neutral",
"dialect": "gulf",
- "toxicity": "none",
- "created_at": "2025-01-15T10:30:00Z",
- "channel_id": "ch_123"
+ "created_at": "2025-01-15T10:30:00Z"
}
],
"pagination": {
- "has_more": true,
- "next_cursor": "cur_def456",
- "total_count": 1523
+ "total": 1523,
+ "limit": 25,
+ "offset": 0,
+ "has_more": true
}
},
"errors": [],
- "request_id": "req_lst_abc123"
+ "request_id": "req_nw_lst1a2b3c4d5e"
}
```
+
+### Comment fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Comment ID |
+| `request_id` | string | The original classification request ID |
+| `text` | string | The comment text |
+| `platform` | string | Source platform |
+| `intent` | string | Detected intent |
+| `sentiment` | string | Detected sentiment |
+| `dialect` | string | Detected Arabic dialect |
+| `created_at` | string | ISO 8601 timestamp |
+
+### Pagination fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `total` | integer | Total matching comments |
+| `limit` | integer | Results per page |
+| `offset` | integer | Current offset |
+| `has_more` | boolean | Whether more results exist beyond this page |
+
+Results are ordered by `created_at` descending (newest first).
diff --git a/api-reference/comments-reply.mdx b/api-reference/comments-reply.mdx
index d6321d7..efcc9b6 100644
--- a/api-reference/comments-reply.mdx
+++ b/api-reference/comments-reply.mdx
@@ -1,62 +1,71 @@
---
-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: "Classify and reply"
+sidebarTitle: "POST /v1/comments/reply"
+description: "Classify a comment and generate a contextual reply in a single API call."
+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 a culturally-aware reply in a single call. For Arabic comments, replies match the detected dialect. Supports tone control and max length configuration.
- Cost: **$0.008** per request (8 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
+ Cost: **$0.008** per request (8 credits). This endpoint does not use semantic caching.
## 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`, or `allam`. |
### 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. |
+| `tone` | string | No | Reply tone: `friendly`, `professional`, `casual`, `formal`. Default: `professional`. |
+| `max_length` | integer | No | Maximum reply length in characters (1-2000). Default: `500`. |
### 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 '{
- "tone": "friendly",
- "context": "Tech review channel focused on smartphones"
+ "text": "هذا المنتج سيء جداً ولا أنصح به",
+ "tone": "professional",
+ "max_length": 500
}'
```
-```typescript TypeScript
-const { data, error } = await nawa.comments.reply('cmt_abc123', {
- tone: 'friendly',
- context: '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",
- tone="friendly",
- context="Tech review channel focused on smartphones"
+ text="هذا المنتج سيء جداً ولا أنصح به",
+ tone="professional",
+ max_length=500
)
```
+```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.comments.reply({
+ text: 'هذا المنتج سيء جداً ولا أنصح به',
+ tone: 'professional',
+ maxLength: 500
+})
+```
+
## Response
@@ -67,15 +76,26 @@ 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_a1b2c3d4e5f6",
+ "object": "comment_reply",
+ "classification": {
+ "intent": ["complaint"],
+ "sentiment": "negative",
+ "priority": "high",
+ "requires_response": true
+ },
+ "reply": {
+ "text": "نعتذر عن التجربة السيئة. نتمنى تتواصل معنا عشان نقدر نساعدك.",
+ "direction": "rtl",
+ "tone": "professional"
+ },
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "cost_usd": 0.008,
+ "credits_used": 8
},
"errors": [],
- "request_id": "req_rep789xyz012"
+ "request_id": "req_nw_rpl1a2b3c4d5e"
}
```
@@ -83,9 +103,28 @@ result = nawa.comments.reply(
| 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 results for the input comment |
+| `classification.intent` | string[] | Detected intents as an array |
+| `classification.sentiment` | string | `positive`, `negative`, `neutral`, or `mixed` |
+| `classification.priority` | string | `high`, `medium`, or `low` |
+| `classification.requires_response` | boolean | Whether the comment warrants a reply |
+| `reply` | object | The generated reply |
+| `reply.text` | string | Generated reply text |
+| `reply.direction` | string | `rtl` for Arabic, `ltr` for English |
+| `reply.tone` | string | The tone used |
+| `provider` | string | AI provider used |
+| `model` | string | Model identifier |
+| `cost_usd` | number | Cost in USD |
+| `credits_used` | integer | Credits deducted (8 per request) |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `text`. Invalid `tone` or `max_length`. |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 402 | `permission_error` | No credits remaining |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal or provider error |
diff --git a/api-reference/detect.mdx b/api-reference/detect.mdx
index 2169b2c..da78907 100644
--- a/api-reference/detect.mdx
+++ b/api-reference/detect.mdx
@@ -1,5 +1,5 @@
---
-title: "Detect Language & Dialect"
+title: "Detect language and dialect"
sidebarTitle: "POST /v1/detect"
description: "Lightweight language and dialect detection. Returns in under 100ms -- no AI model call required."
api: "POST https://api.trynawa.com/v1/detect"
@@ -29,12 +29,23 @@ Detect language, dialect, script, and text direction from any text input. Uses l
### Example request
+
```bash cURL
curl -X POST https://api.trynawa.com/v1/detect \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"text": "وش رايكم بالمحتوى الجديد؟"}'
```
+
+```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.detect(text="وش رايكم بالمحتوى الجديد؟")
+print(f"{result.data.language} / {result.data.dialect}")
+```
+
```typescript TypeScript
import { Nawa } from '@nawalabs/sdk'
@@ -43,19 +54,15 @@ const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
const { data, error } = await nawa.detect({
text: 'وش رايكم بالمحتوى الجديد؟'
})
+console.log(`${data.language} / ${data.dialect}`)
```
-```python Python
-from nawa import Nawa
-nawa = Nawa(api_key="your_api_key")
-
-result = nawa.detect(text="وش رايكم بالمحتوى الجديد؟")
-```
## Response
### Success response (200)
+
```json
{
"success": true,
@@ -70,12 +77,12 @@ result = nawa.detect(text="وش رايكم بالمحتوى الجديد؟")
"text_direction": "rtl",
"word_count": 4,
"fallback_used": false,
- "cached": false,
"cost_usd": 0.002,
- "credits_used": 2
+ "credits_used": 2,
+ "cached": false
},
"errors": [],
- "request_id": "req_nw_4c11w71e44cb"
+ "request_id": "req_nw_det1a2b3c4d5e"
}
```
@@ -83,17 +90,19 @@ result = nawa.detect(text="وش رايكم بالمحتوى الجديد؟")
| Field | Type | Description |
|-------|------|-------------|
+| `id` | string | Detection ID in `det_nw_xxx` format |
+| `object` | string | Always `"detection"` |
| `text` | string | The original input text |
| `language` | string | Detected language: `ar`, `en`, or `mixed` |
| `dialect` | string or null | Detected Arabic dialect: `gulf`, `egyptian`, `levantine`, `msa`. Null if not Arabic. |
-| `dialect_confidence` | number or null | Confidence score 0--1 for dialect detection. Null if not Arabic. |
+| `dialect_confidence` | number or null | Confidence score 0-1 for dialect detection. Null if not Arabic. |
| `script` | string | Script type: `arabic`, `latin`, or `mixed` |
| `text_direction` | string | Text direction: `rtl` or `ltr` |
-| `word_count` | number | Number of words in the input text |
+| `word_count` | integer | Number of words in the input text |
| `fallback_used` | boolean | Always `false` for this endpoint (local detection only) |
-| `cached` | boolean | Whether served from semantic cache |
| `cost_usd` | number | Cost in USD |
-| `credits_used` | number | Credits deducted |
+| `credits_used` | integer | Credits deducted (2 per request) |
+| `cached` | boolean | Whether served from semantic cache |
### Detection examples
@@ -149,6 +158,6 @@ curl -X POST https://api.trynawa.com/v1/detect \
|--------|------|------|
| 400 | `invalid_request_error` | Missing `text`. Text too long. |
| 401 | `authentication_error` | Invalid or missing API key |
-| 402 | `insufficient_credits` | No credits remaining |
+| 402 | `permission_error` | No credits remaining |
| 429 | `rate_limit_error` | Rate limit exceeded |
| 500 | `api_error` | Internal error |
diff --git a/api-reference/feedback.mdx b/api-reference/feedback.mdx
index 4cddf8b..769759d 100644
--- a/api-reference/feedback.mdx
+++ b/api-reference/feedback.mdx
@@ -1,14 +1,14 @@
---
-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 result to improve NAWA's accuracy over time."
api: "POST https://api.trynawa.com/v1/feedback"
---
Submit reinforcement learning from human feedback (RLHF) to continuously improve NAWA's classification accuracy.
- This endpoint is **free** -- 0 credits, no cost.
+ This endpoint is **free** - 0 credits, no cost.
@@ -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 | Array of correct intents if the classification was wrong. |
+| `corrected_sentiment` | string | No | Correct sentiment: `positive`, `negative`, `neutral`, `mixed`. |
+| `comment` | string | No | Free-text explanation of why this correction is needed. |
### Example request
@@ -35,31 +36,42 @@ 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_a1b2c3d4e5f6",
+ "rating": "incorrect",
+ "corrected_intent": ["question"],
+ "corrected_sentiment": "neutral",
+ "comment": "This was a question, not a complaint"
}'
```
-```typescript TypeScript
-const { data, error } = await nawa.feedback.submit({
- requestId: 'req_abc123def456',
- field: 'dialect',
- expectedValue: 'levantine',
- comment: 'This is Lebanese Arabic, not Gulf'
-})
-```
-
```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_a1b2c3d4e5f6",
+ rating="incorrect",
+ corrected_intent=["question"],
+ corrected_sentiment="neutral",
+ comment="This was 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({
+ classificationId: 'cls_nw_a1b2c3d4e5f6',
+ rating: 'incorrect',
+ correctedIntent: ['question'],
+ correctedSentiment: 'neutral',
+ comment: 'This was a question, not a complaint'
+})
+```
+
## Response
@@ -70,11 +82,32 @@ 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_a1b2c3d4e5f6",
+ "object": "feedback",
+ "classification_id": "cls_nw_a1b2c3d4e5f6",
+ "rating": "incorrect",
+ "acknowledged": true
},
"errors": [],
- "request_id": "req_fb_abc123"
+ "request_id": "req_nw_fb1a2b3c4d5e"
}
```
+
+### 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 applies to |
+| `rating` | string | Your rating: `correct`, `incorrect`, or `partial` |
+| `acknowledged` | boolean | Always `true` on success |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `classification_id` or `rating`. Invalid `rating` value. |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal error |
diff --git a/api-reference/health.mdx b/api-reference/health.mdx
index ca110e8..15f62f7 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"
@@ -7,56 +7,99 @@ api: "GET https://api.trynawa.com/v1/health"
Check the operational status of the NAWA API. This endpoint is **free** and does **not** require authentication.
+
+ The health endpoint returns a flat JSON object, not the standard `{ success, result, errors, request_id }` envelope used by other endpoints.
+
+
## Request
-```bash
+
+
+```bash cURL
curl https://api.trynawa.com/v1/health
```
+```python Python
+import requests
+
+response = requests.get("https://api.trynawa.com/v1/health")
+data = response.json()
+print(data["status"])
+```
+
+```typescript TypeScript
+const response = await fetch('https://api.trynawa.com/v1/health')
+const data = await response.json()
+console.log(data.status)
+```
+
+
+
## Response
### Healthy response (200)
```json
{
- "success": true,
- "result": {
- "status": "healthy",
- "version": "1.0.0",
- "services": {
- "api": "operational",
- "classification": "operational",
- "database": "operational",
- "cache": "operational"
+ "status": "healthy",
+ "version": "v1",
+ "services": {
+ "database": {
+ "status": "healthy",
+ "latency_ms": 12
},
- "timestamp": "2025-01-15T12:00:00Z"
+ "redis": {
+ "status": "healthy",
+ "latency_ms": 3
+ },
+ "nagl": {
+ "status": "healthy"
+ }
},
- "errors": [],
- "request_id": "req_hlt_abc123"
+ "timestamp": "2025-01-15T12:00:00Z",
+ "latency_ms": 15
}
```
-### Degraded response (200)
+### Degraded response (503)
```json
{
- "success": true,
- "result": {
- "status": "degraded",
- "version": "1.0.0",
- "services": {
- "api": "operational",
- "classification": "degraded",
- "database": "operational",
- "cache": "operational"
+ "status": "degraded",
+ "version": "v1",
+ "services": {
+ "database": {
+ "status": "healthy",
+ "latency_ms": 12
+ },
+ "redis": {
+ "status": "down",
+ "latency_ms": 0
},
- "timestamp": "2025-01-15T12:00:00Z"
+ "nagl": {
+ "status": "healthy"
+ }
},
- "errors": [],
- "request_id": "req_hlt_def456"
+ "timestamp": "2025-01-15T12:00:00Z",
+ "latency_ms": 12
}
```
+### Response fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `status` | string | `healthy` when all services are up, `degraded` when one or more services are down |
+| `version` | string | API version (always `v1`) |
+| `services` | object | Individual service statuses |
+| `services.database` | object | Database health with `status` and `latency_ms` |
+| `services.redis` | object | Redis cache health with `status` and `latency_ms` |
+| `services.nagl` | object | NAGL classification engine health with `status` |
+| `timestamp` | string | ISO 8601 timestamp of the check |
+| `latency_ms` | integer | Total health check latency |
+
+Each service reports `status` as `healthy`, `degraded`, or `down`.
+
For real-time status monitoring, visit [status.trynawa.com](https://status.trynawa.com).
diff --git a/api-reference/moderate.mdx b/api-reference/moderate.mdx
index 947d30e..4fca7b3 100644
--- a/api-reference/moderate.mdx
+++ b/api-reference/moderate.mdx
@@ -1,11 +1,11 @@
---
-title: "Moderate Content"
+title: "Moderate content"
sidebarTitle: "POST /v1/moderate"
description: "Dialect-aware Arabic content moderation. Catches profanity and slang that Western APIs miss."
api: "POST https://api.trynawa.com/v1/moderate"
---
-Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity, slang, and offensive language differences across Gulf, Egyptian, Levantine, and MSA Arabic -- catching what Western moderation APIs miss entirely.
+Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity, slang, and offensive language differences across Gulf, Egyptian, Levantine, and MSA Arabic -- catching what Western moderation APIs miss entirely. Falls back to Claude Haiku 4.5 if ALLaM is unavailable.
Cost: **$0.004** per request (4 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
@@ -25,8 +25,8 @@ Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity,
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `text` | string | Yes | The text to moderate. Max 5,000 characters. |
-| `context` | string | No | Where the text appears (e.g. "youtube comment", "product review"). Helps the model calibrate. |
| `strictness` | string | No | Moderation strictness: `low`, `medium`, `high`. Default: `medium`. |
+| `context` | string | No | Where the text appears (e.g. "youtube comment", "product review"). Helps the model calibrate. |
| `platform` | string | No | Source platform: `youtube`, `instagram`, `twitter`, `facebook`. |
**Strictness levels:**
@@ -37,6 +37,7 @@ Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity,
### Example request
+
```bash cURL
curl -X POST https://api.trynawa.com/v1/moderate \
-H "Authorization: Bearer nawa_test_sk_xxx" \
@@ -46,6 +47,24 @@ curl -X POST https://api.trynawa.com/v1/moderate \
"strictness": "medium"
}'
```
+
+```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.moderate(
+ text="هذا محتوى رائع جدا",
+ strictness="medium"
+)
+
+if result.data.is_safe:
+ print("Content is safe")
+else:
+ print(f"Flagged: {result.data.flags}")
+ print(f"Cleaned: {result.data.cleaned_text}")
+```
+
```typescript TypeScript
import { Nawa } from '@nawalabs/sdk'
@@ -63,27 +82,13 @@ if (data.is_safe) {
console.log('Cleaned:', data.cleaned_text)
}
```
-```python Python
-from nawa import Nawa
-
-nawa = Nawa(api_key="your_api_key")
-
-result = nawa.moderate(
- text="هذا محتوى رائع جدا",
- strictness="medium"
-)
-if result.data.is_safe:
- print("Content is safe")
-else:
- print(f"Flagged: {result.data.flags}")
- print(f"Cleaned: {result.data.cleaned_text}")
-```
## Response
### Safe content (200)
+
```json
{
"success": true,
@@ -126,7 +131,7 @@ else:
"credits_used": 4
},
"errors": [],
- "request_id": "req_nw_4c11w71e44cb"
+ "request_id": "req_nw_mod1a2b3c4d5e"
}
```
@@ -147,7 +152,7 @@ When content is flagged, the response includes specific flags, severity scores,
"severity": "high",
"severity_score": 0.87,
"language": "ar",
- "dialect": "egyptian",
+ "dialect": "gulf",
"categories": {
"profanity": true,
"hate_speech": false,
@@ -184,89 +189,45 @@ When content is flagged, the response includes specific flags, severity scores,
| Field | Type | Description |
|-------|------|-------------|
+| `id` | string | Moderation ID in `mod_nw_xxx` format |
+| `object` | string | Always `"moderation"` |
| `text` | string | The original input text |
| `is_safe` | boolean | Whether the content passed moderation |
| `verdict` | string | `safe` or `flagged` |
-| `flags` | array | List of triggered category names |
+| `flags` | string[] | List of triggered category names |
| `severity` | string | Overall severity: `none`, `low`, `medium`, `high` |
-| `severity_score` | number | Severity score 0--1 |
+| `severity_score` | number | Severity score 0-1 |
| `language` | string | Detected language: `ar`, `en`, or `mixed` |
| `dialect` | string or null | Detected Arabic dialect. Null if not Arabic. |
| `categories` | object | Boolean flags for each moderation category |
-| `category_scores` | object | Confidence scores 0--1 for each category |
-| `flagged_terms` | array | Specific problematic words/phrases found |
-| `cleaned_text` | string or null | Text with flagged terms replaced by `***`. Null if safe. |
-| `model` | string | Model version used |
+| `category_scores` | object | Confidence scores (0-1) for each category |
+| `flagged_terms` | string[] | Specific problematic words/phrases found |
+| `cleaned_text` | string or null | Text with flagged terms replaced by `***`. Null if content is safe. |
+| `model` | string | Model version (always `nagl-v1`) |
| `provider` | string | AI provider used (`allam` or `claude`) |
| `fallback_used` | boolean | Whether the fallback provider was used |
| `cached` | boolean | Whether served from semantic cache |
| `cost_usd` | number | Cost in USD |
-| `credits_used` | number | Credits deducted |
+| `credits_used` | integer | Credits deducted (4 per request) |
### Moderation categories
| Category | Description |
|----------|-------------|
| `profanity` | Swearing, vulgar language, dialect-specific profanity |
-| `hate_speech` | Content targeting groups based on race, religion, ethnicity, etc. |
+| `hate_speech` | Content targeting groups based on race, religion, ethnicity |
| `harassment` | Personal attacks, bullying, intimidation |
| `sexual` | Sexually explicit or suggestive content |
| `spam` | Promotional content, repetitive text, link spam |
| `self_harm` | Content promoting or describing self-harm |
| `violence` | Threats, glorification of violence, graphic descriptions |
-### Examples
-
-
-
- **Input:** "هذا محتوى رائع جدا"
- **Result:** is_safe: `true`, verdict: `safe`, severity: `none`
-```bash
-curl -X POST https://api.trynawa.com/v1/moderate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "هذا محتوى رائع جدا", "strictness": "medium"}'
-```
-
-
-
- **Input:** "اشتركوا في قناتي الرابط في البايو!! فولو فولو فولو!!"
- **Result:** is_safe: `false`, flags: `["spam"]`, severity: `low`
-```bash
-curl -X POST https://api.trynawa.com/v1/moderate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "اشتركوا في قناتي الرابط في البايو!! فولو فولو فولو!!", "strictness": "medium"}'
-```
-
-
-
- **Input:** Borderline content that passes on medium but flags on high strictness
-```bash
-curl -X POST https://api.trynawa.com/v1/moderate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "your text here", "strictness": "high"}'
-```
-
-
-
- **Input:** Moderate a YouTube comment with platform context
-```bash
-curl -X POST https://api.trynawa.com/v1/moderate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "your text here", "strictness": "medium", "platform": "youtube", "context": "youtube comment"}'
-```
-
-
-
### Error responses
| Status | Type | When |
|--------|------|------|
| 400 | `invalid_request_error` | Missing `text`. Invalid strictness or platform. Text too long. |
| 401 | `authentication_error` | Invalid or missing API key |
-| 402 | `insufficient_credits` | No credits remaining |
+| 402 | `permission_error` | No credits remaining |
| 429 | `rate_limit_error` | Rate limit exceeded |
| 500 | `api_error` | Internal or provider error |
diff --git a/api-reference/rubric-classify.mdx b/api-reference/rubric-classify.mdx
index 4489175..2b5bde9 100644
--- a/api-reference/rubric-classify.mdx
+++ b/api-reference/rubric-classify.mdx
@@ -1,83 +1,97 @@
---
-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 text against a custom rubric with user-defined categories and configurable confidence thresholds."
+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 text against a custom rubric you define. Bring your own categories and optional descriptions for domain-specific classification. Supports multi-label mode and configurable confidence thresholds.
- 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 does not use semantic caching.
## Request
+### Query parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `provider` | string | No | Force a specific AI provider: `claude`, `gemini`, or `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 text to classify. |
+| `rubric` | object | Yes | The rubric definition. |
+| `rubric.categories` | array | Yes | Array of category objects. Each must have a `name` (string). Optional `description` (string) guides the model. Min 1, max 20. |
+| `rubric.multi_label` | boolean | No | If `true`, returns all matching categories above the threshold. Default: `false`. |
+| `rubric.confidence_threshold` | number | No | Minimum confidence to include a category. Range 0-1. 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"
- }'
-```
-
-```typescript TypeScript
-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'
+ "categories": [
+ { "name": "translation_request", "description": "User is asking for subtitles or translation" },
+ { "name": "technical_issue", "description": "Audio, video, or playback problems" },
+ { "name": "content_feedback" },
+ { "name": "off_topic" }
+ ],
+ "multi_label": false,
+ "confidence_threshold": 0.5
}
- },
- platform: 'youtube'
-})
+ }'
```
```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",
- },
- },
- 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"},
+ {"name": "off_topic"}
+ ],
+ "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: [
+ { name: 'translation_request', description: 'User is asking for subtitles or translation' },
+ { name: 'technical_issue', description: 'Audio, video, or playback problems' },
+ { name: 'content_feedback' },
+ { name: 'off_topic' }
+ ],
+ multiLabel: false,
+ confidenceThreshold: 0.5
+ }
+})
+```
+
## Response
@@ -88,23 +102,22 @@ 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_a1b2c3d4e5f6",
+ "object": "rubric_classification",
+ "categories": [
+ { "name": "translation_request", "confidence": 0.94 },
+ { "name": "technical_issue", "confidence": 0.03 }
+ ],
+ "multi_label": false,
"language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "cost_usd": 0.003,
+ "credits_used": 3
},
"errors": [],
- "request_id": "req_rub123abc456"
+ "request_id": "req_nw_rub123abc456"
}
```
@@ -112,10 +125,23 @@ result = nawa.rubric.classify(
| 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` | array | Array of `{ name, confidence }` objects. Filtered by `confidence_threshold`. Sorted by confidence descending. |
+| `multi_label` | boolean | Whether multi-label mode was used |
+| `language` | string | Detected language: `ar`, `en`, or `mixed` |
+| `provider` | string | AI provider used |
+| `model` | string | Model identifier |
+| `fallback_used` | boolean | Whether a fallback provider was used |
+| `cost_usd` | number | Cost in USD |
+| `credits_used` | integer | Credits deducted (3 per request) |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `text` or `rubric`. Empty or oversized `categories` array. Category missing `name`. |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 402 | `permission_error` | No credits remaining |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal or provider error |
diff --git a/api-reference/translate.mdx b/api-reference/translate.mdx
index c10d2ba..b90e72b 100644
--- a/api-reference/translate.mdx
+++ b/api-reference/translate.mdx
@@ -5,7 +5,7 @@ description: "Dialect-aware Arabic translation powered by HUMAIN's ALLaM model."
api: "POST https://api.trynawa.com/v1/translate"
---
-Translate text between English and Arabic with dialect awareness. Generates culturally appropriate translations in Gulf, Egyptian, Levantine, or MSA Arabic.
+Translate text between English and Arabic with dialect awareness. Generates culturally appropriate translations in Gulf, Egyptian, Levantine, or MSA Arabic. Falls back to Claude Haiku 4.5 if ALLaM is unavailable.
Cost: **$0.005** per request (5 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
@@ -25,59 +25,65 @@ Translate text between English and Arabic with dialect awareness. Generates cult
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `text` | string | Yes | The text to translate. Max 5,000 characters. |
-| `source` | string | No | Source language: `ar`, `en`, `auto`. Default: `auto` (detects automatically). |
-| `target` | string | Yes | Target language: `ar`, `en`. |
+| `source` | string | No | Source language: `ar`, `en`, `auto`. Default: `auto`. |
+| `target` | string | Yes | Target language: `ar` or `en`. Must differ from `source`. |
| `dialect` | string | No | Target dialect for Arabic output: `gulf`, `egyptian`, `levantine`, `msa`. Default: `msa`. |
| `tone` | string | No | Translation tone: `formal`, `casual`, `social_media`. Default: `formal`. |
-| `platform` | string | No | Source platform for social media tone context: `youtube`, `instagram`, `twitter`, `facebook`. |
-| `metadata` | object | No | Arbitrary key-value metadata to attach to the translation. |
+| `platform` | string | No | Source platform: `youtube`, `instagram`, `twitter`, `facebook` (validated but reserved for future use). |
### Example request
+
```bash cURL
- curl -X POST https://api.trynawa.com/v1/translate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{
- "text": "Hello, how are you?",
- "source": "en",
- "target": "ar",
- "dialect": "gulf",
- "tone": "casual"
- }'
+curl -X POST https://api.trynawa.com/v1/translate \
+ -H "Authorization: Bearer nawa_test_sk_xxx" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "text": "Hello, how are you?",
+ "source": "en",
+ "target": "ar",
+ "dialect": "gulf",
+ "tone": "casual"
+ }'
```
-```typescript TypeScript
- import { Nawa } from '@nawalabs/sdk'
- const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
-
- const { data, error } = await nawa.translate({
- text: 'Hello, how are you?',
- source: 'en',
- target: 'ar',
- dialect: 'gulf',
- tone: 'casual'
- })
-```
```python Python
- from nawa import Nawa
-
- nawa = Nawa(api_key="your_api_key")
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.translate(
+ text="Hello, how are you?",
+ source="en",
+ target="ar",
+ dialect="gulf",
+ tone="casual"
+)
+print(result.data.translated_text)
+```
- result = nawa.translate(
- text="Hello, how are you?",
- source="en",
- target="ar",
- dialect="gulf",
- tone="casual"
- )
+```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.translate({
+ text: 'Hello, how are you?',
+ source: 'en',
+ target: 'ar',
+ dialect: 'gulf',
+ tone: 'casual'
+})
+console.log(data.translatedText)
```
+
## Response
### Success response (200)
+
```json
{
"success": true,
@@ -91,9 +97,10 @@ Translate text between English and Arabic with dialect awareness. Generates cult
"source_dialect": null,
"target_dialect": "gulf",
"tone": "casual",
- "model": "nagl-v1",
"provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
"fallback_used": false,
+ "tokens_used": null,
"cached": false,
"cost_usd": 0.005,
"credits_used": 5
@@ -107,19 +114,22 @@ Translate text between English and Arabic with dialect awareness. Generates cult
| Field | Type | Description |
|-------|------|-------------|
-| `text` | string | The original input text |
-| `translated_text` | string | The translated output |
-| `source_language` | string | Detected or specified source language |
-| `target_language` | string | Target language |
-| `source_dialect` | string or null | Detected dialect if source is Arabic |
-| `target_dialect` | string or null | Target dialect if target is Arabic |
-| `tone` | string | The tone used for translation |
-| `model` | string | Model version used |
+| `id` | string | Translation ID in `trn_nw_xxx` format |
+| `object` | string | Always `"translation"` |
+| `text` | string | Original input text |
+| `translated_text` | string | Translated output |
+| `source_language` | string | Detected or specified source language: `ar` or `en` |
+| `target_language` | string | Target language: `ar` or `en` |
+| `source_dialect` | string or null | Detected dialect if source is Arabic. Null otherwise. |
+| `target_dialect` | string or null | Target dialect if target is Arabic. Null otherwise. |
+| `tone` | string | Tone used: `formal`, `casual`, or `social_media` |
| `provider` | string | AI provider used (`allam` or `claude`) |
+| `model` | string | Model identifier |
| `fallback_used` | boolean | Whether the fallback provider was used |
+| `tokens_used` | integer or null | Always `null` in the current version |
| `cached` | boolean | Whether served from semantic cache |
| `cost_usd` | number | Cost in USD |
-| `credits_used` | number | Credits deducted |
+| `credits_used` | integer | Credits deducted (5 per request) |
### Dialect examples
@@ -142,7 +152,7 @@ Translate text between English and Arabic with dialect awareness. Generates cult
curl -X POST https://api.trynawa.com/v1/translate \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
- -d '{"text": "Subscribe to my channel for more content!", "source": "en", "target": "ar", "dialect": "egyptian", "tone": "social_media", "platform": "youtube"}'
+ -d '{"text": "Subscribe to my channel for more content!", "source": "en", "target": "ar", "dialect": "egyptian", "tone": "social_media"}'
```
@@ -154,17 +164,6 @@ Translate text between English and Arabic with dialect awareness. Generates cult
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{"text": "وش رايكم بالمحتوى الجديد؟", "source": "ar", "target": "en", "tone": "formal"}'
-```
-
-
-
- **Input:** "كيف حالك اليوم؟" (MSA)
- **Output:** "شلونك اليوم؟" (Gulf)
-```bash
- curl -X POST https://api.trynawa.com/v1/translate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "كيف حالك اليوم؟", "source": "ar", "target": "ar", "dialect": "gulf", "tone": "casual"}'
```
@@ -173,8 +172,8 @@ Translate text between English and Arabic with dialect awareness. Generates cult
| Status | Type | When |
|--------|------|------|
-| 400 | `invalid_request_error` | Missing `text`, `source`, or `target`. Invalid values. Text too long. |
+| 400 | `invalid_request_error` | Missing `text` or `target`. Source equals target. Text too long. Invalid enum value. |
| 401 | `authentication_error` | Invalid or missing API key |
-| 402 | `insufficient_credits` | No credits remaining |
+| 402 | `permission_error` | No credits remaining |
| 429 | `rate_limit_error` | Rate limit exceeded |
| 500 | `api_error` | Internal or provider error |
diff --git a/api-reference/usage.mdx b/api-reference/usage.mdx
index d4b1b6b..a8a6cff 100644
--- a/api-reference/usage.mdx
+++ b/api-reference/usage.mdx
@@ -1,7 +1,7 @@
---
title: "Usage"
sidebarTitle: "GET /v1/usage"
-description: "Retrieve API usage statistics, credit consumption, and request counts."
+description: "Retrieve API usage statistics, credit consumption, and cache hit rates."
api: "GET https://api.trynawa.com/v1/usage"
---
@@ -13,17 +13,40 @@ Retrieve your API usage statistics including request counts, credit consumption,
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `from` | string | No | Start date (ISO 8601). Default: start of current month. |
-| `to` | string | No | End date (ISO 8601). Default: now. |
-| `group_by` | string | No | Group by: `day`, `week`, `month`, `endpoint`. Default: `day`. |
+| `from` | string | No | Start date (ISO 8601). |
+| `to` | string | No | End date (ISO 8601). |
+| `group_by` | string | No | Group by: `endpoint` or `day`. Default: `endpoint`. |
### Example request
-```bash
+
+
+```bash cURL
curl "https://api.trynawa.com/v1/usage?from=2025-01-01T00:00:00Z&group_by=endpoint" \
-H "Authorization: Bearer nawa_test_sk_xxx"
```
+```python Python
+import requests
+
+response = requests.get(
+ "https://api.trynawa.com/v1/usage",
+ params={"from": "2025-01-01T00:00:00Z", "group_by": "endpoint"},
+ headers={"Authorization": "Bearer nawa_test_sk_xxx"}
+)
+data = response.json()
+```
+
+```typescript TypeScript
+const response = await fetch(
+ 'https://api.trynawa.com/v1/usage?from=2025-01-01T00:00:00Z&group_by=endpoint',
+ { headers: { Authorization: 'Bearer nawa_test_sk_xxx' } }
+)
+const data = await response.json()
+```
+
+
+
## Response
### Success response (200)
@@ -32,53 +55,53 @@ curl "https://api.trynawa.com/v1/usage?from=2025-01-01T00:00:00Z&group_by=endpoi
{
"success": true,
"result": {
- "period": {
- "from": "2025-01-01T00:00:00Z",
- "to": "2025-01-31T23:59:59Z"
+ "usage": {
+ "total_requests": 12450,
+ "total_cost": 68.70,
+ "cache_hits": 2231,
+ "cache_hit_rate": 0.18,
+ "period": {
+ "from": "2025-01-01T00:00:00Z",
+ "to": "2025-01-31T23:59:59Z"
+ }
},
- "total_requests": 12450,
- "total_cost": 68.70,
- "cache_hit_rate": 0.23,
"by_endpoint": [
{
"endpoint": "/v1/classify",
"requests": 8500,
"cost": 51.00,
- "cache_hits": 1955,
- "avg_latency_ms": 820
- },
- {
- "endpoint": "/v1/rubric/classify",
- "requests": 2200,
- "cost": 6.60,
- "cache_hits": 880,
- "avg_latency_ms": 650
+ "cache_hits": 1955
},
{
- "endpoint": "/v1/comments/:id/reply",
- "requests": 1750,
- "cost": 14.00,
- "cache_hits": 0,
- "avg_latency_ms": 1200
+ "endpoint": "/v1/translate",
+ "requests": 1200,
+ "cost": 6.00,
+ "cache_hits": 276
}
- ],
- "balance": {
- "current": 31.30,
- "credits_purchased": 100.00,
- "credits_used": 68.70
- }
+ ]
},
"errors": [],
- "request_id": "req_usg_abc123"
+ "request_id": "req_nw_usg1a2b3c4d5e"
}
```
-### Result fields
+### Usage summary fields
| Field | Type | Description |
|-------|------|-------------|
| `total_requests` | integer | Total API requests in the period |
-| `total_cost` | number | Total credit cost in USD |
-| `cache_hit_rate` | number | Percentage of requests served from cache (0–1) |
-| `by_endpoint` | array | Breakdown by endpoint |
-| `balance` | object | Current balance and credit history |
+| `total_cost` | number | Total cost in USD |
+| `cache_hits` | integer | Number of requests served from cache |
+| `cache_hit_rate` | number | Cache hit rate as a decimal (0-1) |
+| `period` | object | The `from` and `to` dates (null if not specified) |
+
+### Breakdown items
+
+The breakdown key changes based on `group_by`:
+
+| `group_by` | Key | Breakdown fields |
+|------------|-----|-----------------|
+| `endpoint` | `by_endpoint` | `endpoint`, `requests`, `cost`, `cache_hits` |
+| `day` | `by_day` | `date` (YYYY-MM-DD), `requests`, `cost`, `cache_hits` |
+
+Breakdowns are sorted by `requests` descending.
diff --git a/openapi.yaml b/openapi.yaml
index 17706f2..8c59fa5 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -1,10 +1,10 @@
openapi: 3.1.0
info:
title: NAWA API
- version: 1.1.1
+ version: 1.2.0
description: |
Arabic language intelligence API.
- تصنيف ذكي للمحتوى العربي - من الخليج إلى المغرب.
+ تصنيف ذكي للمحتوى العربي - من الخليج إلى المغرب.
NAWA classifies Arabic and English text with dialect detection, sentiment analysis,
intent classification, translation, content moderation, and automated reply generation.
@@ -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 direction. 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
+ description: Force a specific AI provider (for A/B testing)
+ schema:
+ type: string
+ enum: [claude, gemini, allam]
requestBody:
required: true
content:
@@ -70,25 +78,15 @@ paths:
text:
type: string
minLength: 1
- maxLength: 5000
- description: The comment text to classify
+ description: The 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:
+ - "Great video, love the content!"
+ context:
type: object
- description: Arbitrary key-value metadata to attach to the classification
+ description: Optional context for classification (reserved for future use)
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:
@@ -112,26 +108,31 @@ paths:
schema:
$ref: "#/components/schemas/ClassifySuccessResponse"
examples:
- gulf_question:
+ arabic_gulf:
summary: Gulf Arabic question
value:
success: true
result:
- text: "متى الجزء الثاني؟"
- intent: question
- intent_confidence: 0.97
+ id: cls_nw_a1b2c3d4e5f6
+ object: classification
+ intent: [question]
sentiment: neutral
- sentiment_confidence: 0.91
- dialect: gulf
- dialect_confidence: 0.95
- toxicity: none
- toxicity_confidence: 0.99
- categories: ["engagement"]
language: ar
- model: nagl-v1
- cached: false
+ dialect: gulf
+ dialect_confidence: 0.92
+ requires_response: true
+ priority: medium
+ 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_a1b2c3d4e5f67890abcdef12
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -193,10 +194,7 @@ paths:
platform:
type: string
enum: [youtube, instagram, twitter, facebook]
- description: Source platform for social media tone context
- metadata:
- type: object
- description: Arbitrary key-value metadata
+ description: Source platform (validated but reserved for future use)
example:
text: "Hello, how are you?"
source: "en"
@@ -213,8 +211,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:
@@ -243,6 +239,7 @@ paths:
provider: allam
model: sdaia/allam-1-13b-instruct
fallback_used: false
+ tokens_used: null
cached: false
cost_usd: 0.005
credits_used: 5
@@ -296,8 +293,6 @@ paths:
$ref: "#/components/headers/X-Request-Id"
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:
@@ -392,8 +387,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:
@@ -457,17 +450,24 @@ 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.
+ Classify text against a user-defined rubric. Supports multi-label classification
+ and configurable confidence thresholds.
tags: [Classification]
x-nawa-cost: "$0.003"
- x-nawa-cache: true
+ x-nawa-cache: false
x-nawa-free: false
+ parameters:
+ - name: provider
+ in: query
+ required: false
+ schema:
+ type: string
+ enum: [claude, gemini, allam]
requestBody:
required: true
content:
@@ -479,40 +479,23 @@ paths:
text:
type: string
minLength: 1
- maxLength: 5000
- description: The comment text to classify
+ description: 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"
+ - name: "off_topic"
+ multi_label: false
+ confidence_threshold: 0.5
responses:
"200":
description: Rubric classification result
@@ -521,10 +504,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:
@@ -537,25 +516,26 @@ paths:
$ref: "#/components/schemas/RubricSuccessResponse"
examples:
translation_request:
- summary: Gulf Arabic translation request
+ summary: Gulf Arabic classified against rubric
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_a1b2c3d4e5f6
+ object: rubric_classification
+ categories:
+ - name: translation_request
+ confidence: 0.94
+ - name: technical_issue
+ confidence: 0.03
+ multi_label: false
language: ar
- model: nagl-v1
- cached: false
+ provider: allam
+ model: sdaia/allam-1-13b-instruct
+ fallback_used: false
+ cost_usd: 0.003
+ credits_used: 3
errors: []
- request_id: req_rub123abc456
+ request_id: req_nw_rub123abc456
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -567,67 +547,59 @@ paths:
"500":
$ref: "#/components/responses/InternalError"
- /comments/{id}/reply:
+ /comments/reply:
post:
- operationId: generateReply
- summary: Generate reply to comment
+ operationId: commentsReply
+ 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 contextual reply in a single call.
+ Supports tone control and max length configuration.
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]
requestBody:
- required: false
+ required: true
content:
application/json:
schema:
type: object
+ required: [text]
properties:
+ text:
+ type: string
+ minLength: 1
+ description: Comment text to classify and reply to
+ examples:
+ - "هذا المنتج سيء جداً ولا أنصح به"
tone:
type: string
enum: [friendly, professional, casual, formal]
- default: friendly
- description: Reply tone
+ default: professional
max_length:
type: integer
minimum: 1
maximum: 2000
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)."
example:
- tone: "friendly"
- context: "Tech review channel focused on smartphones"
+ text: "هذا المنتج سيء جداً ولا أنصح به"
+ tone: "professional"
+ max_length: 500
responses:
"200":
- description: Generated reply
+ description: Classification and reply result
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:
@@ -639,19 +611,28 @@ paths:
schema:
$ref: "#/components/schemas/ReplySuccessResponse"
examples:
- gulf_reply:
- summary: Friendly reply to Gulf Arabic question
+ arabic_reply:
+ summary: Reply to Arabic complaint
value:
success: true
result:
- comment_id: cmt_abc123
- reply_text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔"
- reply_dialect: gulf
- tone: friendly
- original_intent: question
- original_dialect: gulf
+ id: rpl_nw_a1b2c3d4e5f6
+ object: comment_reply
+ classification:
+ intent: [complaint]
+ sentiment: negative
+ priority: high
+ requires_response: true
+ reply:
+ text: "نعتذر عن التجربة السيئة. نتمنى تتواصل معنا عشان نقدر نساعدك."
+ direction: rtl
+ tone: professional
+ provider: allam
+ model: sdaia/allam-1-13b-instruct
+ cost_usd: 0.008
+ credits_used: 8
errors: []
- request_id: req_rep789xyz012
+ request_id: req_nw_rpl1a2b3c4d5e
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -668,8 +649,8 @@ paths:
operationId: submitFeedback
summary: Submit classification feedback
description: |
- Submit feedback on a classification result to improve NAWA's accuracy.
- Free for all tiers. Used for RLHF-based model improvement.
+ Submit feedback on a classification result. Free for all tiers.
+ Used for RLHF-based model improvement.
tags: [Feedback]
x-nawa-cost: "Free"
x-nawa-cache: false
@@ -681,13 +662,14 @@ 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_a1b2c3d4e5f6"
+ rating: "incorrect"
+ corrected_intent: ["question"]
+ corrected_sentiment: "neutral"
+ comment: "This was a question, not a complaint"
responses:
"200":
- description: Feedback accepted
+ description: Feedback acknowledged
headers:
X-Request-Id:
$ref: "#/components/headers/X-Request-Id"
@@ -701,11 +683,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_a1b2c3d4e5f6
+ object: feedback
+ classification_id: cls_nw_a1b2c3d4e5f6
+ rating: incorrect
+ acknowledged: true
errors: []
- request_id: req_fb_abc123
+ request_id: req_nw_fb1a2b3c4d5e
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -720,18 +704,13 @@ paths:
operationId: listComments
summary: List classified comments
description: |
- Retrieve classified comments with filtering and pagination. Free endpoint.
+ Retrieve classified comments with filtering and offset-based pagination.
+ Free endpoint.
tags: [Comments]
x-nawa-cost: "Free"
x-nawa-cache: false
x-nawa-free: true
parameters:
- - name: channel_id
- in: query
- required: false
- schema:
- type: string
- description: Filter by channel ID
- name: platform
in: query
required: false
@@ -760,27 +739,6 @@ paths:
type: string
enum: [gulf, egyptian, levantine, msa]
description: Filter by dialect
- - name: toxicity
- in: query
- required: false
- schema:
- type: string
- enum: [none, mild, moderate, severe]
- description: Filter by toxicity
- - name: from
- in: query
- required: false
- schema:
- type: string
- format: date-time
- description: Start date (ISO 8601)
- - name: to
- in: query
- required: false
- schema:
- type: string
- format: date-time
- description: End date (ISO 8601)
- name: limit
in: query
required: false
@@ -789,13 +747,15 @@ paths:
minimum: 1
maximum: 100
default: 25
- description: Results per page
- - name: cursor
+ description: Results per page (1-100)
+ - name: offset
in: query
required: false
schema:
- type: string
- description: Pagination cursor from previous response
+ type: integer
+ minimum: 0
+ default: 0
+ description: Number of results to skip
responses:
"200":
description: List of classified comments
@@ -814,20 +774,20 @@ paths:
result:
comments:
- id: cmt_abc123
+ request_id: req_nw_a1b2c3d4e5f6
text: "متى الجزء الثاني؟"
platform: youtube
intent: question
sentiment: neutral
dialect: gulf
- toxicity: none
created_at: "2025-01-15T10:30:00Z"
- channel_id: ch_123
pagination:
+ total: 1523
+ limit: 25
+ offset: 0
has_more: true
- next_cursor: cur_def456
- total_count: 1523
errors: []
- request_id: req_lst_abc123
+ request_id: req_nw_lst1a2b3c4d5e
"401":
$ref: "#/components/responses/Unauthorized"
"429":
@@ -840,26 +800,13 @@ paths:
operationId: getAnalytics
summary: Get analytics
description: |
- Retrieve aggregated analytics on comment trends, sentiment shifts, and
- engagement patterns. Free endpoint.
+ Retrieve aggregated analytics on API usage patterns including request counts,
+ costs, cache performance, and latency. Free endpoint.
tags: [Analytics]
x-nawa-cost: "Free"
x-nawa-cache: false
x-nawa-free: true
parameters:
- - name: channel_id
- in: query
- required: false
- schema:
- type: string
- description: Filter by channel ID
- - name: platform
- in: query
- required: false
- schema:
- type: string
- enum: [youtube, instagram, twitter, facebook]
- description: Filter by platform
- name: from
in: query
required: false
@@ -879,9 +826,9 @@ paths:
required: false
schema:
type: string
- enum: [day, week, month]
- default: day
- description: Group results by time period
+ enum: [endpoint, day, provider]
+ default: endpoint
+ description: Group breakdown results by endpoint, day, or provider
responses:
"200":
description: Analytics data
@@ -893,45 +840,40 @@ paths:
schema:
$ref: "#/components/schemas/AnalyticsSuccessResponse"
examples:
- monthly:
- summary: Monthly YouTube analytics
+ by_endpoint:
+ summary: Analytics grouped by endpoint
value:
success: true
result:
- period:
- from: "2025-01-01T00:00:00Z"
- to: "2025-01-31T23:59:59Z"
- total_comments: 4521
- summary:
- intent:
- question: 1205
- praise: 1890
- complaint: 678
- suggestion: 412
- spam: 198
- other: 138
- sentiment:
- positive: 2100
- negative: 890
- neutral: 1231
- mixed: 300
- dialect:
- gulf: 2015
- egyptian: 1302
- levantine: 789
- msa: 415
- toxicity:
- none: 3950
- mild: 320
- moderate: 180
- severe: 71
- trends:
- - period: "2025-01-06/2025-01-12"
- total: 1123
- sentiment_score: 0.72
- top_intent: praise
+ analytics:
+ total_requests: 12450
+ total_cost: 68.70
+ cache_hits: 2231
+ cache_hit_rate: 0.18
+ avg_latency_ms: 920
+ success_rate: 0.99
+ error_count: 125
+ endpoint_distribution:
+ "/v1/classify": 8500
+ "/v1/translate": 1200
+ "/v1/comments/reply": 1750
+ provider_distribution:
+ allam: 9200
+ claude: 3250
+ period:
+ from: "2025-01-01T00:00:00Z"
+ to: "2025-01-31T23:59:59Z"
+ by_endpoint:
+ - endpoint: "/v1/classify"
+ requests: 8500
+ cost: 51.00
+ avg_latency_ms: 820
+ - endpoint: "/v1/translate"
+ requests: 1200
+ cost: 6.00
+ avg_latency_ms: 1100
errors: []
- request_id: req_ana_abc123
+ request_id: req_nw_ana1a2b3c4d5e
"401":
$ref: "#/components/responses/Unauthorized"
"429":
@@ -945,7 +887,7 @@ paths:
summary: Health check
description: |
Check the operational status of the NAWA API and its dependencies.
- No authentication required.
+ No authentication required. Returns a flat JSON object (not the standard envelope).
tags: [System]
x-nawa-cost: "Free"
x-nawa-cache: false
@@ -957,29 +899,30 @@ paths:
content:
application/json:
schema:
- $ref: "#/components/schemas/HealthSuccessResponse"
+ $ref: "#/components/schemas/HealthResponse"
examples:
healthy:
summary: All services operational
value:
- success: true
- result:
- status: healthy
- version: "1.0.0"
- services:
- api: operational
- classification: operational
- database: operational
- cache: operational
- timestamp: "2025-01-15T12:00:00Z"
- errors: []
- request_id: req_hlt_abc123
+ status: healthy
+ version: v1
+ services:
+ database:
+ status: healthy
+ latency_ms: 12
+ redis:
+ status: healthy
+ latency_ms: 3
+ nagl:
+ status: healthy
+ timestamp: "2025-01-15T12:00:00Z"
+ latency_ms: 15
"503":
description: One or more services degraded
content:
application/json:
schema:
- $ref: "#/components/schemas/HealthSuccessResponse"
+ $ref: "#/components/schemas/HealthResponse"
/usage:
get:
@@ -999,22 +942,22 @@ paths:
schema:
type: string
format: date-time
- description: Start date (ISO 8601). Default: start of current month.
+ description: Start date (ISO 8601)
- name: to
in: query
required: false
schema:
type: string
format: date-time
- description: End date (ISO 8601). Default: now.
+ description: End date (ISO 8601)
- name: group_by
in: query
required: false
schema:
type: string
- enum: [day, week, month, endpoint]
- default: day
- description: Group results by time period or endpoint
+ enum: [endpoint, day]
+ default: endpoint
+ description: Group results by endpoint or day
responses:
"200":
description: Usage statistics
@@ -1031,34 +974,25 @@ paths:
value:
success: true
result:
- period:
- from: "2025-01-01T00:00:00Z"
- to: "2025-01-31T23:59:59Z"
- total_requests: 12450
- total_cost: 68.70
- cache_hit_rate: 0.23
+ usage:
+ total_requests: 12450
+ total_cost: 68.70
+ cache_hits: 2231
+ cache_hit_rate: 0.18
+ period:
+ from: "2025-01-01T00:00:00Z"
+ to: "2025-01-31T23:59:59Z"
by_endpoint:
- endpoint: "/v1/classify"
requests: 8500
cost: 51.00
cache_hits: 1955
- avg_latency_ms: 820
- endpoint: "/v1/translate"
requests: 1200
cost: 6.00
cache_hits: 276
- avg_latency_ms: 1100
- - endpoint: "/v1/comments/:id/reply"
- requests: 1750
- cost: 14.00
- cache_hits: 0
- avg_latency_ms: 1200
- balance:
- current: 31.30
- credits_purchased: 100.00
- credits_used: 68.70
errors: []
- request_id: req_usg_abc123
+ request_id: req_nw_usg1a2b3c4d5e
"401":
$ref: "#/components/responses/Unauthorized"
"429":
@@ -1163,14 +1097,14 @@ components:
headers:
X-Request-Id:
- description: Unique request identifier
+ description: Unique request identifier (req_nw_xxx format)
schema:
type: string
X-NAWA-Provider:
description: AI provider used for this request
schema:
type: string
- enum: [allam, claude]
+ enum: [allam, claude, gemini]
X-NAWA-Cache:
description: Whether the response was served from semantic cache
schema:
@@ -1324,7 +1258,7 @@ components:
items:
$ref: "#/components/schemas/CommentItem"
pagination:
- $ref: "#/components/schemas/Pagination"
+ $ref: "#/components/schemas/OffsetPagination"
errors:
type: array
maxItems: 0
@@ -1348,22 +1282,6 @@ components:
request_id:
type: string
- HealthSuccessResponse:
- type: object
- required: [success, result, errors, request_id]
- properties:
- success:
- type: boolean
- const: true
- result:
- $ref: "#/components/schemas/HealthResult"
- errors:
- type: array
- maxItems: 0
- items: {}
- request_id:
- type: string
-
UsageSuccessResponse:
type: object
required: [success, result, errors, request_id]
@@ -1424,51 +1342,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 (cls_nw_xxx format)
+ examples: [cls_nw_a1b2c3d4e5f6]
+ 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 (array)
+ examples: [[question], [praise, suggestion]]
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
+ nullable: true
+ description: Arabic dialect. Null for English text.
enum: [gulf, egyptian, levantine, msa]
dialect_confidence:
type: number
+ nullable: true
minimum: 0
maximum: 1
- toxicity:
+ requires_response:
+ type: boolean
+ 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
+ properties:
+ text:
+ type: string
+ direction:
+ type: string
+ enum: [ltr, rtl]
+ 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
+ description: Always null in the current version
+ cost_usd:
+ type: number
+ description: Cost in USD
+ credits_used:
+ type: integer
+ description: Credits deducted (6 per request)
TranslateResult:
type: object
@@ -1507,12 +1441,14 @@ components:
enum: [formal, casual, social_media]
provider:
type: string
- description: "AI provider used (allam or claude)"
model:
type: string
fallback_used:
type: boolean
- description: Whether the fallback provider was used
+ tokens_used:
+ type: integer
+ nullable: true
+ description: Always null in the current version
cached:
type: boolean
cost_usd:
@@ -1532,36 +1468,29 @@ components:
const: detection
text:
type: string
- description: The original input text
language:
type: string
enum: [ar, en, mixed]
- description: Detected language
dialect:
type: string
nullable: true
enum: [gulf, egyptian, levantine, msa]
- description: Detected Arabic dialect. Null if not Arabic.
dialect_confidence:
type: number
nullable: true
minimum: 0
maximum: 1
- description: Confidence score for dialect detection. Null if not Arabic.
script:
type: string
enum: [arabic, latin, mixed]
- description: Script type
text_direction:
type: string
enum: [rtl, ltr]
- description: Text direction
word_count:
type: integer
- description: Number of words in the input text
fallback_used:
type: boolean
- description: Always false for this endpoint (local detection only)
+ description: Always false (local detection only)
cost_usd:
type: number
credits_used:
@@ -1581,10 +1510,8 @@ components:
const: moderation
text:
type: string
- description: The original input text
is_safe:
type: boolean
- description: Whether the content passed moderation
verdict:
type: string
enum: [safe, flagged]
@@ -1592,7 +1519,6 @@ components:
type: array
items:
type: string
- description: List of triggered category names
severity:
type: string
enum: [none, low, medium, high]
@@ -1615,19 +1541,16 @@ components:
type: array
items:
type: string
- description: Specific problematic words/phrases found
cleaned_text:
type: string
nullable: true
- description: Text with flagged terms replaced by ***. Null if safe.
+ description: Text with flagged terms replaced. Null if safe.
model:
type: string
provider:
type: string
- description: "AI provider used (allam or claude)"
fallback_used:
type: boolean
- description: Whether the fallback provider was used
cost_usd:
type: number
credits_used:
@@ -1687,97 +1610,128 @@ components:
RubricResult:
type: object
+ required: [id, object, categories, multi_label, language, provider, model, cost_usd, credits_used]
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:
+ examples: [rcl_nw_a1b2c3d4e5f6]
+ 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
+ required: [name, confidence]
+ properties:
+ name:
+ type: string
+ confidence:
+ type: number
+ minimum: 0
+ maximum: 1
+ multi_label:
+ type: boolean
language:
type: string
- enum: [ar, en, mixed]
+ provider:
+ type: string
model:
type: string
- cached:
+ fallback_used:
type: boolean
+ cost_usd:
+ type: number
+ credits_used:
+ type: integer
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:
+ examples: [rpl_nw_a1b2c3d4e5f6]
+ 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: array
+ items:
+ type: string
+ sentiment:
+ type: string
+ priority:
+ type: string
+ requires_response:
+ type: boolean
+ reply:
+ type: object
+ properties:
+ text:
+ type: string
+ direction:
+ type: string
+ enum: [ltr, rtl]
+ tone:
+ type: string
+ provider:
type: string
- enum: [question, complaint, praise, suggestion, spam, other]
- description: Detected intent of the original comment
- original_dialect:
+ 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 cls_nw_xxx ID from a classify response
+ rating:
type: string
- enum: [intent, sentiment, dialect, toxicity, category]
- description: The field to correct
- expected_value:
+ enum: [correct, incorrect, partial]
+ corrected_intent:
+ type: array
+ items:
+ type: string
+ corrected_sentiment:
type: string
- description: The correct value the model should have returned
+ enum: [positive, negative, neutral, mixed]
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:
+ examples: [fb_nw_a1b2c3d4e5f6]
+ object:
+ type: string
+ const: feedback
+ classification_id:
type: string
- enum: [accepted]
- message:
+ rating:
type: string
+ acknowledged:
+ type: boolean
+ const: true
CommentItem:
type: object
properties:
id:
type: string
+ request_id:
+ type: string
text:
type: string
platform:
@@ -1785,82 +1739,103 @@ components:
enum: [youtube, instagram, twitter, facebook]
intent:
type: string
- enum: [question, complaint, praise, suggestion, spam, other]
sentiment:
type: string
enum: [positive, negative, neutral, mixed]
dialect:
type: string
enum: [gulf, egyptian, levantine, msa]
- toxicity:
- type: string
- enum: [none, mild, moderate, severe]
created_at:
type: string
format: date-time
- channel_id:
- type: string
- Pagination:
+ OffsetPagination:
type: object
properties:
+ total:
+ type: integer
+ limit:
+ type: integer
+ offset:
+ type: integer
has_more:
type: boolean
- next_cursor:
- type: string
- nullable: true
- total_count:
- type: integer
AnalyticsResult:
type: object
properties:
- period:
+ analytics:
type: object
properties:
- from:
- type: string
- format: date-time
- to:
- type: string
- format: date-time
- total_comments:
- type: integer
- summary:
- type: object
- properties:
- intent:
+ total_requests:
+ type: integer
+ total_cost:
+ type: number
+ cache_hits:
+ type: integer
+ cache_hit_rate:
+ type: number
+ minimum: 0
+ maximum: 1
+ avg_latency_ms:
+ type: number
+ success_rate:
+ type: number
+ minimum: 0
+ maximum: 1
+ error_count:
+ type: integer
+ endpoint_distribution:
type: object
additionalProperties:
type: integer
- sentiment:
+ provider_distribution:
type: object
additionalProperties:
type: integer
- dialect:
+ period:
type: object
- additionalProperties:
- type: integer
- toxicity:
+ properties:
+ from:
+ type: string
+ format: date-time
+ nullable: true
+ to:
+ type: string
+ format: date-time
+ nullable: true
+
+ UsageResult:
+ type: object
+ properties:
+ usage:
+ type: object
+ properties:
+ total_requests:
+ type: integer
+ total_cost:
+ type: number
+ cache_hits:
+ type: integer
+ cache_hit_rate:
+ type: number
+ minimum: 0
+ maximum: 1
+ period:
type: object
- additionalProperties:
- type: integer
- trends:
- type: array
- items:
- type: object
- properties:
- period:
- type: string
- total:
- type: integer
- sentiment_score:
- type: number
- top_intent:
- type: string
+ properties:
+ from:
+ type: string
+ format: date-time
+ nullable: true
+ to:
+ type: string
+ format: date-time
+ nullable: true
- HealthResult:
+ HealthResponse:
type: object
+ required: [status, version, services, timestamp]
properties:
status:
type: string
@@ -1870,67 +1845,58 @@ components:
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]
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [healthy, degraded, down]
+ latency_ms:
+ type: integer
+ redis:
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [healthy, degraded, down]
+ latency_ms:
+ type: integer
+ nagl:
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [healthy, down]
timestamp:
type: string
format: date-time
+ latency_ms:
+ type: integer
- UsageResult:
+ Rubric:
type: object
+ required: [categories]
properties:
- period:
- type: object
- properties:
- from:
- type: string
- format: date-time
- to:
- type: string
- format: date-time
- total_requests:
- type: integer
- total_cost:
- type: number
- description: Total cost in USD
- cache_hit_rate:
- type: number
- minimum: 0
- maximum: 1
- by_endpoint:
+ categories:
type: array
+ minItems: 1
+ maxItems: 20
items:
type: object
+ required: [name]
properties:
- endpoint:
+ name:
type: string
- requests:
- type: integer
- cost:
- type: number
- cache_hits:
- type: integer
- avg_latency_ms:
- type: integer
- balance:
- type: object
- properties:
- current:
- type: number
- credits_purchased:
- type: number
- credits_used:
- type: number
+ description:
+ type: string
+ multi_label:
+ type: boolean
+ default: false
+ confidence_threshold:
+ type: number
+ minimum: 0
+ maximum: 1
+ default: 0.5
WebhookEndpoint:
type: object