diff --git a/api-reference/analytics.mdx b/api-reference/analytics.mdx
index c2879c7..65008d1 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 API analytics including request counts, costs, and cache performance."
api: "GET https://api.trynawa.com/v1/analytics"
---
-Retrieve aggregated analytics across your classified comments. This endpoint is **free**.
+Retrieve aggregated API analytics including request counts, costs, cache hit rates, and latency. This endpoint is **free**.
## Request
@@ -13,19 +13,45 @@ 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 results by: `endpoint` (default), `day`, or `provider`. |
### 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"
```
+```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.analytics({
+ from: '2025-01-01T00:00:00Z',
+ to: '2025-01-31T23:59:59Z',
+ groupBy: 'endpoint'
+})
+```
+
+```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.analytics(
+ from_date="2025-01-01T00:00:00Z",
+ to_date="2025-01-31T23:59:59Z",
+ group_by="endpoint"
+)
+```
+
+
+
## Response
### Success response (200)
@@ -34,49 +60,59 @@ 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": 2870,
+ "cache_hit_rate": 0.23,
+ "avg_latency_ms": 820,
+ "success_rate": 0.99,
+ "error_count": 12,
+ "endpoint_distribution": {
+ "/v1/classify": 8500,
+ "/v1/translate": 1200,
+ "/v1/comments/reply": 1750
},
- "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_ana_abc123"
}
```
+
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `analytics.total_requests` | integer | Total API requests in the period |
+| `analytics.total_cost` | number | Total cost in USD |
+| `analytics.cache_hits` | integer | Number of cache hits |
+| `analytics.cache_hit_rate` | number | Cache hit ratio (0-1) |
+| `analytics.avg_latency_ms` | number | Average response latency in milliseconds |
+| `analytics.success_rate` | number | Success ratio (0-1) |
+| `analytics.error_count` | integer | Total error responses |
+| `analytics.endpoint_distribution` | object | Request counts by endpoint |
+| `analytics.provider_distribution` | object | Request counts by AI provider |
+| `analytics.period` | object | Date range for the analytics |
diff --git a/api-reference/classify.mdx b/api-reference/classify.mdx
index ac3d730..b4ceaa7 100644
--- a/api-reference/classify.mdx
+++ b/api-reference/classify.mdx
@@ -1,16 +1,16 @@
---
-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 an Arabic or English comment by intent, sentiment, dialect, and priority in a single API call."
api: "POST https://api.trynawa.com/v1/classify"
---
-Classify any comment with a single request. Returns intent, sentiment, dialect, and toxicity analysis. Arabic comments are routed to HUMAIN's ALLaM model for dialect detection. English comments are routed to Claude for high-accuracy classification.
+Classify any comment with a single request. Returns intent, sentiment, dialect, priority, and a suggested reply direction. Arabic comments are routed to HUMAIN's ALLaM model for dialect detection. English comments are routed to Claude for high-accuracy classification.
## Request
- Cost: **$0.006** per request. Semantic cache hits are free (`X-NAWA-Cache: HIT`).
+ Cost: **$0.006** per request (6 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
### Headers
@@ -25,9 +25,13 @@ Classify any comment with a single request. Returns intent, sentiment, dialect,
| 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. |
+| `context` | object | No | Optional context for classification. |
+
+### Query parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `provider` | string | No | Force a specific AI provider: `claude`, `gemini`, or `allam`. Useful for A/B testing. |
### Example request
@@ -38,9 +42,7 @@ 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": "متى الجزء الثاني؟"
}'
```
@@ -50,9 +52,7 @@ import { Nawa } from '@nawalabs/sdk'
const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
const { data, error } = await nawa.classify({
- text: 'متى الجزء الثاني؟',
- platform: 'youtube',
- channelId: 'ch_123'
+ text: 'متى الجزء الثاني؟'
})
```
@@ -62,9 +62,7 @@ from nawa import Nawa
nawa = Nawa(api_key="your_api_key")
result = nawa.classify(
- text="متى الجزء الثاني؟",
- platform="youtube",
- channel_id="ch_123"
+ text="متى الجزء الثاني؟"
)
```
@@ -78,22 +76,28 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "متى الجزء الثاني؟",
- "intent": "question",
- "intent_confidence": 0.97,
+ "id": "cls_a1b2c3d4e5f6",
+ "object": "classification",
+ "intent": ["question"],
"sentiment": "neutral",
- "sentiment_confidence": 0.91,
+ "language": "ar",
"dialect": "gulf",
"dialect_confidence": 0.95,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "requires_response": true,
+ "priority": "high",
+ "suggested_reply": {
+ "text": "سؤال من متابع عن موعد الجزء الثاني",
+ "direction": "rtl"
+ },
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "tokens_used": null,
+ "cost_usd": 0.006,
+ "credits_used": 6
},
"errors": [],
- "request_id": "req_abc123def456"
+ "request_id": "req_nw_a1b2c3d4e5f67890"
}
```
@@ -106,32 +110,35 @@ result = nawa.classify(
| `X-RateLimit-Remaining` | Requests remaining in current window |
| `X-RateLimit-Reset` | Window reset time (RFC 3339) |
| `X-NAWA-Balance` | Current credit balance in USD |
-| `X-NAWA-Balance-Warning` | `low_balance` when below $5 |
| `X-NAWA-Cache` | `HIT` if served from semantic cache (no cost) |
+| `X-NAWA-Provider` | AI provider used for this request |
### Result fields
| Field | Type | Description |
|-------|------|-------------|
-| `text` | string | The original input text |
-| `intent` | string | `question`, `complaint`, `praise`, `suggestion`, `spam`, `other` |
-| `intent_confidence` | number | Confidence score (0–1) |
+| `id` | string | Classification ID in `cls_xxx` format |
+| `object` | string | Always `classification` |
+| `intent` | string[] | Detected intents (e.g. `question`, `complaint`, `praise`, `spam`) |
| `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 |
+| `language` | string | Detected language: `ar`, `en`, or `mixed` |
+| `dialect` | string \| null | `gulf`, `egyptian`, `levantine`, `msa`. Null for English text. |
+| `dialect_confidence` | number \| null | Confidence score (0-1). Null for English text. |
+| `requires_response` | boolean | Whether the comment needs a reply |
+| `priority` | string | `high`, `medium`, or `low` |
+| `suggested_reply` | object \| null | Suggested reply text and direction |
+| `provider` | string | AI provider used (`allam` or `claude`) |
+| `model` | string | Model identifier |
+| `fallback_used` | boolean | Whether the fallback provider was used |
+| `tokens_used` | integer \| null | Token count (null when not available) |
+| `cost_usd` | number | Cost in USD for this request |
+| `credits_used` | integer | Credits deducted from your balance |
### Error responses
| Status | Type | When |
|--------|------|------|
-| 400 | `invalid_request_error` | Missing `text`, invalid `platform`, text too long |
+| 400 | `invalid_request_error` | Missing `text` or text too long |
| 401 | `authentication_error` | Invalid or missing API key |
| 402 | `insufficient_credits` | No credits remaining |
| 429 | `rate_limit_error` | Rate limit exceeded |
@@ -140,12 +147,12 @@ result = nawa.classify(
### More examples
-
+
```bash
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,32 +160,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_en_praise_001",
+ "object": "classification",
+ "intent": ["praise"],
"sentiment": "positive",
- "sentiment_confidence": 0.98,
+ "language": "en",
"dialect": null,
"dialect_confidence": null,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "en",
- "model": "claude-v1",
- "cached": false
+ "requires_response": false,
+ "priority": "low",
+ "suggested_reply": {
+ "text": "Positive viewer expressing strong appreciation for content quality",
+ "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_praise_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": "The audio quality is terrible in this one. Can barely hear anything after the 5 minute mark.", "platform": "youtube"}'
+ -d '{"text": "The audio quality is terrible in this one. Can barely hear anything after the 5 minute mark."}'
```
Response:
@@ -186,65 +199,38 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "The audio quality is terrible in this one. Can barely hear anything after the 5 minute mark.",
- "intent": "complaint",
- "intent_confidence": 0.94,
+ "id": "cls_en_complaint_001",
+ "object": "classification",
+ "intent": ["complaint"],
"sentiment": "negative",
- "sentiment_confidence": 0.96,
- "dialect": null,
- "dialect_confidence": null,
- "toxicity": "none",
- "toxicity_confidence": 0.97,
- "categories": ["feedback"],
"language": "en",
- "model": "claude-v1",
- "cached": false
- },
- "errors": [],
- "request_id": "req_en_complaint_001"
- }
- ```
-
-
-
- ```bash
- curl -X POST https://api.trynawa.com/v1/classify \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "What camera and lens setup are you using for these shots?", "platform": "youtube"}'
- ```
-
- Response:
- ```json
- {
- "success": true,
- "result": {
- "text": "What camera and lens setup are you using for these shots?",
- "intent": "question",
- "intent_confidence": 0.97,
- "sentiment": "neutral",
- "sentiment_confidence": 0.92,
"dialect": null,
"dialect_confidence": null,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "en",
- "model": "claude-v1",
- "cached": false
+ "requires_response": true,
+ "priority": "high",
+ "suggested_reply": {
+ "text": "Viewer reporting audio issue starting at 5-minute mark",
+ "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_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": "ما شاء الله عليك، محتوى رهيب!", "platform": "youtube"}'
+ -d '{"text": "ما شاء الله عليك، محتوى رهيب!"}'
```
Response:
@@ -252,32 +238,38 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "ما شاء الله عليك، محتوى رهيب!",
- "intent": "praise",
- "intent_confidence": 0.98,
+ "id": "cls_ghi789jkl012",
+ "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_ghi789jkl012"
}
```
-
+
```bash
curl -X POST https://api.trynawa.com/v1/classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
- -d '{"text": "الصوت وحش أوي في الفيديو ده", "platform": "youtube"}'
+ -d '{"text": "الصوت وحش أوي في الفيديو ده"}'
```
Response:
@@ -285,32 +277,38 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "الصوت وحش أوي في الفيديو ده",
- "intent": "complaint",
- "intent_confidence": 0.94,
+ "id": "cls_mno345pqr678",
+ "object": "classification",
+ "intent": ["complaint"],
"sentiment": "negative",
- "sentiment_confidence": 0.96,
+ "language": "ar",
"dialect": "egyptian",
"dialect_confidence": 0.97,
- "toxicity": "none",
- "toxicity_confidence": 0.95,
- "categories": ["feedback"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "requires_response": true,
+ "priority": "high",
+ "suggested_reply": {
+ "text": "شكوى من جودة الصوت في الفيديو",
+ "direction": "rtl"
+ },
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "tokens_used": null,
+ "cost_usd": 0.006,
+ "credits_used": 6
},
"errors": [],
- "request_id": "req_mno345pqr678"
+ "request_id": "req_nw_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"}'
+ -d '{"text": "لو سمحت حاول تحكي عن المطاعم بلبنان"}'
```
Response:
@@ -318,22 +316,28 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "لو سمحت حاول تحكي عن المطاعم بلبنان",
- "intent": "suggestion",
- "intent_confidence": 0.92,
+ "id": "cls_stu901vwx234",
+ "object": "classification",
+ "intent": ["suggestion"],
"sentiment": "neutral",
- "sentiment_confidence": 0.88,
+ "language": "ar",
"dialect": "levantine",
"dialect_confidence": 0.96,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "requires_response": false,
+ "priority": "medium",
+ "suggested_reply": {
+ "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_stu901vwx234"
}
```
diff --git a/api-reference/comments-list.mdx b/api-reference/comments-list.mdx
index 1e18d08..2cd6fbc 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."
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. Results are scoped to the authenticated API key. This endpoint is **free**.
## Request
@@ -13,49 +13,46 @@ 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"
```
```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',
- limit: 10
+ limit: 10,
+ offset: 0
})
-
-// Auto-pagination
-for await (const comment of nawa.comments.list({ platform: 'youtube' })) {
- console.log(comment.text)
-}
```
```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
result = nawa.comments.list(
platform="youtube",
intent="question",
- limit=10
+ limit=10,
+ offset=0
)
-
-# Auto-pagination
-for comment in nawa.comments.list(platform="youtube"):
- print(comment.text)
```
@@ -70,24 +67,41 @@ for comment in nawa.comments.list(platform="youtube"):
"result": {
"comments": [
{
- "id": "cmt_abc123",
+ "id": "cls_a1b2c3d4e5f6",
+ "request_id": "req_nw_abc123",
"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_lst_abc123"
}
```
+
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `comments[].id` | string | Classification ID |
+| `comments[].request_id` | string | Original request ID |
+| `comments[].text` | string | The comment text |
+| `comments[].platform` | string | Source platform |
+| `comments[].intent` | string | Detected intent |
+| `comments[].sentiment` | string | Detected sentiment |
+| `comments[].dialect` | string \| null | Detected dialect |
+| `comments[].created_at` | string | Classification timestamp (ISO 8601) |
+| `pagination.total` | integer | Total matching comments |
+| `pagination.limit` | integer | Current page size |
+| `pagination.offset` | integer | Current offset |
+| `pagination.has_more` | boolean | Whether more results exist |
diff --git a/api-reference/comments-reply.mdx b/api-reference/comments-reply.mdx
index d6321d7..aacb7ec 100644
--- a/api-reference/comments-reply.mdx
+++ b/api-reference/comments-reply.mdx
@@ -1,59 +1,81 @@
---
-title: "Reply to Comment"
-sidebarTitle: "POST /v1/comments/:id/reply"
-description: "Generate a context-aware reply to a comment in Arabic or English."
-api: "POST https://api.trynawa.com/v1/comments/{id}/reply"
+title: "Reply to comment"
+sidebarTitle: "POST /v1/comments/reply"
+description: "Classify a comment and generate a contextual reply in a single 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 an AI-powered reply in one request. For Arabic comments, replies match the detected dialect. For English comments, replies are natural and platform-appropriate.
- Cost: **$0.008** per request (8 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
+ Cost: **$0.008** per request (8 credits).
## Request
-### Path parameters
+### Body parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `id` | string | Yes | The comment ID to reply to. |
+| `text` | string | Yes | The comment text to classify and reply to. |
+| `tone` | string | No | Reply tone: `friendly`, `professional` (default), `casual`, `formal`. |
+| `max_length` | integer | No | Maximum reply length in characters (1-2000). Default: 500. |
+| `context` | object | No | Optional context object. |
+| `context.platform` | string | No | Platform context (e.g. `youtube`, `instagram`). |
+| `context.brand_voice` | string | No | Brand voice description to guide reply style. |
-### Body parameters
+### Query 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). |
+| `provider` | string | No | Force a specific AI provider: `claude`, `gemini`, or `allam`. |
### Example request
```bash cURL
-curl -X POST https://api.trynawa.com/v1/comments/cmt_abc123/reply \
+curl -X POST https://api.trynawa.com/v1/comments/reply \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
+ "text": "متى الجزء الثاني؟",
"tone": "friendly",
- "context": "Tech review channel focused on smartphones"
+ "max_length": 500,
+ "context": {
+ "platform": "youtube",
+ "brand_voice": "Tech review channel focused on smartphones"
+ }
}'
```
```typescript TypeScript
-const { data, error } = await nawa.comments.reply('cmt_abc123', {
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.comments.reply({
+ text: 'متى الجزء الثاني؟',
tone: 'friendly',
- context: 'Tech review channel focused on smartphones'
+ context: {
+ platform: 'youtube',
+ brand_voice: 'Tech review channel focused on smartphones'
+ }
})
```
```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
result = nawa.comments.reply(
- comment_id="cmt_abc123",
+ text="متى الجزء الثاني؟",
tone="friendly",
- context="Tech review channel focused on smartphones"
+ context={
+ "platform": "youtube",
+ "brand_voice": "Tech review channel focused on smartphones"
+ }
)
```
@@ -67,15 +89,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": ["question"],
+ "sentiment": "neutral",
+ "priority": "high",
+ "requires_response": true
+ },
+ "reply": {
+ "text": "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك",
+ "direction": "rtl",
+ "tone": "friendly"
+ },
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "cost_usd": 0.008,
+ "credits_used": 8
},
"errors": [],
- "request_id": "req_rep789xyz012"
+ "request_id": "req_nw_rep789xyz012"
}
```
@@ -83,9 +116,16 @@ 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_xxx` format |
+| `object` | string | Always `comment_reply` |
+| `classification.intent` | string[] | Detected intents of the original comment |
+| `classification.sentiment` | string | Detected sentiment |
+| `classification.priority` | string | Priority level (`high`, `medium`, `low`) |
+| `classification.requires_response` | boolean | Whether the comment needs a reply |
+| `reply.text` | string | The generated reply text |
+| `reply.direction` | string | Text direction: `rtl` if the reply contains Arabic, otherwise `ltr` |
+| `reply.tone` | string | The tone used for the reply |
+| `provider` | string | AI provider used |
+| `model` | string | Model identifier |
+| `cost_usd` | number | Cost in USD for this request |
+| `credits_used` | integer | Credits deducted |
diff --git a/api-reference/feedback.mdx b/api-reference/feedback.mdx
index 4cddf8b..f6dba17 100644
--- a/api-reference/feedback.mdx
+++ b/api-reference/feedback.mdx
@@ -1,5 +1,5 @@
---
-title: "Submit Feedback"
+title: "Submit feedback"
sidebarTitle: "POST /v1/feedback"
description: "Submit RLHF feedback to improve classification accuracy over time."
api: "POST https://api.trynawa.com/v1/feedback"
@@ -21,9 +21,10 @@ 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. |
+| `classification_id` | string | Yes | The classification ID (`cls_xxx`) from the original classify response. |
+| `rating` | string | Yes | Whether the classification was `correct`, `incorrect`, or `partial`. |
+| `corrected_intent` | string[] | No | The correct intent(s) if the classification was wrong. |
+| `corrected_sentiment` | string | No | The correct sentiment: `positive`, `negative`, `neutral`, `mixed`. |
| `comment` | string | No | Optional free-text explanation of why this correction is needed. |
### Example request
@@ -35,28 +36,39 @@ curl -X POST https://api.trynawa.com/v1/feedback \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
- "request_id": "req_abc123def456",
- "field": "dialect",
- "expected_value": "levantine",
- "comment": "This is Lebanese Arabic, not Gulf"
+ "classification_id": "cls_a1b2c3d4e5f6",
+ "rating": "incorrect",
+ "corrected_intent": ["complaint"],
+ "corrected_sentiment": "negative",
+ "comment": "This is a complaint, not praise"
}'
```
```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
const { data, error } = await nawa.feedback.submit({
- requestId: 'req_abc123def456',
- field: 'dialect',
- expectedValue: 'levantine',
- comment: 'This is Lebanese Arabic, not Gulf'
+ classificationId: 'cls_a1b2c3d4e5f6',
+ rating: 'incorrect',
+ correctedIntent: ['complaint'],
+ correctedSentiment: 'negative',
+ comment: 'This is a complaint, not praise'
})
```
```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_a1b2c3d4e5f6",
+ rating="incorrect",
+ corrected_intent=["complaint"],
+ corrected_sentiment="negative",
+ comment="This is a complaint, not praise"
)
```
@@ -70,11 +82,23 @@ result = nawa.feedback.submit(
{
"success": true,
"result": {
- "feedback_id": "fb_xyz789",
- "status": "accepted",
- "message": "Thank you! Your feedback helps improve NAWA's accuracy."
+ "id": "fb_nw_xyz789abc",
+ "object": "feedback",
+ "classification_id": "cls_a1b2c3d4e5f6",
+ "rating": "incorrect",
+ "acknowledged": true
},
"errors": [],
- "request_id": "req_fb_abc123"
+ "request_id": "req_nw_fb_abc123"
}
```
+
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Feedback ID in `fb_xxx` format |
+| `object` | string | Always `feedback` |
+| `classification_id` | string | The classification this feedback applies to |
+| `rating` | string | The rating you submitted |
+| `acknowledged` | boolean | Always `true` on success |
diff --git a/api-reference/health.mdx b/api-reference/health.mdx
index ca110e8..87e2b8a 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"
@@ -22,41 +22,53 @@ curl https://api.trynawa.com/v1/health
"success": true,
"result": {
"status": "healthy",
- "version": "1.0.0",
+ "version": "v1",
"services": {
- "api": "operational",
- "classification": "operational",
- "database": "operational",
- "cache": "operational"
+ "database": {"status": "healthy", "latency_ms": 12},
+ "redis": {"status": "healthy", "latency_ms": 3},
+ "nagl": {"status": "healthy"}
},
- "timestamp": "2025-01-15T12:00:00Z"
+ "timestamp": "2025-01-15T12:00:00Z",
+ "latency_ms": 15
},
"errors": [],
- "request_id": "req_hlt_abc123"
+ "request_id": "req_nw_hlt_abc123"
}
```
-### Degraded response (200)
+### Degraded response (503)
```json
{
"success": true,
"result": {
"status": "degraded",
- "version": "1.0.0",
+ "version": "v1",
"services": {
- "api": "operational",
- "classification": "degraded",
- "database": "operational",
- "cache": "operational"
+ "database": {"status": "healthy", "latency_ms": 12},
+ "redis": {"status": "down"},
+ "nagl": {"status": "healthy"}
},
- "timestamp": "2025-01-15T12:00:00Z"
+ "timestamp": "2025-01-15T12:00:00Z",
+ "latency_ms": 15
},
"errors": [],
- "request_id": "req_hlt_def456"
+ "request_id": "req_nw_hlt_def456"
}
```
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `status` | string | `healthy` or `degraded` |
+| `version` | string | API version (always `v1`) |
+| `services.database` | object | Database connectivity status and latency |
+| `services.redis` | object | Redis connectivity status and latency |
+| `services.nagl` | object | NAGL classification engine status |
+| `timestamp` | string | Check time (ISO 8601) |
+| `latency_ms` | integer | Total health check duration in milliseconds |
+
For real-time status monitoring, visit [status.trynawa.com](https://status.trynawa.com).
diff --git a/api-reference/openapi.yaml b/api-reference/openapi.yaml
deleted file mode 100644
index bbfc559..0000000
--- a/api-reference/openapi.yaml
+++ /dev/null
@@ -1,493 +0,0 @@
-openapi: 3.0.3
-info:
- title: NAWA API
- description: Arabic-first AI comment classification API.
- version: 1.0.0
- contact:
- name: NAWA Support
- url: https://developers.trynawa.com
- email: support@trynawa.com
-servers:
- - url: https://api.trynawa.com
- description: Production
-security:
- - BearerAuth: []
-paths:
- /v1/classify:
- post:
- operationId: classifyComment
- summary: Classify an Arabic comment
- description: Classify a comment by intent, sentiment, dialect, and toxicity.
- tags:
- - Classification
- requestBody:
- required: true
- content:
- application/json:
- schema:
- type: object
- required:
- - text
- properties:
- text:
- type: string
- description: The comment text to classify. Max 5,000 characters.
- maxLength: 5000
- example: "متى الجزء الثاني؟"
- platform:
- type: string
- enum: [youtube, instagram, twitter, facebook]
- description: Source platform for context.
- example: youtube
- channel_id:
- type: string
- description: Channel or account identifier.
- example: ch_123
- metadata:
- type: object
- description: Arbitrary key-value metadata.
- additionalProperties: true
- responses:
- "200":
- description: Classification successful
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ClassifyResponse"
- example:
- success: true
- result:
- text: "متى الجزء الثاني؟"
- intent: question
- intent_confidence: 0.97
- 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
- errors: []
- request_id: req_abc123def456
- headers:
- X-Request-Id:
- schema:
- type: string
- description: Unique request identifier
- X-RateLimit-Limit:
- schema:
- type: integer
- description: Rate limit ceiling
- X-RateLimit-Remaining:
- schema:
- type: integer
- description: Requests remaining
- X-RateLimit-Reset:
- schema:
- type: string
- format: date-time
- description: Window reset time (RFC 3339)
- X-NAWA-Balance:
- schema:
- type: string
- description: Current credit balance in USD
- X-NAWA-Cache:
- schema:
- type: string
- enum: [HIT, MISS]
- description: Cache status
- "400":
- description: Invalid request
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ErrorResponse"
- "401":
- description: Authentication error
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ErrorResponse"
- "402":
- description: Insufficient credits
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ErrorResponse"
- "429":
- description: Rate limit exceeded
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ErrorResponse"
- "500":
- description: Internal server error
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ErrorResponse"
- /v1/rubric/classify:
- post:
- operationId: rubricClassify
- summary: Rubric-based classification
- description: Classify a comment against a custom rubric with predefined categories.
- tags:
- - Classification
- requestBody:
- required: true
- content:
- application/json:
- schema:
- type: object
- required:
- - text
- - rubric
- properties:
- text:
- type: string
- maxLength: 5000
- example: "وين الترجمة العربية؟ مافهمت شي"
- rubric:
- type: object
- required:
- - categories
- properties:
- categories:
- type: array
- items:
- type: string
- example: [translation_request, technical_issue, content_feedback]
- descriptions:
- type: object
- additionalProperties:
- type: string
- platform:
- type: string
- enum: [youtube, instagram, twitter, facebook]
- responses:
- "200":
- description: Rubric classification successful
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/RubricClassifyResponse"
- example:
- success: true
- result:
- text: "وين الترجمة العربية؟ مافهمت شي"
- category: translation_request
- category_confidence: 0.94
- scores:
- translation_request: 0.94
- technical_issue: 0.03
- content_feedback: 0.03
- dialect: gulf
- dialect_confidence: 0.91
- language: ar
- model: nagl-v1
- cached: false
- errors: []
- request_id: req_rub123abc456
- "400":
- $ref: "#/components/responses/BadRequest"
- "401":
- $ref: "#/components/responses/Unauthorized"
- "402":
- $ref: "#/components/responses/InsufficientCredits"
- "429":
- $ref: "#/components/responses/RateLimited"
- "500":
- $ref: "#/components/responses/InternalError"
- /v1/feedback:
- post:
- operationId: submitFeedback
- summary: Submit RLHF feedback
- description: Submit feedback to improve classification accuracy. This endpoint is free.
- tags:
- - Feedback
- requestBody:
- required: true
- content:
- application/json:
- schema:
- type: object
- required:
- - request_id
- - field
- - expected_value
- properties:
- request_id:
- type: string
- description: The request_id from the original classification.
- example: req_abc123def456
- field:
- type: string
- enum: [intent, sentiment, dialect, toxicity, category]
- description: The field to correct.
- example: dialect
- expected_value:
- type: string
- description: The correct value.
- example: levantine
- comment:
- type: string
- description: Optional explanation.
- example: "This is Lebanese Arabic, not Gulf"
- responses:
- "200":
- description: Feedback accepted
- content:
- application/json:
- schema:
- type: object
- properties:
- success:
- type: boolean
- example: true
- result:
- type: object
- properties:
- feedback_id:
- type: string
- example: fb_xyz789
- status:
- type: string
- example: accepted
- message:
- type: string
- example: "Thank you! Your feedback helps improve NAWA's accuracy."
- errors:
- type: array
- items:
- $ref: "#/components/schemas/Error"
- request_id:
- type: string
- example: req_fb_abc123
- "400":
- $ref: "#/components/responses/BadRequest"
- "401":
- $ref: "#/components/responses/Unauthorized"
- /v1/health:
- get:
- operationId: healthCheck
- summary: Health check
- description: Check operational status. No authentication required.
- tags:
- - System
- security: []
- responses:
- "200":
- description: Service status
- content:
- application/json:
- schema:
- type: object
- properties:
- success:
- type: boolean
- example: true
- result:
- type: object
- properties:
- status:
- type: string
- enum: [healthy, degraded, down]
- example: healthy
- version:
- type: string
- example: "1.0.0"
- services:
- type: object
- properties:
- api:
- type: string
- example: operational
- classification:
- type: string
- example: operational
- database:
- type: string
- example: operational
- cache:
- type: string
- example: operational
- timestamp:
- type: string
- format: date-time
- errors:
- type: array
- items:
- $ref: "#/components/schemas/Error"
- request_id:
- type: string
-components:
- securitySchemes:
- BearerAuth:
- type: http
- scheme: bearer
- description: "API key authentication. Use format: Bearer nawa_live_sk_xxx"
- schemas:
- ClassifyResponse:
- type: object
- properties:
- success:
- type: boolean
- result:
- type: object
- properties:
- text:
- type: string
- intent:
- type: string
- enum: [question, complaint, praise, suggestion, spam, other]
- intent_confidence:
- type: number
- minimum: 0
- maximum: 1
- sentiment:
- type: string
- enum: [positive, negative, neutral, mixed]
- sentiment_confidence:
- type: number
- minimum: 0
- maximum: 1
- dialect:
- type: string
- enum: [gulf, egyptian, levantine, msa]
- dialect_confidence:
- type: number
- minimum: 0
- maximum: 1
- toxicity:
- type: string
- enum: [none, mild, moderate, severe]
- toxicity_confidence:
- type: number
- minimum: 0
- maximum: 1
- categories:
- type: array
- items:
- type: string
- language:
- type: string
- model:
- type: string
- cached:
- type: boolean
- errors:
- type: array
- items:
- $ref: "#/components/schemas/Error"
- request_id:
- type: string
- RubricClassifyResponse:
- type: object
- properties:
- success:
- type: boolean
- result:
- type: object
- properties:
- text:
- type: string
- category:
- type: string
- category_confidence:
- type: number
- scores:
- type: object
- additionalProperties:
- type: number
- dialect:
- type: string
- dialect_confidence:
- type: number
- language:
- type: string
- model:
- type: string
- cached:
- type: boolean
- errors:
- type: array
- items:
- $ref: "#/components/schemas/Error"
- request_id:
- type: string
- Error:
- type: object
- properties:
- type:
- type: string
- enum:
- - invalid_request_error
- - authentication_error
- - insufficient_credits
- - rate_limit_error
- - api_error
- code:
- type: string
- message:
- type: string
- display_message:
- type: string
- param:
- type: string
- nullable: true
- doc_url:
- type: string
- format: uri
- suggested_action:
- type: string
- ErrorResponse:
- type: object
- properties:
- success:
- type: boolean
- example: false
- result:
- nullable: true
- type: string
- description: Always null on error responses
- errors:
- type: array
- items:
- $ref: "#/components/schemas/Error"
- request_id:
- type: string
- responses:
- BadRequest:
- description: Invalid request
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ErrorResponse"
- Unauthorized:
- description: Authentication error
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ErrorResponse"
- InsufficientCredits:
- description: Insufficient credits
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ErrorResponse"
- RateLimited:
- description: Rate limit exceeded
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ErrorResponse"
- InternalError:
- description: Internal server error
- content:
- application/json:
- schema:
- $ref: "#/components/schemas/ErrorResponse"
diff --git a/api-reference/rubric-classify.mdx b/api-reference/rubric-classify.mdx
index 4489175..345f133 100644
--- a/api-reference/rubric-classify.mdx
+++ b/api-reference/rubric-classify.mdx
@@ -1,14 +1,14 @@
---
-title: "Rubric Classify"
-sidebarTitle: "POST /v1/rubric/classify"
-description: "Classify a comment against a custom rubric with predefined scoring criteria."
-api: "POST https://api.trynawa.com/v1/rubric/classify"
+title: "Rubric classify"
+sidebarTitle: "POST /v1/rubric-classify"
+description: "Classify a comment against a custom rubric with up to 20 categories."
+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 user-defined rubric. Define up to 20 categories with optional descriptions for domain-specific classification. Supports multi-label classification 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).
## Request
@@ -18,63 +18,77 @@ Classify a comment against a custom rubric. Define your own categories and scori
| 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. |
+| `rubric` | object | Yes | The rubric definition. |
+| `rubric.categories` | array | Yes | Array of category objects, each with `name` and optional `description`. Max 20 categories. |
+| `rubric.multi_label` | boolean | No | If `true`, return all categories above the confidence threshold. Default: `false`. |
+| `rubric.confidence_threshold` | number | No | Minimum confidence (0-1) to include a category. Default: `0.5`. |
+
+### Query parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `provider` | string | No | Force a specific AI provider: `claude`, `gemini`, or `allam`. |
### Example request
```bash cURL
-curl -X POST https://api.trynawa.com/v1/rubric/classify \
+curl -X POST https://api.trynawa.com/v1/rubric-classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
"text": "وين الترجمة العربية؟ مافهمت شي",
"rubric": {
- "categories": ["translation_request", "technical_issue", "content_feedback", "off_topic"],
- "descriptions": {
- "translation_request": "User is asking for subtitles or translation",
- "technical_issue": "Audio, video, or playback problems",
- "content_feedback": "Comments about the content quality",
- "off_topic": "Not related to the video"
- }
- },
- "platform": "youtube"
+ "categories": [
+ {"name": "translation_request", "description": "User is asking for subtitles or translation"},
+ {"name": "technical_issue", "description": "Audio, video, or playback problems"},
+ {"name": "content_feedback", "description": "Comments about the content quality"},
+ {"name": "off_topic", "description": "Not related to the video"}
+ ],
+ "multi_label": false,
+ "confidence_threshold": 0.5
+ }
}'
```
```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
const { data, error } = await nawa.rubric.classify({
text: 'وين الترجمة العربية؟ مافهمت شي',
rubric: {
- categories: ['translation_request', 'technical_issue', 'content_feedback', 'off_topic'],
- descriptions: {
- translation_request: 'User is asking for subtitles or translation',
- technical_issue: 'Audio, video, or playback problems',
- content_feedback: 'Comments about the content quality',
- off_topic: 'Not related to the video'
- }
- },
- platform: 'youtube'
+ categories: [
+ { name: 'translation_request', description: 'User is asking for subtitles or translation' },
+ { name: 'technical_issue', description: 'Audio, video, or playback problems' },
+ { name: 'content_feedback', description: 'Comments about the content quality' },
+ { name: 'off_topic', description: 'Not related to the video' }
+ ],
+ multiLabel: false,
+ confidenceThreshold: 0.5
+ }
})
```
```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
result = nawa.rubric.classify(
text="وين الترجمة العربية؟ مافهمت شي",
rubric={
- "categories": ["translation_request", "technical_issue", "content_feedback", "off_topic"],
- "descriptions": {
- "translation_request": "User is asking for subtitles or translation",
- "technical_issue": "Audio, video, or playback problems",
- "content_feedback": "Comments about the content quality",
- "off_topic": "Not related to the video",
- },
+ "categories": [
+ {"name": "translation_request", "description": "User is asking for subtitles or translation"},
+ {"name": "technical_issue", "description": "Audio, video, or playback problems"},
+ {"name": "content_feedback", "description": "Comments about the content quality"},
+ {"name": "off_topic", "description": "Not related to the video"},
+ ],
+ "multi_label": False,
+ "confidence_threshold": 0.5,
},
- platform="youtube",
)
```
@@ -88,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_pon1yom9bkfe",
+ "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,13 @@ 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 | Rubric classification ID in `rcl_xxx` format |
+| `object` | string | Always `rubric_classification` |
+| `categories` | array | Matched categories with `name` and `confidence` (0-1) score |
+| `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 the fallback provider was used |
+| `cost_usd` | number | Cost in USD |
+| `credits_used` | integer | Credits deducted |
diff --git a/api-reference/usage.mdx b/api-reference/usage.mdx
index d4b1b6b..b4dc19f 100644
--- a/api-reference/usage.mdx
+++ b/api-reference/usage.mdx
@@ -15,15 +15,41 @@ Retrieve your API usage statistics including request counts, credit consumption,
|-----------|------|----------|-------------|
| `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`. |
+| `group_by` | string | No | Group by: `endpoint` (default) or `day`. |
### 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"
```
+```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.usage({
+ from: '2025-01-01T00:00:00Z',
+ groupBy: 'endpoint'
+})
+```
+
+```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.usage(
+ from_date="2025-01-01T00:00:00Z",
+ group_by="endpoint"
+)
+```
+
+
+
## Response
### Success response (200)
@@ -32,44 +58,39 @@ 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": 2870,
+ "cache_hit_rate": 0.23,
+ "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
+ "cache_hits": 1955
},
{
- "endpoint": "/v1/rubric/classify",
- "requests": 2200,
- "cost": 6.60,
- "cache_hits": 880,
- "avg_latency_ms": 650
+ "endpoint": "/v1/translate",
+ "requests": 1200,
+ "cost": 6.00,
+ "cache_hits": 276
},
{
- "endpoint": "/v1/comments/:id/reply",
+ "endpoint": "/v1/comments/reply",
"requests": 1750,
"cost": 14.00,
- "cache_hits": 0,
- "avg_latency_ms": 1200
+ "cache_hits": 0
}
- ],
- "balance": {
- "current": 31.30,
- "credits_purchased": 100.00,
- "credits_used": 68.70
- }
+ ]
},
"errors": [],
- "request_id": "req_usg_abc123"
+ "request_id": "req_nw_usg_abc123"
}
```
@@ -77,8 +98,8 @@ curl "https://api.trynawa.com/v1/usage?from=2025-01-01T00:00:00Z&group_by=endpoi
| 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 |
+| `usage.total_requests` | integer | Total API requests in the period |
+| `usage.total_cost` | number | Total credit cost in USD |
+| `usage.cache_hits` | integer | Number of cache hits |
+| `usage.cache_hit_rate` | number | Cache hit ratio (0-1) |
+| `usage.period` | object | Date range queried |
diff --git a/authentication.mdx b/authentication.mdx
index 402154b..2e4f3ce 100644
--- a/authentication.mdx
+++ b/authentication.mdx
@@ -1,7 +1,7 @@
---
title: "Authentication"
sidebarTitle: "Authentication"
-description: "API key management, free vs paid modes, and security best practices."
+description: "API key management, free vs live modes, tiers, and security best practices."
---
All NAWA API requests require a Bearer token in the `Authorization` header.
@@ -14,15 +14,17 @@ Authorization: Bearer nawa_live_sk_xxx
| Environment | Prefix | Example |
|-------------|--------|---------|
-| Free | `nawa_test_sk_` | `nawa_test_sk_a1b2c3d4e5f6` |
-| Live | `nawa_live_sk_` | `nawa_live_sk_x9y8z7w6v5u4` |
+| Free | `nawa_test_sk_` | `nawa_test_sk_a1b2c3d4e5f67890abcdef0123456789` |
+| Live | `nawa_live_sk_` | `nawa_live_sk_a1b2c3d4e5f67890abcdef0123456789` |
+
+Keys are 32 hex characters after the prefix.
## Free vs live keys
**Free, no credit card required.**
- - 100 lifetime requests
+ - 100 lifetime requests (across all free keys)
- 14-day key expiry
- Uses real AI models (same accuracy)
- Rate limit: 10 requests/minute
@@ -38,6 +40,20 @@ Authorization: Bearer nawa_live_sk_xxx
+## API tiers
+
+| Tier | Description |
+|------|-------------|
+| `sandbox` | Free keys. 100 lifetime requests total, 10 req/min. |
+| `trial` | Entry-level live keys. |
+| `growth` | Standard live keys after first credit purchase. |
+| `enterprise` | High-volume access with calibration endpoints. |
+| `enterprise_plus` | Highest-volume access with custom SLAs. |
+
+
+ Your tier is determined automatically based on your account status. The `enterprise` and `enterprise_plus` tiers unlock the `/v1/calibration` endpoint for custom classification thresholds.
+
+
## Creating API keys
@@ -45,13 +61,13 @@ Authorization: Bearer nawa_live_sk_xxx
Go to [trynawa.com/developers/keys](https://trynawa.com/developers/keys) and sign in with your account.
- Go to [trynawa.com/developers/keys](https://trynawa.com/developers/keys) and click **Create Key**.
+ Click **Create Key** and give it a name.
Your key is displayed **once** at creation. Copy it immediately and store it securely.
- API keys are shown only once at creation. They are stored as SHA-256 hashes - we cannot retrieve your key later. If you lose it, revoke the old key and create a new one.
+ API keys are shown only once at creation. They are stored as SHA-256 hashes -- we cannot retrieve your key later. If you lose it, revoke the old key and create a new one.
@@ -71,20 +87,16 @@ We recommend rotating live keys every **90 days**. To rotate without downtime:
## Key revocation
-Revoke a key instantly from the dashboard under **Settings → API Keys**. Revoked keys return a `401 authentication_error` immediately.
+Revoke a key instantly from the dashboard under **Settings > API Keys**. Revoked keys return a `401 authentication_error` immediately.
## Security best practices
- **Never commit keys to source control.** Use environment variables or a secret manager.
- **Use free keys for development.** Reserve live keys for deployed applications.
-- **Restrict keys by IP** (planned) for live workloads.
- **Monitor usage** in the dashboard to detect unauthorized use.
- **Rotate keys** every 90 days or immediately if compromised.
```typescript
-// ✅ Load from environment
+// Load from environment
const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
-
-// ❌ Never hardcode keys
-const nawa = new Nawa({ apiKey: 'nawa_live_sk_abc123' })
```
diff --git a/billing.mdx b/billing.mdx
index e28bb54..1ce7a2f 100644
--- a/billing.mdx
+++ b/billing.mdx
@@ -1,5 +1,5 @@
---
-title: "Billing & Credits"
+title: "Billing & credits"
sidebarTitle: "Billing"
description: "Credit packs, per-endpoint pricing, free tier details, and refund policy."
---
@@ -16,13 +16,13 @@ NAWA uses a prepaid credit system.
- - **100 lifetime requests** per free key (`nawa_test_sk_`)
+ - **100 lifetime requests** per account (across all free keys, `nawa_test_sk_`)
- 14-day key expiry
- No credit card required
- Uses real AI models -- same accuracy as paid
- Rate limit: 10 requests/minute
- Free endpoints (health, usage, analytics, comments, feedback) do not count toward the 100 limit
- - When exhausted: create a new free key or purchase credits for a live key
+ - When exhausted: purchase credits for a live key
@@ -47,15 +47,14 @@ NAWA uses a prepaid credit system.
| `POST /v1/translate` | $0.005 | 5 credits |
| `POST /v1/detect` | $0.002 | 2 credits |
| `POST /v1/moderate` | $0.004 | 4 credits |
-| `POST /v1/rubric/classify` | $0.003 | 3 credits |
-| `POST /v1/comments/:id/reply` | $0.008 | 8 credits |
-| `POST /v1/report` (Basic) | $0.50 | 500 credits |
-| `POST /v1/report` (Pro) | $1.50 | 1,500 credits |
+| `POST /v1/rubric-classify` | $0.003 | 3 credits |
+| `POST /v1/comments/reply` | $0.008 | 8 credits |
| `POST /v1/feedback` | **Free** | 0 credits |
| `GET /v1/comments` | **Free** | 0 credits |
| `GET /v1/analytics` | **Free** | 0 credits |
| `GET /v1/health` | **Free** | 0 credits |
| `GET /v1/usage` | **Free** | 0 credits |
+| `POST /v1/webhooks` | **Free** | 0 credits |
Semantic cache hits (`X-NAWA-Cache: HIT`) cost **$0** -- even on paid endpoints. Classifying the same or semantically similar text is free after the first call.
@@ -68,14 +67,9 @@ Every API response includes balance information in headers:
| Header | Description | Example |
|--------|-------------|---------|
| `X-NAWA-Balance` | Current credit balance in USD | `12.45` |
-| `X-NAWA-Balance-Warning` | Warning when balance is low | `low_balance` |
| `X-NAWA-Cache` | Cache status | `HIT` or `MISS` |
| `X-Request-Id` | Unique request identifier | `req_nw_abc123` |
-
- The `X-NAWA-Balance-Warning` header appears when your balance drops below $5.00 (5,000 credits).
-
-
## What happens when credits run out
When your credit balance reaches 0:
@@ -86,7 +80,7 @@ When your credit balance reaches 0:
- Purchase any credit pack to restore access immediately
- There is no grace period. When credits hit zero, paid endpoints stop immediately. Enable monitoring on your `X-NAWA-Balance` header to avoid service interruption.
+ There is no grace period. When credits hit zero, paid endpoints stop immediately. Monitor your `X-NAWA-Balance` header to avoid service interruption.
## Cost estimation
@@ -97,8 +91,8 @@ When your credit balance reaches 0:
| `/v1/translate` | $5 | $50 | $500 |
| `/v1/detect` | $2 | $20 | $200 |
| `/v1/moderate` | $4 | $40 | $400 |
-| `/v1/rubric/classify` | $3 | $30 | $300 |
-| `/v1/comments/:id/reply` | $8 | $80 | $800 |
+| `/v1/rubric-classify` | $3 | $30 | $300 |
+| `/v1/comments/reply` | $8 | $80 | $800 |
With typical semantic cache hit rates of 20-30%, actual costs are 20-30% lower than these estimates.
diff --git a/docs.json b/docs.json
index 8f8f901..6822578 100644
--- a/docs.json
+++ b/docs.json
@@ -57,7 +57,6 @@
"api-reference/moderate",
"api-reference/rubric-classify",
"api-reference/comments-reply",
- "api-reference/report",
"api-reference/feedback",
"api-reference/comments-list",
"api-reference/analytics",
diff --git a/errors.mdx b/errors.mdx
index fdcf365..6b1a2eb 100644
--- a/errors.mdx
+++ b/errors.mdx
@@ -10,20 +10,17 @@ Every NAWA API error returns a consistent JSON envelope with machine-readable co
```json
{
- "success": false,
- "result": null,
- "errors": [
- {
- "type": "invalid_request_error",
- "code": "missing_required_param",
- "message": "The 'text' parameter is required.",
- "display_message": "Please provide the comment text to classify.",
- "param": "text",
- "doc_url": "https://developers.trynawa.com/errors#missing_required_param",
- "suggested_action": "Include the 'text' field in your request body."
- }
- ],
- "request_id": "req_abc123def456"
+ "type": "error",
+ "error": {
+ "type": "invalid_request_error",
+ "code": "missing_field",
+ "message": "`text` is required",
+ "display_message": "The required field `text` is missing.",
+ "param": "text",
+ "doc_url": "https://developers.trynawa.com/errors#missing_field",
+ "suggested_action": "Include `text` in your request body."
+ },
+ "request_id": "req_nw_a1b2c3d4e5f67890"
}
```
@@ -114,7 +111,7 @@ The request was malformed or missing required parameters.
The API key is missing, invalid, expired, or revoked.
-
+
**Cause:** No `Authorization` header was provided.
**Fix:** Include the header: `Authorization: Bearer nawa_live_sk_xxx`
@@ -122,17 +119,17 @@ The API key is missing, invalid, expired, or revoked.
```json
{
"type": "authentication_error",
- "code": "missing_api_key",
- "message": "No API key provided in the Authorization header.",
+ "code": "missing_auth_header",
+ "message": "No Authorization header provided.",
"suggested_action": "Add 'Authorization: Bearer YOUR_API_KEY' to your request headers."
}
```
- **Cause:** The API key is not recognized. It may be malformed or from a different environment.
+ **Cause:** The API key is not recognized. It may be malformed, from a different environment, or the prefix is incorrect.
- **Fix:** Verify the key is correct and matches the environment (free/live).
+ **Fix:** Verify the key is correct and starts with `nawa_live_sk_` or `nawa_test_sk_`.
```json
{
@@ -144,30 +141,30 @@ The API key is missing, invalid, expired, or revoked.
```
-
+
**Cause:** The free API key has exceeded its 14-day expiry.
- **Fix:** Create a new free key from the dashboard. Live keys don't expire.
+ **Fix:** Create a new free key from the dashboard. Live keys do not expire.
```json
{
"type": "authentication_error",
- "code": "expired_api_key",
+ "code": "key_expired",
"message": "This free API key has expired (14-day limit).",
"suggested_action": "Create a new free key from the dashboard."
}
```
-
- **Cause:** The API key has been manually revoked.
+
+ **Cause:** The API key has been revoked or deactivated.
**Fix:** Use a different active key or create a new one.
```json
{
"type": "authentication_error",
- "code": "revoked_api_key",
+ "code": "key_revoked",
"message": "This API key has been revoked.",
"suggested_action": "Create a new API key from the dashboard."
}
diff --git a/openapi.yaml b/openapi.yaml
index 17706f2..83e8020 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: NAWA API
- version: 1.1.1
+ version: 1.2.0
description: |
Arabic language intelligence API.
تصنيف ذكي للمحتوى العربي - من الخليج إلى المغرب.
@@ -39,7 +39,7 @@ tags:
- name: Comments
description: Comment retrieval and filtering
- name: Analytics
- description: Aggregated analytics
+ description: API analytics
- name: Usage
description: API usage statistics
- name: System
@@ -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 in a single API call. Supports semantic caching.
tags: [Classification]
x-nawa-cost: "$0.006"
x-nawa-cache: true
x-nawa-free: false
+ parameters:
+ - name: provider
+ in: query
+ required: false
+ description: Force a specific AI provider (for A/B testing)
+ schema:
+ type: string
+ enum: [claude, gemini, allam]
requestBody:
required: true
content:
@@ -75,20 +83,11 @@ paths:
examples:
- "متى الجزء الثاني؟"
- "ما شاء الله عليك، محتوى رهيب!"
- platform:
- type: string
- enum: [youtube, instagram, twitter, facebook]
- description: Source platform for context-aware classification
- channel_id:
- type: string
- description: Your channel or account identifier for analytics grouping
- metadata:
+ context:
type: object
- description: Arbitrary key-value metadata to attach to the classification
+ description: Optional context for classification
example:
text: "متى الجزء الثاني؟"
- platform: "youtube"
- channel_id: "ch_123"
responses:
"200":
description: Classification result
@@ -117,19 +116,25 @@ paths:
value:
success: true
result:
- text: "متى الجزء الثاني؟"
- intent: question
- intent_confidence: 0.97
+ id: cls_a1b2c3d4e5f6
+ object: classification
+ intent:
+ - question
sentiment: neutral
- sentiment_confidence: 0.91
+ language: ar
dialect: gulf
dialect_confidence: 0.95
- toxicity: none
- toxicity_confidence: 0.99
- categories: ["engagement"]
- language: ar
- model: nagl-v1
- cached: false
+ requires_response: true
+ priority: high
+ suggested_reply:
+ text: "سؤال من متابع عن موعد الجزء الثاني"
+ direction: rtl
+ provider: allam
+ model: sdaia/allam-1-13b-instruct
+ fallback_used: false
+ tokens_used: null
+ cost_usd: 0.006
+ credits_used: 6
errors: []
request_id: req_nw_a1b2c3d4e5f67890
"400":
@@ -194,9 +199,6 @@ paths:
type: string
enum: [youtube, instagram, twitter, facebook]
description: Source platform for social media tone context
- metadata:
- type: object
- description: Arbitrary key-value metadata
example:
text: "Hello, how are you?"
source: "en"
@@ -243,6 +245,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
@@ -457,17 +460,26 @@ 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. Define up to 20 categories
+ with optional descriptions for domain-specific classification.
+ 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
+ description: Force a specific AI provider
+ schema:
+ type: string
+ enum: [claude, gemini, allam]
requestBody:
required: true
content:
@@ -492,27 +504,38 @@ paths:
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
+ type: object
+ required: [name]
+ properties:
+ name:
+ type: string
+ description:
+ type: string
+ description: Category definitions for classification
+ multi_label:
+ type: boolean
+ default: false
+ description: "If true, return all matching categories above the confidence threshold"
+ confidence_threshold:
+ type: number
+ minimum: 0
+ maximum: 1
+ default: 0.5
+ description: Minimum confidence to include a category in results
example:
text: "وين الترجمة العربية؟ مافهمت شي"
rubric:
- categories: ["translation_request", "technical_issue", "content_feedback", "off_topic"]
- descriptions:
- translation_request: "User is asking for subtitles or translation"
- technical_issue: "Audio, video, or playback problems"
- content_feedback: "Comments about the content quality"
- off_topic: "Not related to the video"
- platform: "youtube"
+ categories:
+ - name: translation_request
+ description: "User is asking for subtitles or translation"
+ - name: technical_issue
+ description: "Audio, video, or playback problems"
+ - name: content_feedback
+ description: "Comments about the content quality"
+ - name: off_topic
+ description: "Not related to the video"
+ multi_label: false
+ confidence_threshold: 0.5
responses:
"200":
description: Rubric classification result
@@ -521,8 +544,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:
@@ -541,21 +562,22 @@ paths:
value:
success: true
result:
- text: "وين الترجمة العربية؟ مافهمت شي"
- category: translation_request
- category_confidence: 0.94
- scores:
- translation_request: 0.94
- technical_issue: 0.03
- content_feedback: 0.02
- off_topic: 0.01
- dialect: gulf
- dialect_confidence: 0.91
+ id: rcl_nw_pon1yom9bkfe
+ 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,37 +589,44 @@ 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, max length, and optional brand voice context.
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
+ description: Force a specific AI provider
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
+ default: professional
description: Reply tone
max_length:
type: integer
@@ -606,26 +635,26 @@ paths:
default: 500
description: Maximum reply length in characters
context:
- type: string
- description: Additional context about the channel or video
- language:
- type: string
- enum: [ar, en, auto]
- default: auto
- description: "Force reply language. Default: auto (matches commenter's language)."
+ type: object
+ properties:
+ platform:
+ type: string
+ description: Platform context (e.g. youtube, instagram)
+ brand_voice:
+ type: string
+ description: Brand voice description to guide reply style
example:
+ text: "متى الجزء الثاني؟"
tone: "friendly"
- context: "Tech review channel focused on smartphones"
+ 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:
@@ -644,14 +673,24 @@ paths:
value:
success: true
result:
- comment_id: cmt_abc123
- reply_text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔"
- reply_dialect: gulf
- tone: friendly
- original_intent: question
- original_dialect: gulf
+ id: rpl_nw_a1b2c3d4e5f6
+ object: comment_reply
+ classification:
+ intent:
+ - question
+ sentiment: neutral
+ priority: high
+ requires_response: true
+ reply:
+ text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك"
+ direction: rtl
+ tone: friendly
+ provider: allam
+ model: sdaia/allam-1-13b-instruct
+ cost_usd: 0.008
+ credits_used: 8
errors: []
- request_id: req_rep789xyz012
+ request_id: req_nw_rep789xyz012
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -681,10 +720,12 @@ 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_a1b2c3d4e5f6"
+ rating: "incorrect"
+ corrected_intent:
+ - "complaint"
+ corrected_sentiment: "negative"
+ comment: "This is a complaint, not praise"
responses:
"200":
description: Feedback accepted
@@ -701,11 +742,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_xyz789abc
+ object: feedback
+ classification_id: cls_a1b2c3d4e5f6
+ rating: incorrect
+ acknowledged: true
errors: []
- request_id: req_fb_abc123
+ request_id: req_nw_fb_abc123
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -721,17 +764,12 @@ paths:
summary: List classified comments
description: |
Retrieve classified comments with filtering and pagination. Free endpoint.
+ Results are scoped to the authenticated API key.
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 +798,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
@@ -790,12 +807,13 @@ paths:
maximum: 100
default: 25
description: Results per page
- - name: cursor
+ - name: offset
in: query
required: false
schema:
- type: string
- description: Pagination cursor from previous response
+ type: integer
+ default: 0
+ description: Number of results to skip for pagination
responses:
"200":
description: List of classified comments
@@ -813,21 +831,21 @@ paths:
success: true
result:
comments:
- - id: cmt_abc123
+ - id: cls_a1b2c3d4e5f6
+ request_id: req_nw_abc123
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_lst_abc123
"401":
$ref: "#/components/responses/Unauthorized"
"429":
@@ -838,28 +856,15 @@ paths:
/analytics:
get:
operationId: getAnalytics
- summary: Get analytics
+ summary: Get API analytics
description: |
- Retrieve aggregated analytics on comment trends, sentiment shifts, and
- engagement patterns. Free endpoint.
+ Retrieve aggregated API analytics including request counts, costs,
+ cache hit rates, 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 +884,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 results by endpoint, day, or provider
responses:
"200":
description: Analytics data
@@ -893,45 +898,36 @@ 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: 2870
+ cache_hit_rate: 0.23
+ avg_latency_ms: 820
+ success_rate: 0.99
+ error_count: 12
+ 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
errors: []
- request_id: req_ana_abc123
+ request_id: req_nw_ana_abc123
"401":
$ref: "#/components/responses/Unauthorized"
"429":
@@ -965,15 +961,20 @@ paths:
success: true
result:
status: healthy
- version: "1.0.0"
+ version: v1
services:
- api: operational
- classification: operational
- database: operational
- cache: operational
+ database:
+ status: healthy
+ latency_ms: 12
+ redis:
+ status: healthy
+ latency_ms: 3
+ nagl:
+ status: healthy
timestamp: "2025-01-15T12:00:00Z"
+ latency_ms: 15
errors: []
- request_id: req_hlt_abc123
+ request_id: req_nw_hlt_abc123
"503":
description: One or more services degraded
content:
@@ -999,22 +1000,22 @@ paths:
schema:
type: string
format: date-time
- description: Start date (ISO 8601). Default: start of current month.
+ description: Start date (ISO 8601). Default is start of current month.
- name: to
in: query
required: false
schema:
type: string
format: date-time
- description: End date (ISO 8601). Default: now.
+ description: End date (ISO 8601). Default is now.
- 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 +1032,29 @@ 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: 2870
+ cache_hit_rate: 0.23
+ 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"
+ - endpoint: "/v1/comments/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_usg_abc123
"401":
$ref: "#/components/responses/Unauthorized"
"429":
@@ -1070,7 +1066,9 @@ paths:
post:
operationId: createWebhook
summary: Create webhook endpoint
- description: Register a new webhook endpoint for event notifications.
+ description: |
+ Register a new webhook endpoint for event notifications.
+ Maximum 10 webhooks per user. URL must use HTTPS.
tags: [Webhooks]
x-nawa-cost: "Free"
x-nawa-cache: false
@@ -1097,8 +1095,6 @@ paths:
enum:
- classification.completed
- classification.failed
- - comment.new
- - comment.replied
- credits.low
- credits.exhausted
responses:
@@ -1158,12 +1154,12 @@ components:
type: http
scheme: bearer
description: |
- API key authentication. Format: `Bearer nawa_live_sk_xxx` (production)
- or `Bearer nawa_test_sk_xxx` (sandbox, 100 request cap).
+ API key authentication. Format: `Bearer nawa_live_sk_xxx` (live)
+ or `Bearer nawa_test_sk_xxx` (free, 100 request cap).
headers:
X-Request-Id:
- description: Unique request identifier
+ description: Unique request identifier (req_nw_xxx format)
schema:
type: string
X-NAWA-Provider:
@@ -1424,51 +1420,68 @@ 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_xxx format)
+ examples: [cls_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 (e.g. praise, question, complaint)
sentiment:
type: string
enum: [positive, negative, neutral, mixed]
- sentiment_confidence:
- type: number
- minimum: 0
- maximum: 1
+ language:
+ type: string
+ enum: [ar, en, mixed]
dialect:
type: string
- enum: [gulf, egyptian, levantine, msa]
+ nullable: true
+ description: Arabic dialect (gulf, egyptian, levantine, msa). Null for English text.
dialect_confidence:
type: number
+ nullable: true
minimum: 0
maximum: 1
- toxicity:
+ requires_response:
+ type: boolean
+ description: Whether the comment needs a reply
+ priority:
type: string
- enum: [none, mild, moderate, severe]
- toxicity_confidence:
- type: number
- minimum: 0
- maximum: 1
- categories:
- type: array
- items:
- type: string
- description: "Content categories: engagement, support, feedback, spam"
- language:
+ enum: [high, medium, low]
+ suggested_reply:
+ type: object
+ nullable: true
+ properties:
+ text:
+ type: string
+ direction:
+ type: string
+ enum: [ltr, rtl]
+ provider:
type: string
- enum: [ar, en, mixed]
+ description: AI provider used (allam or claude)
model:
type: string
- cached:
+ description: Model identifier
+ fallback_used:
type: boolean
+ description: Whether the fallback provider was used
+ tokens_used:
+ type: integer
+ nullable: true
+ cost_usd:
+ type: number
+ description: Cost in USD for this request
+ credits_used:
+ type: integer
+ description: Credits deducted
TranslateResult:
type: object
@@ -1476,7 +1489,7 @@ components:
properties:
id:
type: string
- description: Translation ID (trn_nw_xxx format)
+ description: Translation ID (trn_xxx format)
object:
type: string
const: translation
@@ -1513,6 +1526,9 @@ components:
fallback_used:
type: boolean
description: Whether the fallback provider was used
+ tokens_used:
+ type: integer
+ nullable: true
cached:
type: boolean
cost_usd:
@@ -1526,7 +1542,7 @@ components:
properties:
id:
type: string
- description: Detection ID (det_nw_xxx format)
+ description: Detection ID (det_xxx format)
object:
type: string
const: detection
@@ -1575,7 +1591,7 @@ components:
properties:
id:
type: string
- description: Moderation ID (mod_nw_xxx format)
+ description: Moderation ID (mod_xxx format)
object:
type: string
const: moderation
@@ -1687,250 +1703,267 @@ 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:
+ description: Rubric classification ID (rcl_xxx format)
+ object:
type: string
- enum: [gulf, egyptian, levantine, msa]
- dialect_confidence:
- type: number
- minimum: 0
- maximum: 1
+ const: rubric_classification
+ categories:
+ type: array
+ items:
+ type: object
+ required: [name, confidence]
+ properties:
+ name:
+ type: string
+ confidence:
+ type: number
+ minimum: 0
+ maximum: 1
+ description: Matched categories with confidence scores
+ multi_label:
+ type: boolean
+ description: Whether multi-label mode was used
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:
+ description: Reply ID (rpl_xxx format)
+ object:
type: string
- enum: [friendly, professional, casual, formal]
- description: The tone used for the reply
- original_intent:
+ const: comment_reply
+ classification:
+ type: object
+ properties:
+ intent:
+ type: array
+ items:
+ type: string
+ sentiment:
+ type: string
+ priority:
+ type: string
+ requires_response:
+ type: boolean
+ reply:
+ type: object
+ properties:
+ text:
+ type: string
+ description: The generated reply text
+ direction:
+ type: string
+ enum: [ltr, rtl]
+ description: Text direction (rtl if reply contains Arabic characters)
+ 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 classification ID (cls_xxx) from the original classify response
+ rating:
type: string
- enum: [intent, sentiment, dialect, toxicity, category]
- description: The field to correct
- expected_value:
+ enum: [correct, incorrect, partial]
+ description: Whether the classification was correct, incorrect, or partially correct
+ corrected_intent:
+ type: array
+ items:
+ type: string
+ description: The correct intent(s) if the classification was wrong
+ corrected_sentiment:
type: string
- description: The correct value the model should have returned
+ enum: [positive, negative, neutral, mixed]
+ description: The correct sentiment if the classification was wrong
comment:
type: string
- description: Optional explanation of why this correction is needed
+ description: Optional free-text explanation
FeedbackResult:
type: object
+ required: [id, object, classification_id, rating, acknowledged]
properties:
- feedback_id:
+ id:
type: string
- status:
+ description: Feedback ID (fb_xxx format)
+ 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:
type: string
- 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]
+ nullable: true
created_at:
type: string
format: date-time
- channel_id:
- type: string
Pagination:
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:
- type: object
- additionalProperties:
- type: integer
- sentiment:
+ total_requests:
+ type: integer
+ total_cost:
+ type: number
+ cache_hits:
+ type: integer
+ cache_hit_rate:
+ type: number
+ avg_latency_ms:
+ type: number
+ success_rate:
+ type: number
+ error_count:
+ type: integer
+ endpoint_distribution:
type: object
additionalProperties:
type: integer
- dialect:
+ provider_distribution:
type: object
additionalProperties:
type: integer
- toxicity:
+ 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:
type: object
+ required: [status, version, services, timestamp]
properties:
status:
type: string
enum: [healthy, degraded]
version:
type: string
+ const: v1
services:
type: object
properties:
- api:
- type: string
- enum: [operational, degraded, down]
- classification:
- type: string
- enum: [operational, degraded, down]
database:
- type: string
- enum: [operational, degraded, down]
- cache:
- type: string
- enum: [operational, degraded, down]
+ $ref: "#/components/schemas/ServiceStatus"
+ redis:
+ $ref: "#/components/schemas/ServiceStatus"
+ nagl:
+ $ref: "#/components/schemas/ServiceStatus"
timestamp:
type: string
format: date-time
+ latency_ms:
+ type: integer
- UsageResult:
+ ServiceStatus:
type: object
properties:
- period:
- type: object
- properties:
- from:
- type: string
- format: date-time
- to:
- type: string
- format: date-time
- total_requests:
+ status:
+ type: string
+ enum: [healthy, degraded, down]
+ latency_ms:
type: integer
- total_cost:
- type: number
- description: Total cost in USD
- cache_hit_rate:
- type: number
- minimum: 0
- maximum: 1
- by_endpoint:
- type: array
- items:
- type: object
- properties:
- endpoint:
- type: string
- requests:
- type: integer
- cost:
- type: number
- cache_hits:
- type: integer
- avg_latency_ms:
- type: integer
- balance:
+
+ UsageResult:
+ type: object
+ properties:
+ usage:
type: object
properties:
- current:
+ total_requests:
+ type: integer
+ total_cost:
type: number
- credits_purchased:
- type: number
- credits_used:
+ description: Total cost in USD
+ cache_hits:
+ type: integer
+ cache_hit_rate:
type: number
+ minimum: 0
+ maximum: 1
+ period:
+ type: object
+ properties:
+ from:
+ type: string
+ format: date-time
+ nullable: true
+ to:
+ type: string
+ format: date-time
+ nullable: true
WebhookEndpoint:
type: object
@@ -1950,11 +1983,19 @@ components:
type: string
signing_secret:
type: string
- description: HMAC signing secret (shown once at creation)
+ description: HMAC signing secret (nawa_wh_xxx format, shown once at creation)
active:
type: boolean
failure_count:
type: integer
+ last_delivery_at:
+ type: string
+ format: date-time
+ nullable: true
+ disabled_at:
+ type: string
+ format: date-time
+ nullable: true
created_at:
type: string
format: date-time
diff --git a/quickstart.mdx b/quickstart.mdx
index 8d1384c..28173ec 100644
--- a/quickstart.mdx
+++ b/quickstart.mdx
@@ -1,7 +1,7 @@
---
title: "Quickstart"
sidebarTitle: "Quickstart"
-description: "First API call in 3 minutes. Classify Arabic comments with a single request - no credit card required."
+description: "First API call in 3 minutes. Classify Arabic comments with a single request -- no credit card required."
---
# First API call in 3 minutes
@@ -10,10 +10,10 @@ Get up and running with the NAWA classification API in three steps.
- Go to [trynawa.com/developers/keys](https://trynawa.com/developers/keys) to create your free API key. Sign in with Google or email - no credit card required.
+ Go to [trynawa.com/developers/keys](https://trynawa.com/developers/keys) to create your free API key. Sign in with Google or email -- no credit card required.
- Free keys (`nawa_test_sk_xxx`) require no credit card. Limited to 100 lifetime requests and 14-day expiry. They use real AI models - same accuracy as paid keys.
+ Free keys (`nawa_test_sk_xxx`) require no credit card. Limited to 100 lifetime requests and 14-day expiry. They use real AI models -- same accuracy as paid keys.
@@ -27,8 +27,7 @@ Get up and running with the NAWA classification API in three steps.
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
- "text": "متى الجزء الثاني؟",
- "platform": "youtube"
+ "text": "متى الجزء الثاني؟"
}'
```
@@ -38,8 +37,7 @@ Get up and running with the NAWA classification API in three steps.
const nawa = new Nawa({ apiKey: 'nawa_test_sk_xxx' })
const { data, error } = await nawa.classify({
- text: 'متى الجزء الثاني؟',
- platform: 'youtube'
+ text: 'متى الجزء الثاني؟'
})
if (error) {
@@ -55,8 +53,7 @@ Get up and running with the NAWA classification API in three steps.
nawa = Nawa(api_key="nawa_test_sk_xxx")
result = nawa.classify(
- text="متى الجزء الثاني؟",
- platform="youtube"
+ text="متى الجزء الثاني؟"
)
if result.error:
@@ -75,37 +72,43 @@ Get up and running with the NAWA classification API in three steps.
{
"success": true,
"result": {
- "text": "متى الجزء الثاني؟",
- "intent": "question",
- "intent_confidence": 0.97,
+ "id": "cls_a1b2c3d4e5f6",
+ "object": "classification",
+ "intent": ["question"],
"sentiment": "neutral",
- "sentiment_confidence": 0.91,
+ "language": "ar",
"dialect": "gulf",
"dialect_confidence": 0.95,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "requires_response": true,
+ "priority": "high",
+ "suggested_reply": {
+ "text": "سؤال من متابع عن موعد الجزء الثاني",
+ "direction": "rtl"
+ },
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "tokens_used": null,
+ "cost_usd": 0.006,
+ "credits_used": 6
},
"errors": [],
- "request_id": "req_abc123def456"
+ "request_id": "req_nw_a1b2c3d4e5f67890"
}
```
- **Response fields:**
+ **Key fields:**
| Field | Type | Description |
|-------|------|-------------|
- | `intent` | string | One of: `question`, `complaint`, `praise`, `suggestion`, `spam`, `other` |
- | `intent_confidence` | number | Confidence score between 0 and 1 |
- | `sentiment` | string | One of: `positive`, `negative`, `neutral`, `mixed` |
- | `dialect` | string | One of: `gulf`, `egyptian`, `levantine`, `msa` |
- | `dialect_confidence` | number | Confidence score between 0 and 1 |
- | `toxicity` | string | One of: `none`, `mild`, `moderate`, `severe` |
- | `categories` | array | Content categories: `engagement`, `support`, `feedback`, `spam` |
- | `cached` | boolean | `true` if served from semantic cache (no cost) |
+ | `intent` | string[] | Detected intents (e.g. `question`, `praise`, `complaint`) |
+ | `sentiment` | string | `positive`, `negative`, `neutral`, or `mixed` |
+ | `dialect` | string \| null | Arabic dialect: `gulf`, `egyptian`, `levantine`, `msa`. Null for English. |
+ | `requires_response` | boolean | Whether the comment needs a reply |
+ | `priority` | string | `high`, `medium`, or `low` |
+ | `provider` | string | AI provider used (`allam` or `claude`) |
+ | `cost_usd` | number | Cost in USD for this request |
+ | `credits_used` | integer | Credits deducted from your balance |
@@ -118,7 +121,7 @@ Get up and running with the NAWA classification API in three steps.
Explore all endpoints, parameters, and response schemas.
-
+
Deep dive into Gulf, Egyptian, Levantine, and MSA classification.
diff --git a/rate-limits.mdx b/rate-limits.mdx
index 26c3d15..661fbba 100644
--- a/rate-limits.mdx
+++ b/rate-limits.mdx
@@ -10,13 +10,14 @@ NAWA enforces per-minute rate limits on each API key to ensure fair usage and pl
| Tier | Requests/min | How you get it |
|------|-------------|----------------|
-| **Free** | 10 | Free keys (`nawa_test_sk_`) |
-| **Growth** | 120 | Live keys (`nawa_live_sk_`) with credits |
+| **Sandbox** | 10 | Free keys (`nawa_test_sk_`). 100 lifetime request cap. |
+| **Trial** | 60 | Entry-level live keys |
+| **Growth** | 120 | Live keys (`nawa_live_sk_`) after first credit purchase |
| **Enterprise** | 300 | Contact [sales@trynawa.com](mailto:sales@trynawa.com) |
| **Enterprise+** | 1,000 | Contact [sales@trynawa.com](mailto:sales@trynawa.com) |
- Free keys are rate-limited to 10 requests/minute and have a hard cap of 100 lifetime requests. Live keys start at the Growth tier (120/min). Enterprise tiers are available on request -- contact [sales@trynawa.com](mailto:sales@trynawa.com).
+ Free keys are rate-limited to 10 requests/minute and have a hard cap of 100 lifetime requests across all free keys. Live keys start at the Growth tier (120/min). Enterprise tiers are available on request -- contact [sales@trynawa.com](mailto:sales@trynawa.com).
## Rate limit headers
diff --git a/webhooks.mdx b/webhooks.mdx
index 2ef4922..7c89423 100644
--- a/webhooks.mdx
+++ b/webhooks.mdx
@@ -12,11 +12,13 @@ Receive real-time notifications when events occur in your NAWA account. Webhooks
|-------|-------------|---------|
| `classification.completed` | A classification request succeeded | After `/v1/classify` completes |
| `classification.failed` | A classification request failed | On provider or internal errors |
-| `comment.new` | A new comment was ingested | When a connected platform receives a comment |
-| `comment.replied` | A reply was posted | After `/v1/comments/:id/reply` succeeds |
| `credits.low` | Credit balance below threshold | Balance drops below $5 |
| `credits.exhausted` | Credit balance is $0 | Balance reaches $0 |
+
+ You can register up to 10 webhook endpoints per account. All webhook URLs must use HTTPS.
+
+
## Webhook payload
```json