From bdc0dc8f19d25621df1feb83c2efd801061417f7 Mon Sep 17 00:00:00 2001
From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com>
Date: Mon, 13 Apr 2026 19:42:50 +0000
Subject: [PATCH] fix: align all API docs with edge function implementations
Audited every Supabase edge function against its documentation page and
fixed critical discrepancies that would cause developer integration failures:
- classify: removed invented params (platform, channel_id, metadata), added
actual response fields (id, object, requires_response, priority,
suggested_reply, provider, fallback_used, cost_usd, credits_used),
documented provider query param for A/B testing
- comments/reply: corrected path from /comments/{id}/reply to /comments/reply,
added required text body param, fixed response from flat to nested
classification+reply structure, corrected default tone to professional
- feedback: replaced incorrect request_id/field/expected_value schema with
actual classification_id/rating/corrected_intent/corrected_sentiment schema
- comments-list: switched from cursor to offset pagination, removed invented
params (channel_id, toxicity, from, to)
- analytics: fixed group_by values from day/week/month to endpoint/day/provider,
replaced content analytics response with actual operational metrics response
- health: corrected response to show actual service checks (database, redis,
nagl) with latency_ms, noted flat JSON (no envelope)
- usage: fixed group_by to endpoint/day, corrected response to nested
usage object with dynamic breakdown key
- rubric-classify: fixed path to /rubric-classify, updated rubric.categories
to object array with name/description, added multi_label and
confidence_threshold params
- translate: removed invented metadata param, added actual response fields
(id, object, tokens_used)
- moderate: verified implementation matches docs, added response headers
- report: marked as private beta (no edge function exists yet)
- billing: fixed endpoint paths to match actual routes
- webhooks: removed unimplemented event types (comment.new, comment.replied)
- openapi.yaml: aligned all schemas, paths, params, and examples with code
Generated-By: mintlify-agent
---
api-reference/analytics.mdx | 143 ++++++---
api-reference/classify.mdx | 294 ++++++++----------
api-reference/comments-list.mdx | 73 +++--
api-reference/comments-reply.mdx | 140 +++++++--
api-reference/detect.mdx | 8 +-
api-reference/feedback.mdx | 78 +++--
api-reference/health.mdx | 83 +++--
api-reference/moderate.mdx | 27 +-
api-reference/report.mdx | 4 +
api-reference/rubric-classify.mdx | 178 +++++++----
api-reference/translate.mdx | 129 ++++----
api-reference/usage.mdx | 144 ++++++---
billing.mdx | 8 +-
openapi.yaml | 486 +++++++++++++++++-------------
webhooks.mdx | 2 -
15 files changed, 1104 insertions(+), 693 deletions(-)
diff --git a/api-reference/analytics.mdx b/api-reference/analytics.mdx
index c2879c7..1dd993a 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 usage analytics including request counts, costs, cache rates, and latency."
api: "GET https://api.trynawa.com/v1/analytics"
---
-Retrieve aggregated analytics across your classified comments. This endpoint is **free**.
+Retrieve aggregated analytics for your API usage. Returns request counts, costs, cache hit rates, latency, success rates, and breakdowns grouped by endpoint, day, or provider. This endpoint is **free**.
## Request
@@ -13,70 +13,125 @@ 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`. |
+| `from` | string | No | Start date (ISO 8601). Filters usage logs from this date. |
+| `to` | string | No | End date (ISO 8601). Filters usage logs up to this date. |
+| `group_by` | string | No | Group breakdown by: `endpoint`, `day`, `provider`. Default: `endpoint`. |
### Example request
-```bash
-curl "https://api.trynawa.com/v1/analytics?platform=youtube&from=2025-01-01T00:00:00Z&to=2025-01-31T23:59:59Z&group_by=week" \
+
+
+```bash cURL
+curl "https://api.trynawa.com/v1/analytics?from=2025-01-01T00:00:00Z&to=2025-01-31T23:59:59Z&group_by=day" \
-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'
+})
+
+console.log(data.analytics.total_requests)
+console.log(data.analytics.cache_hit_rate)
+```
+
+```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"
+)
+
+print(result.data.analytics["total_requests"])
+print(result.data.analytics["cache_hit_rate"])
+```
+
+
+
## Response
### Success response (200)
+The breakdown key name is dynamic based on your `group_by` value: `by_endpoint`, `by_day`, or `by_provider`.
+
```json
{
"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
+ "analytics": {
+ "total_requests": 4521,
+ "total_cost": 27.126,
+ "cache_hits": 1040,
+ "cache_hit_rate": 0.23,
+ "avg_latency_ms": 820,
+ "success_rate": 0.99,
+ "error_count": 45,
+ "endpoint_distribution": {
+ "/v1/classify": 3200,
+ "/v1/comments/reply": 800,
+ "/v1/detect": 521
},
- "sentiment": {
- "positive": 2100,
- "negative": 890,
- "neutral": 1231,
- "mixed": 300
+ "provider_distribution": {
+ "allam": 2800,
+ "claude": 1721
},
- "dialect": {
- "gulf": 2015,
- "egyptian": 1302,
- "levantine": 789,
- "msa": 415
- },
- "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": [
+ {
+ "endpoint": "/v1/classify",
+ "requests": 3200,
+ "cost": 19.2,
+ "avg_latency_ms": 750
+ },
{
- "period": "2025-01-06/2025-01-12",
- "total": 1123,
- "sentiment_score": 0.72,
- "top_intent": "praise"
+ "endpoint": "/v1/comments/reply",
+ "requests": 800,
+ "cost": 6.4,
+ "avg_latency_ms": 1100
}
]
},
"errors": [],
- "request_id": "req_ana_abc123"
+ "request_id": "req_nw_ana_abc123"
}
```
+
+### Analytics 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 hit requests |
+| `analytics.cache_hit_rate` | number | Cache hit rate (0-1) |
+| `analytics.avg_latency_ms` | integer | Average response latency in milliseconds |
+| `analytics.success_rate` | number | Success rate (0-1) |
+| `analytics.error_count` | integer | Total error responses |
+| `analytics.endpoint_distribution` | object | Request count per endpoint |
+| `analytics.provider_distribution` | object | Request count per AI provider |
+| `analytics.period` | object | `from` and `to` date bounds applied |
+
+### Breakdown fields
+
+Each item in the `by_endpoint`, `by_day`, or `by_provider` array contains:
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `endpoint` / `day` / `provider` | string | The grouping key |
+| `requests` | integer | Request count for this group |
+| `cost` | number | Cost in USD for this group |
+| `avg_latency_ms` | integer | Average latency for this group |
diff --git a/api-reference/classify.mdx b/api-reference/classify.mdx
index ac3d730..5821e8b 100644
--- a/api-reference/classify.mdx
+++ b/api-reference/classify.mdx
@@ -1,16 +1,18 @@
---
-title: "Classify Comment"
+title: "Classify comment"
sidebarTitle: "POST /v1/classify"
-description: "Classify an Arabic or English comment by intent, sentiment, dialect, and toxicity in a single API call."
+description: "Classify an Arabic or English comment by intent, sentiment, dialect, and priority in a single API call."
api: "POST https://api.trynawa.com/v1/classify"
---
-Classify any comment with a single request. Returns intent, sentiment, dialect, and toxicity analysis. Arabic comments are routed to HUMAIN's ALLaM model for dialect detection. English comments are routed to Claude for high-accuracy classification.
+Classify any comment with a single request. Returns intent, sentiment, dialect detection, priority scoring, and a suggested reply direction. Arabic comments are routed to HUMAIN's ALLaM model for dialect-aware classification. English comments are routed to Claude.
+
+All successful responses are wrapped in the standard envelope: `{ success: true, result: { ... }, errors: [], request_id: "..." }`.
## 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
@@ -24,10 +26,14 @@ 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. |
+| `text` | string | Yes | The comment text to classify. |
+| `context` | object | No | Optional context object for additional classification hints. |
+
+### Query parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `provider` | string | No | Override the AI provider for A/B testing: `claude`, `gemini`, `allam`. By default, NAWA routes Arabic to ALLaM and English to Claude. |
### Example request
@@ -38,9 +44,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,10 +54,14 @@ 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: 'متى الجزء الثاني؟'
})
+
+if (data) {
+ console.log(data.intent) // "question"
+ console.log(data.requires_response) // true
+ console.log(data.suggested_reply) // { text: "...", direction: "rtl" }
+}
```
```python Python
@@ -61,11 +69,11 @@ from nawa import Nawa
nawa = Nawa(api_key="your_api_key")
-result = nawa.classify(
- text="متى الجزء الثاني؟",
- platform="youtube",
- channel_id="ch_123"
-)
+result = nawa.classify(text="متى الجزء الثاني؟")
+
+print(result.data.intent) # "question"
+print(result.data.requires_response) # True
+print(result.data.suggested_reply) # {"text": "...", "direction": "rtl"}
```
@@ -78,22 +86,28 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "متى الجزء الثاني؟",
+ "id": "cls_nw_a1b2c3d4e5f6",
+ "object": "classification",
"intent": "question",
- "intent_confidence": 0.97,
"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_abc123def456"
}
```
@@ -102,50 +116,54 @@ result = nawa.classify(
| Header | Description |
|--------|-------------|
| `X-Request-Id` | Unique request identifier for debugging |
+| `X-NAWA-Cache` | `HIT` if served from semantic cache (no cost), `MISS` otherwise |
+| `X-NAWA-Provider` | AI provider used for this request (`allam`, `claude`, `gemini`) |
+| `X-NAWA-Latency` | Processing time in milliseconds |
+| `X-NAWA-Calibration-Version` | Calibration threshold version applied to this result |
| `X-RateLimit-Limit` | Rate limit ceiling for current window |
| `X-RateLimit-Remaining` | Requests remaining in current window |
| `X-RateLimit-Reset` | Window reset time (RFC 3339) |
-| `X-NAWA-Balance` | Current credit balance in USD |
-| `X-NAWA-Balance-Warning` | `low_balance` when below $5 |
-| `X-NAWA-Cache` | `HIT` if served from semantic cache (no cost) |
### 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 | Unique classification identifier (`cls_nw_xxx`) |
+| `object` | string | Always `"classification"` |
+| `intent` | string | Detected intent: `question`, `complaint`, `praise`, `suggestion`, `spam`, `other` |
| `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 code: `ar`, `en`, or `mixed` |
+| `dialect` | string or null | Arabic dialect: `gulf`, `egyptian`, `levantine`, `msa`. Returns `null` for English text. |
+| `dialect_confidence` | number or null | Confidence score (0-1) for dialect. Returns `null` for English text. |
+| `requires_response` | boolean | Whether this comment warrants a reply |
+| `priority` | string | Priority level for response triage |
+| `suggested_reply` | object | Contains `text` (suggested reply) and `direction` (`rtl` or `ltr`) |
+| `provider` | string | AI provider used: `allam`, `claude`, or `gemini` |
+| `model` | string | Specific model identifier used |
+| `fallback_used` | boolean | Whether a fallback provider was used due to primary failure |
+| `tokens_used` | null | Reserved for future use |
+| `cost_usd` | number | Cost of this request in USD |
+| `credits_used` | number | Credits deducted for this request |
### Error responses
| Status | Type | When |
|--------|------|------|
-| 400 | `invalid_request_error` | Missing `text`, invalid `platform`, text too long |
+| 400 | `invalid_request_error` | Missing `text`, invalid `provider`, invalid JSON body |
| 401 | `authentication_error` | Invalid or missing API key |
-| 402 | `insufficient_credits` | No credits remaining |
+| 402 | `insufficient_credits` | No credits remaining (live keys only) |
| 429 | `rate_limit_error` | Rate limit exceeded |
| 500 | `api_error` | Internal or provider error |
### 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 +171,38 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "This is hands down the best review I have seen on this phone. Subscribed!",
+ "id": "cls_nw_en001abc002d",
+ "object": "classification",
"intent": "praise",
- "intent_confidence": 0.96,
"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": "",
+ "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 +210,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.",
+ "id": "cls_nw_en002def003g",
+ "object": "classification",
"intent": "complaint",
- "intent_confidence": 0.94,
"sentiment": "negative",
- "sentiment_confidence": 0.96,
- "dialect": null,
- "dialect_confidence": null,
- "toxicity": "none",
- "toxicity_confidence": 0.97,
- "categories": ["feedback"],
"language": "en",
- "model": "claude-v1",
- "cached": false
- },
- "errors": [],
- "request_id": "req_en_complaint_001"
- }
- ```
-
-
-
- ```bash
- curl -X POST https://api.trynawa.com/v1/classify \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "What camera and lens setup are you using for these shots?", "platform": "youtube"}'
- ```
-
- Response:
- ```json
- {
- "success": true,
- "result": {
- "text": "What camera and lens setup are you using for these shots?",
- "intent": "question",
- "intent_confidence": 0.97,
- "sentiment": "neutral",
- "sentiment_confidence": 0.92,
"dialect": null,
"dialect_confidence": null,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "en",
- "model": "claude-v1",
- "cached": false
+ "requires_response": true,
+ "priority": "high",
+ "suggested_reply": {
+ "text": "Sorry about the audio issues! We will fix this in the next upload.",
+ "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,89 +249,40 @@ result = nawa.classify(
{
"success": true,
"result": {
- "text": "ما شاء الله عليك، محتوى رهيب!",
+ "id": "cls_nw_ar001ghi004j",
+ "object": "classification",
"intent": "praise",
- "intent_confidence": 0.98,
"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"}'
- ```
-
- Response:
- ```json
- {
- "success": true,
- "result": {
- "text": "الصوت وحش أوي في الفيديو ده",
- "intent": "complaint",
- "intent_confidence": 0.94,
- "sentiment": "negative",
- "sentiment_confidence": 0.96,
- "dialect": "egyptian",
- "dialect_confidence": 0.97,
- "toxicity": "none",
- "toxicity_confidence": 0.95,
- "categories": ["feedback"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
- },
- "errors": [],
- "request_id": "req_mno345pqr678"
- }
- ```
-
+
+ Force classification through a specific provider using the `provider` query parameter:
-
```bash
- curl -X POST https://api.trynawa.com/v1/classify \
+ curl -X POST "https://api.trynawa.com/v1/classify?provider=claude" \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
- -d '{"text": "لو سمحت حاول تحكي عن المطاعم بلبنان", "platform": "instagram"}'
- ```
-
- Response:
- ```json
- {
- "success": true,
- "result": {
- "text": "لو سمحت حاول تحكي عن المطاعم بلبنان",
- "intent": "suggestion",
- "intent_confidence": 0.92,
- "sentiment": "neutral",
- "sentiment_confidence": 0.88,
- "dialect": "levantine",
- "dialect_confidence": 0.96,
- "toxicity": "none",
- "toxicity_confidence": 0.99,
- "categories": ["engagement"],
- "language": "ar",
- "model": "nagl-v1",
- "cached": false
- },
- "errors": [],
- "request_id": "req_stu901vwx234"
- }
+ -d '{"text": "ما شاء الله عليك، محتوى رهيب!"}'
```
diff --git a/api-reference/comments-list.mdx b/api-reference/comments-list.mdx
index 1e18d08..1d702b1 100644
--- a/api-reference/comments-list.mdx
+++ b/api-reference/comments-list.mdx
@@ -1,11 +1,15 @@
---
-title: "List Comments"
+title: "List comments"
sidebarTitle: "GET /v1/comments"
-description: "Retrieve and filter classified comments with pagination."
+description: "Retrieve and filter previously classified comments with offset 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 ordered by `created_at` (newest first). This endpoint is **free**.
+
+
+ Comment history storage is being rolled out. If no classifications have been stored yet, this endpoint returns an empty array with a helpful message.
+
## Request
@@ -13,27 +17,27 @@ 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',
@@ -47,6 +51,10 @@ for await (const comment of nawa.comments.list({ platform: 'youtube' })) {
```
```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
result = nawa.comments.list(
platform="youtube",
intent="question",
@@ -70,24 +78,53 @@ for comment in nawa.comments.list(platform="youtube"):
"result": {
"comments": [
{
- "id": "cmt_abc123",
+ "id": "abc123",
+ "request_id": "req_nw_xyz789",
"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": 10,
+ "offset": 0,
+ "has_more": true
}
},
"errors": [],
- "request_id": "req_lst_abc123"
+ "request_id": "req_nw_lst_abc123"
}
```
+
+### Comment fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Comment record identifier |
+| `request_id` | string | The original classification request ID |
+| `text` | string | The classified comment text |
+| `platform` | string or null | Source platform |
+| `intent` | string | Detected intent |
+| `sentiment` | string | Detected sentiment |
+| `dialect` | string or null | Detected dialect |
+| `created_at` | string | ISO 8601 timestamp |
+
+### Pagination fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `total` | integer | Total matching comments |
+| `limit` | integer | Page size used |
+| `offset` | integer | Current offset |
+| `has_more` | boolean | Whether more results exist beyond this page |
+
+To fetch the next page, increment `offset` by `limit`:
+
+```bash
+curl "https://api.trynawa.com/v1/comments?platform=youtube&limit=10&offset=10" \
+ -H "Authorization: Bearer nawa_test_sk_xxx"
+```
diff --git a/api-reference/comments-reply.mdx b/api-reference/comments-reply.mdx
index d6321d7..1fda1fa 100644
--- a/api-reference/comments-reply.mdx
+++ b/api-reference/comments-reply.mdx
@@ -1,60 +1,98 @@
---
-title: "Reply to Comment"
-sidebarTitle: "POST /v1/comments/:id/reply"
-description: "Generate a context-aware reply to a comment in Arabic or English."
-api: "POST https://api.trynawa.com/v1/comments/{id}/reply"
+title: "Reply to comment"
+sidebarTitle: "POST /v1/comments/reply"
+description: "Classify a comment and generate a context-aware reply in 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 a single call. 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 from the input text.
- 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
+### Headers
+
+| Header | Required | Description |
+|--------|----------|-------------|
+| `Authorization` | Yes | `Bearer nawa_live_sk_xxx` or `Bearer nawa_test_sk_xxx` |
+| `Content-Type` | Yes | `application/json` |
+
+### 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`, `casual`, `formal`. Default: `professional`. |
+| `max_length` | integer | No | Maximum reply length in characters (1-2000). Default: 500. |
+| `context` | object | No | Additional context. Accepts `platform` (string) and `brand_voice` (string) fields. |
-### 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 | Override the AI provider: `claude`, `gemini`, `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": 300,
+ "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'
+ maxLength: 300,
+ context: {
+ platform: 'youtube',
+ brandVoice: 'Tech review channel focused on smartphones'
+ }
})
+
+if (data) {
+ console.log(data.reply.text) // The generated reply
+ console.log(data.classification.intent) // "question"
+ console.log(data.classification.sentiment) // "neutral"
+}
```
```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"
+ max_length=300,
+ context={
+ "platform": "youtube",
+ "brand_voice": "Tech review channel focused on smartphones"
+ }
)
+
+print(result.data.reply["text"])
+print(result.data.classification["intent"])
```
@@ -67,25 +105,63 @@ result = nawa.comments.reply(
{
"success": true,
"result": {
- "comment_id": "cmt_abc123",
- "reply_text": "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔",
- "reply_dialect": "gulf",
- "tone": "friendly",
- "original_intent": "question",
- "original_dialect": "gulf"
+ "id": "rpl_nw_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"
}
```
+### Response headers
+
+| Header | Description |
+|--------|-------------|
+| `X-Request-Id` | Unique request identifier |
+| `X-NAWA-Provider` | AI provider used |
+| `X-NAWA-Latency` | Processing time in milliseconds |
+
### Result fields
| Field | Type | Description |
|-------|------|-------------|
-| `comment_id` | string | The comment that was replied to |
-| `reply_text` | string | The generated reply text |
-| `reply_dialect` | string \| null | Dialect used in the reply (matches original). `null` for English replies. |
-| `tone` | string | The tone used for the reply |
-| `original_intent` | string | Detected intent of the original comment |
-| `original_dialect` | string | Detected dialect of the original comment |
+| `id` | string | Unique reply identifier (`rpl_nw_xxx`) |
+| `object` | string | Always `"comment_reply"` |
+| `classification` | object | Classification results for the input comment |
+| `classification.intent` | string | Detected intent: `question`, `complaint`, `praise`, `suggestion`, `spam`, `other` |
+| `classification.sentiment` | string | `positive`, `negative`, `neutral`, `mixed` |
+| `classification.priority` | string | Priority level for response triage |
+| `classification.requires_response` | boolean | Whether this comment warrants a reply |
+| `reply` | object | The generated reply |
+| `reply.text` | string | Generated reply text |
+| `reply.direction` | string | Text direction: `rtl` or `ltr` |
+| `reply.tone` | string | Tone used for the reply |
+| `provider` | string | AI provider used |
+| `model` | string | Model identifier used |
+| `cost_usd` | number | Cost in USD |
+| `credits_used` | number | Credits deducted |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `text`, invalid `tone`, `max_length` out of range, invalid JSON |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 402 | `insufficient_credits` | No credits remaining |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal or provider error |
diff --git a/api-reference/detect.mdx b/api-reference/detect.mdx
index 2169b2c..3508762 100644
--- a/api-reference/detect.mdx
+++ b/api-reference/detect.mdx
@@ -1,11 +1,11 @@
---
-title: "Detect Language & Dialect"
+title: "Detect language and dialect"
sidebarTitle: "POST /v1/detect"
-description: "Lightweight language and dialect detection. Returns in under 100ms -- no AI model call required."
+description: "Lightweight language and dialect detection. Returns in under 100ms with no AI model call required."
api: "POST https://api.trynawa.com/v1/detect"
---
-Detect language, dialect, script, and text direction from any text input. Uses local NAGL modules only -- no external AI call, so responses return in under 100ms.
+Detect language, dialect, script, and text direction from any text input. Uses local NAGL modules only with no external AI call, so responses return in under 100ms.
Cost: **$0.002** per request (2 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
@@ -86,7 +86,7 @@ result = nawa.detect(text="وش رايكم بالمحتوى الجديد؟")
| `text` | string | The original input text |
| `language` | string | Detected language: `ar`, `en`, or `mixed` |
| `dialect` | string or null | Detected Arabic dialect: `gulf`, `egyptian`, `levantine`, `msa`. Null if not Arabic. |
-| `dialect_confidence` | number or null | Confidence score 0--1 for dialect detection. Null if not Arabic. |
+| `dialect_confidence` | number or null | Confidence score 0-1 for dialect detection. Null if not Arabic. |
| `script` | string | Script type: `arabic`, `latin`, or `mixed` |
| `text_direction` | string | Text direction: `rtl` or `ltr` |
| `word_count` | number | Number of words in the input text |
diff --git a/api-reference/feedback.mdx b/api-reference/feedback.mdx
index 4cddf8b..53deda5 100644
--- a/api-reference/feedback.mdx
+++ b/api-reference/feedback.mdx
@@ -1,14 +1,14 @@
---
-title: "Submit Feedback"
+title: "Submit feedback"
sidebarTitle: "POST /v1/feedback"
-description: "Submit RLHF feedback to improve classification accuracy over time."
+description: "Submit RLHF feedback on a classification to improve accuracy over time."
api: "POST https://api.trynawa.com/v1/feedback"
---
-Submit reinforcement learning from human feedback (RLHF) to continuously improve NAWA's classification accuracy.
+Submit reinforcement learning from human feedback (RLHF) to continuously improve NAWA classification accuracy. Reference a specific classification by its `id` and rate whether it was correct.
- This endpoint is **free** -- 0 credits, no cost.
+ This endpoint is **free** - 0 credits, no cost.
@@ -21,10 +21,11 @@ Submit reinforcement learning from human feedback (RLHF) to continuously improve
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `request_id` | string | Yes | The `request_id` from the original classification response. |
-| `field` | string | Yes | The field to correct: `intent`, `sentiment`, `dialect`, `toxicity`, `category`. |
-| `expected_value` | string | Yes | The correct value the model should have returned. |
-| `comment` | string | No | Optional free-text explanation of why this correction is needed. |
+| `classification_id` | string | Yes | The `id` from the classification response (e.g. `cls_nw_a1b2c3d4e5f6`). |
+| `rating` | string | Yes | Your assessment: `correct`, `incorrect`, `partial`. |
+| `corrected_intent` | string[] | No | Array of correct intents if the model got it wrong (e.g. `["question", "suggestion"]`). |
+| `corrected_sentiment` | string | No | Correct sentiment: `positive`, `negative`, `neutral`, `mixed`. |
+| `comment` | string | No | Free-text explanation of why this correction is needed. |
### Example request
@@ -35,28 +36,36 @@ curl -X POST https://api.trynawa.com/v1/feedback \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
- "request_id": "req_abc123def456",
- "field": "dialect",
- "expected_value": "levantine",
- "comment": "This is Lebanese Arabic, not Gulf"
+ "classification_id": "cls_nw_a1b2c3d4e5f6",
+ "rating": "incorrect",
+ "corrected_sentiment": "negative",
+ "comment": "This was sarcasm, not genuine 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_nw_a1b2c3d4e5f6',
+ rating: 'incorrect',
+ correctedSentiment: 'negative',
+ comment: 'This was sarcasm, not genuine 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_nw_a1b2c3d4e5f6",
+ rating="incorrect",
+ corrected_sentiment="negative",
+ comment="This was sarcasm, not genuine praise"
)
```
@@ -70,11 +79,32 @@ result = nawa.feedback.submit(
{
"success": true,
"result": {
- "feedback_id": "fb_xyz789",
- "status": "accepted",
- "message": "Thank you! Your feedback helps improve NAWA's accuracy."
+ "id": "fb_nw_x1y2z3w4v5u6",
+ "object": "feedback",
+ "classification_id": "cls_nw_a1b2c3d4e5f6",
+ "rating": "incorrect",
+ "acknowledged": true
},
"errors": [],
- "request_id": "req_fb_abc123"
+ "request_id": "req_nw_fb_abc123"
}
```
+
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Unique feedback identifier (`fb_nw_xxx`) |
+| `object` | string | Always `"feedback"` |
+| `classification_id` | string | The classification this feedback refers to |
+| `rating` | string | The rating you submitted |
+| `acknowledged` | boolean | Always `true` on success |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `classification_id` or `rating`, invalid `rating` value, invalid `corrected_sentiment` |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal error |
diff --git a/api-reference/health.mdx b/api-reference/health.mdx
index ca110e8..7dea92d 100644
--- a/api-reference/health.mdx
+++ b/api-reference/health.mdx
@@ -1,11 +1,15 @@
---
-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"
---
-Check the operational status of the NAWA API. This endpoint is **free** and does **not** require authentication.
+Check the operational status of the NAWA API and its backing services. This endpoint is **free** and does **not** require authentication.
+
+
+ Unlike other endpoints, the health check does **not** use the standard `{ success, result, errors, request_id }` envelope. It returns a flat JSON object directly.
+
## Request
@@ -17,46 +21,71 @@ curl https://api.trynawa.com/v1/health
### Healthy response (200)
+When all services are operational, the endpoint returns HTTP 200:
+
```json
{
- "success": true,
- "result": {
- "status": "healthy",
- "version": "1.0.0",
- "services": {
- "api": "operational",
- "classification": "operational",
- "database": "operational",
- "cache": "operational"
+ "status": "healthy",
+ "version": "v1",
+ "services": {
+ "database": {
+ "status": "healthy",
+ "latency_ms": 12
+ },
+ "redis": {
+ "status": "healthy",
+ "latency_ms": 3
},
- "timestamp": "2025-01-15T12:00:00Z"
+ "nagl": {
+ "status": "healthy"
+ }
},
- "errors": [],
- "request_id": "req_hlt_abc123"
+ "timestamp": "2025-01-15T12:00:00.000Z",
+ "latency_ms": 15
}
```
-### Degraded response (200)
+### Degraded response (503)
+
+When one or more services are down or degraded, the endpoint returns HTTP 503:
```json
{
- "success": true,
- "result": {
- "status": "degraded",
- "version": "1.0.0",
- "services": {
- "api": "operational",
- "classification": "degraded",
- "database": "operational",
- "cache": "operational"
+ "status": "degraded",
+ "version": "v1",
+ "services": {
+ "database": {
+ "status": "healthy",
+ "latency_ms": 14
+ },
+ "redis": {
+ "status": "down",
+ "latency_ms": 5002
},
- "timestamp": "2025-01-15T12:00:00Z"
+ "nagl": {
+ "status": "healthy"
+ }
},
- "errors": [],
- "request_id": "req_hlt_def456"
+ "timestamp": "2025-01-15T12:00:00.000Z",
+ "latency_ms": 5010
}
```
+### Response fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `status` | string | Overall health: `healthy` or `degraded` |
+| `version` | string | API version (`v1`) |
+| `services` | object | Individual service statuses |
+| `services.database` | object | PostgreSQL connectivity. Contains `status` and `latency_ms`. |
+| `services.redis` | object | Redis cache connectivity. Contains `status` and `latency_ms`. |
+| `services.nagl` | object | NAGL classification engine. Contains `status`. |
+| `timestamp` | string | ISO 8601 timestamp of the check |
+| `latency_ms` | integer | Total health check duration in milliseconds |
+
+Each service status is one of: `healthy`, `degraded`, `down`.
+
For real-time status monitoring, visit [status.trynawa.com](https://status.trynawa.com).
diff --git a/api-reference/moderate.mdx b/api-reference/moderate.mdx
index 947d30e..5d475e9 100644
--- a/api-reference/moderate.mdx
+++ b/api-reference/moderate.mdx
@@ -1,11 +1,11 @@
---
-title: "Moderate Content"
+title: "Moderate content"
sidebarTitle: "POST /v1/moderate"
description: "Dialect-aware Arabic content moderation. Catches profanity and slang that Western APIs miss."
api: "POST https://api.trynawa.com/v1/moderate"
---
-Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity, slang, and offensive language differences across Gulf, Egyptian, Levantine, and MSA Arabic -- catching what Western moderation APIs miss entirely.
+Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity, slang, and offensive language differences across Gulf, Egyptian, Levantine, and MSA Arabic, catching what Western moderation APIs miss entirely.
Cost: **$0.004** per request (4 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
@@ -30,9 +30,9 @@ Dialect-aware Arabic content moderation powered by ALLaM. Understands profanity,
| `platform` | string | No | Source platform: `youtube`, `instagram`, `twitter`, `facebook`. |
**Strictness levels:**
-- **low** -- only flag severe content (explicit hate speech, extreme profanity, direct threats)
-- **medium** -- flag moderate and severe content (profanity, harassment, hate speech, spam)
-- **high** -- flag all potentially problematic content (mild profanity, borderline content, subtle insults)
+- **low** - only flag severe content (explicit hate speech, extreme profanity, direct threats)
+- **medium** - flag moderate and severe content (profanity, harassment, hate speech, spam)
+- **high** - flag all potentially problematic content (mild profanity, borderline content, subtle insults)
### Example request
@@ -180,20 +180,31 @@ When content is flagged, the response includes specific flags, severity scores,
}
```
+### Response headers
+
+| Header | Description |
+|--------|-------------|
+| `X-Request-Id` | Unique request identifier |
+| `X-NAWA-Cache` | `HIT` if served from cache (no cost), `MISS` otherwise |
+| `X-NAWA-Provider` | AI provider used (`allam` or `claude`) |
+| `X-NAWA-Latency` | Processing time in milliseconds |
+
### Result fields
| Field | Type | Description |
|-------|------|-------------|
+| `id` | string | Unique moderation identifier (`mod_nw_xxx`) |
+| `object` | string | Always `"moderation"` |
| `text` | string | The original input text |
| `is_safe` | boolean | Whether the content passed moderation |
| `verdict` | string | `safe` or `flagged` |
| `flags` | array | List of triggered category names |
| `severity` | string | Overall severity: `none`, `low`, `medium`, `high` |
-| `severity_score` | number | Severity score 0--1 |
+| `severity_score` | number | Severity score 0-1 |
| `language` | string | Detected language: `ar`, `en`, or `mixed` |
| `dialect` | string or null | Detected Arabic dialect. Null if not Arabic. |
| `categories` | object | Boolean flags for each moderation category |
-| `category_scores` | object | Confidence scores 0--1 for each category |
+| `category_scores` | object | Confidence scores 0-1 for each category |
| `flagged_terms` | array | Specific problematic words/phrases found |
| `cleaned_text` | string or null | Text with flagged terms replaced by `***`. Null if safe. |
| `model` | string | Model version used |
@@ -265,7 +276,7 @@ curl -X POST https://api.trynawa.com/v1/moderate \
| Status | Type | When |
|--------|------|------|
-| 400 | `invalid_request_error` | Missing `text`. Invalid strictness or platform. Text too long. |
+| 400 | `invalid_request_error` | Missing `text`. Invalid strictness or platform. Text too long (max 5,000 chars). |
| 401 | `authentication_error` | Invalid or missing API key |
| 402 | `insufficient_credits` | No credits remaining |
| 429 | `rate_limit_error` | Rate limit exceeded |
diff --git a/api-reference/report.mdx b/api-reference/report.mdx
index 28fd10e..93af7f6 100644
--- a/api-reference/report.mdx
+++ b/api-reference/report.mdx
@@ -7,6 +7,10 @@ api: "POST https://api.trynawa.com/v1/report"
Generate a structured audience intelligence report from comment data. The report analyzes audience clusters, dominant narratives, sentiment patterns, spam/bot detection, and produces data-backed strategic recommendations.
+
+ This endpoint is currently in **private beta**. Contact [support@trynawa.com](mailto:support@trynawa.com) to request access. The API contract below is subject to change before general availability.
+
+
Cost: **$0.50** per Basic report (500 credits). **$1.50** per Pro report (1500 credits). Pro includes content brief, repeat commenter analysis, and engagement playbook.
diff --git a/api-reference/rubric-classify.mdx b/api-reference/rubric-classify.mdx
index 4489175..432a655 100644
--- a/api-reference/rubric-classify.mdx
+++ b/api-reference/rubric-classify.mdx
@@ -1,80 +1,101 @@
---
-title: "Rubric Classify"
-sidebarTitle: "POST /v1/rubric/classify"
-description: "Classify a comment against a custom rubric with predefined scoring criteria."
-api: "POST https://api.trynawa.com/v1/rubric/classify"
+title: "Rubric classify"
+sidebarTitle: "POST /v1/rubric-classify"
+description: "Classify text against a custom rubric with user-defined categories."
+api: "POST https://api.trynawa.com/v1/rubric-classify"
---
-Classify a comment against a custom rubric. Define your own categories and scoring criteria for domain-specific classification.
+Classify text against a custom rubric. Define your own 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). Semantic cache hits are free.
## Request
+### Headers
+
+| Header | Required | Description |
+|--------|----------|-------------|
+| `Authorization` | Yes | `Bearer nawa_live_sk_xxx` or `Bearer nawa_test_sk_xxx` |
+| `Content-Type` | Yes | `application/json` |
+
### Body parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `text` | string | Yes | The comment text to classify. Max 5,000 characters. |
-| `rubric` | object | Yes | The rubric definition with categories and criteria. |
-| `rubric.categories` | string[] | Yes | List of category names to classify against. |
-| `rubric.descriptions` | object | No | Category descriptions to guide the model. Keys are category names, values are description strings. |
-| `platform` | string | No | Source platform for context. |
+| `text` | string | Yes | The text to classify. |
+| `rubric` | object | Yes | The rubric definition. |
+| `rubric.categories` | array | Yes | Array of category objects. Each must have a `name` (string) and optional `description` (string). Max 20 categories. |
+| `rubric.multi_label` | boolean | No | Enable multi-label classification (return all categories above 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 | Override the AI provider: `claude`, `gemini`, `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 +109,51 @@ result = nawa.rubric.classify(
{
"success": true,
"result": {
- "text": "وين الترجمة العربية؟ مافهمت شي",
- "category": "translation_request",
- "category_confidence": 0.94,
- "scores": {
- "translation_request": 0.94,
- "technical_issue": 0.03,
- "content_feedback": 0.02,
- "off_topic": 0.01
- },
- "dialect": "gulf",
- "dialect_confidence": 0.91,
+ "id": "rcl_nw_a1b2c3d4e5f6",
+ "object": "rubric_classification",
+ "categories": [
+ {
+ "name": "translation_request",
+ "confidence": 0.94
+ }
+ ],
+ "multi_label": false,
+ "language": "ar",
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "cost_usd": 0.003,
+ "credits_used": 3
+ },
+ "errors": [],
+ "request_id": "req_nw_rub123abc456"
+}
+```
+
+### Multi-label response
+
+When `multi_label` is `true`, multiple categories can be returned:
+
+```json
+{
+ "success": true,
+ "result": {
+ "id": "rcl_nw_x7y8z9w0v1u2",
+ "object": "rubric_classification",
+ "categories": [
+ { "name": "translation_request", "confidence": 0.94 },
+ { "name": "content_feedback", "confidence": 0.62 }
+ ],
+ "multi_label": true,
"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_rub456def789"
}
```
@@ -112,10 +161,23 @@ result = nawa.rubric.classify(
| Field | Type | Description |
|-------|------|-------------|
-| `category` | string | The top matching category from your rubric |
-| `category_confidence` | number | Confidence score (0–1) for the top category |
-| `scores` | object | Confidence scores for all rubric categories |
-| `dialect` | string | Detected Arabic dialect |
-| `dialect_confidence` | number | Dialect confidence score (0–1) |
+| `id` | string | Unique identifier (`rcl_nw_xxx`) |
+| `object` | string | Always `"rubric_classification"` |
+| `categories` | array | Matched categories with confidence scores. Each has `name` (string) and `confidence` (number 0-1). |
+| `multi_label` | boolean | Whether multi-label mode was used |
| `language` | string | Detected language code |
-| `cached` | boolean | Whether served from semantic cache |
+| `provider` | string | AI provider used |
+| `model` | string | Model identifier used |
+| `fallback_used` | boolean | Whether a fallback provider was used |
+| `cost_usd` | number | Cost in USD |
+| `credits_used` | number | Credits deducted |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `text` or `rubric.categories`, empty categories, more than 20 categories, invalid category format |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 402 | `insufficient_credits` | No credits remaining |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal or provider error |
diff --git a/api-reference/translate.mdx b/api-reference/translate.mdx
index c10d2ba..5b14244 100644
--- a/api-reference/translate.mdx
+++ b/api-reference/translate.mdx
@@ -5,7 +5,7 @@ description: "Dialect-aware Arabic translation powered by HUMAIN's ALLaM model."
api: "POST https://api.trynawa.com/v1/translate"
---
-Translate text between English and Arabic with dialect awareness. Generates culturally appropriate translations in Gulf, Egyptian, Levantine, or MSA Arabic.
+Translate text between English and Arabic with dialect awareness. Generates culturally appropriate translations in Gulf, Egyptian, Levantine, or MSA Arabic. Uses ALLaM as the primary model with Claude as a fallback.
Cost: **$0.005** per request (5 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
@@ -26,52 +26,55 @@ Translate text between English and Arabic with dialect awareness. Generates cult
|-----------|------|----------|-------------|
| `text` | string | Yes | The text to translate. Max 5,000 characters. |
| `source` | string | No | Source language: `ar`, `en`, `auto`. Default: `auto` (detects automatically). |
-| `target` | string | Yes | Target language: `ar`, `en`. |
-| `dialect` | string | No | Target dialect for Arabic output: `gulf`, `egyptian`, `levantine`, `msa`. Default: `msa`. |
+| `target` | string | Yes | Target language: `ar`, `en`. Must differ from source. |
+| `dialect` | string | No | Target dialect for Arabic output: `gulf`, `egyptian`, `levantine`, `msa`. Default: `msa`. When source is Arabic and dialect is not specified, it is auto-detected. |
| `tone` | string | No | Translation tone: `formal`, `casual`, `social_media`. Default: `formal`. |
-| `platform` | string | No | Source platform for social media tone context: `youtube`, `instagram`, `twitter`, `facebook`. |
-| `metadata` | object | No | Arbitrary key-value metadata to attach to the translation. |
+| `platform` | string | No | Source platform for context: `youtube`, `instagram`, `twitter`, `facebook`. |
### Example request
```bash cURL
- curl -X POST https://api.trynawa.com/v1/translate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{
- "text": "Hello, how are you?",
- "source": "en",
- "target": "ar",
- "dialect": "gulf",
- "tone": "casual"
- }'
+curl -X POST https://api.trynawa.com/v1/translate \
+ -H "Authorization: Bearer nawa_test_sk_xxx" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "text": "Hello, how are you?",
+ "source": "en",
+ "target": "ar",
+ "dialect": "gulf",
+ "tone": "casual"
+ }'
```
```typescript TypeScript
- import { Nawa } from '@nawalabs/sdk'
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
- const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+const { data, error } = await nawa.translate({
+ text: 'Hello, how are you?',
+ source: 'en',
+ target: 'ar',
+ dialect: 'gulf',
+ tone: 'casual'
+})
- const { data, error } = await nawa.translate({
- text: 'Hello, how are you?',
- source: 'en',
- target: 'ar',
- dialect: 'gulf',
- tone: 'casual'
- })
+console.log(data.translated_text) // "هلا، كيف حالك؟"
```
```python Python
- from nawa import Nawa
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
- nawa = Nawa(api_key="your_api_key")
+result = nawa.translate(
+ text="Hello, how are you?",
+ source="en",
+ target="ar",
+ dialect="gulf",
+ tone="casual"
+)
- result = nawa.translate(
- text="Hello, how are you?",
- source="en",
- target="ar",
- dialect="gulf",
- tone="casual"
- )
+print(result.data.translated_text) # "هلا، كيف حالك؟"
```
@@ -91,22 +94,34 @@ Translate text between English and Arabic with dialect awareness. Generates cult
"source_dialect": null,
"target_dialect": "gulf",
"tone": "casual",
- "model": "nagl-v1",
"provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
"fallback_used": false,
- "cached": false,
+ "tokens_used": null,
"cost_usd": 0.005,
- "credits_used": 5
+ "credits_used": 5,
+ "cached": false
},
"errors": [],
"request_id": "req_nw_4c11w71e44cb"
}
```
+### Response headers
+
+| Header | Description |
+|--------|-------------|
+| `X-Request-Id` | Unique request identifier |
+| `X-NAWA-Cache` | `HIT` if served from cache (no cost), `MISS` otherwise |
+| `X-NAWA-Provider` | AI provider used (`allam` or `claude`) |
+| `X-NAWA-Latency` | Processing time in milliseconds |
+
### Result fields
| Field | Type | Description |
|-------|------|-------------|
+| `id` | string | Unique translation identifier (`trn_nw_xxx`) |
+| `object` | string | Always `"translation"` |
| `text` | string | The original input text |
| `translated_text` | string | The translated output |
| `source_language` | string | Detected or specified source language |
@@ -114,12 +129,13 @@ Translate text between English and Arabic with dialect awareness. Generates cult
| `source_dialect` | string or null | Detected dialect if source is Arabic |
| `target_dialect` | string or null | Target dialect if target is Arabic |
| `tone` | string | The tone used for translation |
-| `model` | string | Model version used |
| `provider` | string | AI provider used (`allam` or `claude`) |
+| `model` | string | Model identifier used |
| `fallback_used` | boolean | Whether the fallback provider was used |
-| `cached` | boolean | Whether served from semantic cache |
+| `tokens_used` | null | Reserved for future use |
| `cost_usd` | number | Cost in USD |
| `credits_used` | number | Credits deducted |
+| `cached` | boolean | Whether served from semantic cache |
### Dialect examples
@@ -128,10 +144,10 @@ Translate text between English and Arabic with dialect awareness. Generates cult
**Input:** "The food was amazing, thank you so much"
**Output:** "الأكل كان وايد حلو، مشكور وايد"
```bash
- curl -X POST https://api.trynawa.com/v1/translate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "The food was amazing, thank you so much", "source": "en", "target": "ar", "dialect": "gulf", "tone": "casual"}'
+curl -X POST https://api.trynawa.com/v1/translate \
+ -H "Authorization: Bearer nawa_test_sk_xxx" \
+ -H "Content-Type: application/json" \
+ -d '{"text": "The food was amazing, thank you so much", "source": "en", "target": "ar", "dialect": "gulf", "tone": "casual"}'
```
@@ -139,10 +155,10 @@ Translate text between English and Arabic with dialect awareness. Generates cult
**Input:** "Subscribe to my channel for more content!"
**Output:** "اشتركوا في القناة عشان يوصلكم كل جديد!"
```bash
- curl -X POST https://api.trynawa.com/v1/translate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "Subscribe to my channel for more content!", "source": "en", "target": "ar", "dialect": "egyptian", "tone": "social_media", "platform": "youtube"}'
+curl -X POST https://api.trynawa.com/v1/translate \
+ -H "Authorization: Bearer nawa_test_sk_xxx" \
+ -H "Content-Type: application/json" \
+ -d '{"text": "Subscribe to my channel for more content!", "source": "en", "target": "ar", "dialect": "egyptian", "tone": "social_media", "platform": "youtube"}'
```
@@ -150,21 +166,10 @@ Translate text between English and Arabic with dialect awareness. Generates cult
**Input:** "وش رايكم بالمحتوى الجديد؟"
**Output:** "What do you think of the new content?"
```bash
- curl -X POST https://api.trynawa.com/v1/translate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "وش رايكم بالمحتوى الجديد؟", "source": "ar", "target": "en", "tone": "formal"}'
-```
-
-
-
- **Input:** "كيف حالك اليوم؟" (MSA)
- **Output:** "شلونك اليوم؟" (Gulf)
-```bash
- curl -X POST https://api.trynawa.com/v1/translate \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
- -H "Content-Type: application/json" \
- -d '{"text": "كيف حالك اليوم؟", "source": "ar", "target": "ar", "dialect": "gulf", "tone": "casual"}'
+curl -X POST https://api.trynawa.com/v1/translate \
+ -H "Authorization: Bearer nawa_test_sk_xxx" \
+ -H "Content-Type: application/json" \
+ -d '{"text": "وش رايكم بالمحتوى الجديد؟", "source": "ar", "target": "en", "tone": "formal"}'
```
@@ -173,7 +178,7 @@ Translate text between English and Arabic with dialect awareness. Generates cult
| Status | Type | When |
|--------|------|------|
-| 400 | `invalid_request_error` | Missing `text`, `source`, or `target`. Invalid values. Text too long. |
+| 400 | `invalid_request_error` | Missing `text` or `target`. Same source and target. Invalid values. Text too long (max 5,000 chars). |
| 401 | `authentication_error` | Invalid or missing API key |
| 402 | `insufficient_credits` | No credits remaining |
| 429 | `rate_limit_error` | Rate limit exceeded |
diff --git a/api-reference/usage.mdx b/api-reference/usage.mdx
index d4b1b6b..94637a1 100644
--- a/api-reference/usage.mdx
+++ b/api-reference/usage.mdx
@@ -1,7 +1,7 @@
---
title: "Usage"
sidebarTitle: "GET /v1/usage"
-description: "Retrieve API usage statistics, credit consumption, and request counts."
+description: "Retrieve API usage statistics, credit consumption, and cache hit rates."
api: "GET https://api.trynawa.com/v1/usage"
---
@@ -13,72 +13,146 @@ Retrieve your API usage statistics including request counts, credit consumption,
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `from` | string | No | Start date (ISO 8601). Default: start of current month. |
-| `to` | string | No | End date (ISO 8601). Default: now. |
-| `group_by` | string | No | Group by: `day`, `week`, `month`, `endpoint`. Default: `day`. |
+| `from` | string | No | Start date (ISO 8601). |
+| `to` | string | No | End date (ISO 8601). |
+| `group_by` | string | No | Group by: `endpoint` or `day`. Default: `endpoint`. |
### Example request
-```bash
+
+
+```bash cURL
curl "https://api.trynawa.com/v1/usage?from=2025-01-01T00:00:00Z&group_by=endpoint" \
-H "Authorization: Bearer nawa_test_sk_xxx"
```
+```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'
+})
+
+console.log(data.usage.total_requests)
+console.log(data.usage.cache_hit_rate)
+```
+
+```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"
+)
+
+print(result.data.usage["total_requests"])
+print(result.data.usage["cache_hit_rate"])
+```
+
+
+
## Response
### Success response (200)
+The breakdown key is dynamic based on `group_by`: `by_endpoint` or `by_day`.
+
```json
{
"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": 2835,
+ "cache_hit_rate": 0.23,
+ "period": {
+ "from": "2025-01-01T00:00:00Z",
+ "to": null
+ }
},
- "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
+ "cost": 51.0,
+ "cache_hits": 1955
},
{
- "endpoint": "/v1/rubric/classify",
+ "endpoint": "/v1/rubric-classify",
"requests": 2200,
- "cost": 6.60,
- "cache_hits": 880,
- "avg_latency_ms": 650
+ "cost": 6.6,
+ "cache_hits": 880
},
{
- "endpoint": "/v1/comments/:id/reply",
+ "endpoint": "/v1/comments/reply",
"requests": 1750,
- "cost": 14.00,
- "cache_hits": 0,
- "avg_latency_ms": 1200
+ "cost": 14.0,
+ "cache_hits": 0
+ }
+ ]
+ },
+ "errors": [],
+ "request_id": "req_nw_usg_abc123"
+}
+```
+
+When grouped by `day`:
+
+```json
+{
+ "success": true,
+ "result": {
+ "usage": {
+ "total_requests": 350,
+ "total_cost": 2.1,
+ "cache_hits": 80,
+ "cache_hit_rate": 0.23,
+ "period": {
+ "from": "2025-01-01T00:00:00Z",
+ "to": "2025-01-07T23:59:59Z"
}
- ],
- "balance": {
- "current": 31.30,
- "credits_purchased": 100.00,
- "credits_used": 68.70
- }
+ },
+ "by_day": [
+ {
+ "date": "2025-01-07",
+ "requests": 65,
+ "cost": 0.39,
+ "cache_hits": 15
+ },
+ {
+ "date": "2025-01-06",
+ "requests": 58,
+ "cost": 0.348,
+ "cache_hits": 12
+ }
+ ]
},
"errors": [],
- "request_id": "req_usg_abc123"
+ "request_id": "req_nw_usg_def456"
}
```
-### Result fields
+### Usage fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `usage.total_requests` | integer | Total API requests in the period |
+| `usage.total_cost` | number | Total cost in USD |
+| `usage.cache_hits` | integer | Requests served from cache |
+| `usage.cache_hit_rate` | number | Cache hit rate (0-1) |
+| `usage.period` | object | `from` and `to` dates (null if not specified) |
+
+### Breakdown fields
| Field | Type | Description |
|-------|------|-------------|
-| `total_requests` | integer | Total API requests in the period |
-| `total_cost` | number | Total credit cost in USD |
-| `cache_hit_rate` | number | Percentage of requests served from cache (0–1) |
-| `by_endpoint` | array | Breakdown by endpoint |
-| `balance` | object | Current balance and credit history |
+| `endpoint` or `date` | string | The grouping key |
+| `requests` | integer | Request count |
+| `cost` | number | Cost in USD |
+| `cache_hits` | integer | Cache hits in this group |
diff --git a/billing.mdx b/billing.mdx
index e28bb54..d65ee2a 100644
--- a/billing.mdx
+++ b/billing.mdx
@@ -47,8 +47,8 @@ NAWA uses a prepaid credit system.
| `POST /v1/translate` | $0.005 | 5 credits |
| `POST /v1/detect` | $0.002 | 2 credits |
| `POST /v1/moderate` | $0.004 | 4 credits |
-| `POST /v1/rubric/classify` | $0.003 | 3 credits |
-| `POST /v1/comments/:id/reply` | $0.008 | 8 credits |
+| `POST /v1/rubric-classify` | $0.003 | 3 credits |
+| `POST /v1/comments/reply` | $0.008 | 8 credits |
| `POST /v1/report` (Basic) | $0.50 | 500 credits |
| `POST /v1/report` (Pro) | $1.50 | 1,500 credits |
| `POST /v1/feedback` | **Free** | 0 credits |
@@ -97,8 +97,8 @@ When your credit balance reaches 0:
| `/v1/translate` | $5 | $50 | $500 |
| `/v1/detect` | $2 | $20 | $200 |
| `/v1/moderate` | $4 | $40 | $400 |
-| `/v1/rubric/classify` | $3 | $30 | $300 |
-| `/v1/comments/:id/reply` | $8 | $80 | $800 |
+| `/v1/rubric-classify` | $3 | $30 | $300 |
+| `/v1/comments/reply` | $8 | $80 | $800 |
With typical semantic cache hit rates of 20-30%, actual costs are 20-30% lower than these estimates.
diff --git a/openapi.yaml b/openapi.yaml
index 17706f2..4926eff 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -59,6 +59,14 @@ paths:
x-nawa-cost: "$0.006"
x-nawa-cache: true
x-nawa-free: false
+ parameters:
+ - name: provider
+ in: query
+ required: false
+ description: Override AI provider for A/B testing
+ schema:
+ type: string
+ enum: [claude, gemini, allam]
requestBody:
required: true
content:
@@ -70,25 +78,15 @@ paths:
text:
type: string
minLength: 1
- maxLength: 5000
description: The comment text to classify
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 object for additional classification hints
example:
text: "متى الجزء الثاني؟"
- platform: "youtube"
- channel_id: "ch_123"
responses:
"200":
description: Classification result
@@ -117,19 +115,24 @@ paths:
value:
success: true
result:
- text: "متى الجزء الثاني؟"
+ id: cls_nw_a1b2c3d4e5f6
+ object: classification
intent: question
- intent_confidence: 0.97
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 +197,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"
@@ -457,7 +457,7 @@ paths:
"500":
$ref: "#/components/responses/InternalError"
- /rubric/classify:
+ /rubric-classify:
post:
operationId: rubricClassify
summary: Classify with custom rubric
@@ -492,27 +492,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: Array of category objects with name and optional description
+ multi_label:
+ type: boolean
+ default: false
+ description: Enable multi-label classification
+ confidence_threshold:
+ type: number
+ minimum: 0
+ maximum: 1
+ default: 0.5
+ description: Minimum confidence to include a category
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
@@ -567,37 +578,40 @@ paths:
"500":
$ref: "#/components/responses/InternalError"
- /comments/{id}/reply:
+ /comments/reply:
post:
operationId: generateReply
- summary: Generate reply to comment
+ summary: Classify comment and generate reply
description: |
- Generate a culturally-aware, dialect-matched reply to an Arabic comment.
+ Classify a comment and generate a culturally-aware, dialect-matched reply in a single call.
Replies match the commenter's dialect and are contextually appropriate.
tags: [Reply]
x-nawa-cost: "$0.008"
- x-nawa-cache: true
+ x-nawa-cache: false
x-nawa-free: false
parameters:
- - name: id
- in: path
- required: true
- description: The comment ID to reply to
+ - name: provider
+ in: query
+ required: false
+ description: Override the AI provider for A/B testing
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
+ description: The comment text to classify and reply to
tone:
type: string
enum: [friendly, professional, casual, formal]
- default: friendly
+ default: professional
description: Reply tone
max_length:
type: integer
@@ -606,16 +620,19 @@ 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
+ description: Additional context
+ properties:
+ platform:
+ type: string
+ brand_voice:
+ type: string
example:
- tone: "friendly"
- context: "Tech review channel focused on smartphones"
+ text: "متى الجزء الثاني؟"
+ tone: "professional"
+ context:
+ platform: "youtube"
+ brand_voice: "Tech review channel"
responses:
"200":
description: Generated reply
@@ -879,9 +896,9 @@ paths:
required: false
schema:
type: string
- enum: [day, week, month]
- default: day
- description: Group results by time period
+ enum: [endpoint, day, provider]
+ default: endpoint
+ description: "Group breakdown by: endpoint, day, or provider"
responses:
"200":
description: Analytics data
@@ -996,25 +1013,25 @@ paths:
- name: from
in: query
required: false
+ description: "Start date (ISO 8601). Default: start of current month."
schema:
type: string
format: date-time
- description: Start date (ISO 8601). Default: start of current month.
- name: to
in: query
required: false
+ description: "End date (ISO 8601). Default: now."
schema:
type: string
format: date-time
- description: End date (ISO 8601). Default: now.
- name: group_by
in: query
required: false
+ description: "Group by: endpoint or day"
schema:
type: string
- enum: [day, week, month, endpoint]
- default: day
- description: Group results by time period or endpoint
+ enum: [endpoint, day]
+ default: endpoint
responses:
"200":
description: Usage statistics
@@ -1424,51 +1441,65 @@ components:
ClassificationResult:
type: object
+ required: [id, object, intent, sentiment, language, provider, model, cost_usd, credits_used]
properties:
- text:
+ id:
type: string
- description: The original input text
+ description: Classification ID (cls_nw_xxx format)
+ object:
+ type: string
+ const: classification
intent:
type: string
enum: [question, complaint, praise, suggestion, spam, other]
description: Detected intent
- intent_confidence:
- type: number
- minimum: 0
- maximum: 1
sentiment:
type: string
enum: [positive, negative, neutral, mixed]
- sentiment_confidence:
- type: number
- minimum: 0
- maximum: 1
+ language:
+ type: string
+ enum: [ar, en, mixed]
dialect:
type: string
+ nullable: true
enum: [gulf, egyptian, levantine, msa]
+ description: Arabic dialect. Null for English text.
dialect_confidence:
type: number
+ nullable: true
minimum: 0
maximum: 1
- toxicity:
+ description: Dialect confidence. Null for English text.
+ requires_response:
+ type: boolean
+ description: Whether this comment warrants 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:
+ description: Priority level for response triage
+ suggested_reply:
+ type: object
+ properties:
+ text:
+ type: string
+ direction:
+ type: string
+ enum: [rtl, ltr]
+ provider:
type: string
- enum: [ar, en, mixed]
+ description: "AI provider used (allam, claude, or gemini)"
model:
type: string
- cached:
+ description: Model identifier used
+ fallback_used:
type: boolean
+ description: Whether a fallback provider was used
+ tokens_used:
+ nullable: true
+ description: Reserved for future use
+ cost_usd:
+ type: number
+ credits_used:
+ type: integer
TranslateResult:
type: object
@@ -1720,68 +1751,103 @@ components:
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_nw_xxx format)
+ object:
type: string
- enum: [friendly, professional, casual, formal]
- description: The tone used for the reply
- original_intent:
+ const: comment_reply
+ classification:
+ type: object
+ properties:
+ intent:
+ type: string
+ enum: [question, complaint, praise, suggestion, spam, other]
+ sentiment:
+ type: string
+ enum: [positive, negative, neutral, mixed]
+ priority:
+ type: string
+ requires_response:
+ type: boolean
+ reply:
+ type: object
+ properties:
+ text:
+ type: string
+ description: Generated reply text
+ direction:
+ type: string
+ enum: [rtl, ltr]
+ tone:
+ type: string
+ enum: [friendly, professional, casual, formal]
+ provider:
type: string
- enum: [question, complaint, praise, suggestion, spam, other]
- description: Detected intent of the original comment
- original_dialect:
+ 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 id from the classification response (cls_nw_xxx)
+ rating:
type: string
- enum: [intent, sentiment, dialect, toxicity, category]
- description: The field to correct
- expected_value:
+ enum: [correct, incorrect, partial]
+ description: Your assessment of the classification accuracy
+ corrected_intent:
+ type: array
+ items:
+ type: string
+ description: Array of correct intents if the model got it wrong
+ corrected_sentiment:
type: string
- description: The correct value the model should have returned
+ enum: [positive, negative, neutral, mixed]
+ description: Correct sentiment value
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_nw_xxx format)
+ object:
type: string
- enum: [accepted]
- message:
+ const: feedback
+ classification_id:
type: string
+ rating:
+ type: string
+ enum: [correct, incorrect, partial]
+ acknowledged:
+ type: boolean
CommentItem:
type: object
properties:
id:
type: string
+ request_id:
+ type: string
+ description: The original classification request ID
text:
type: string
platform:
type: string
+ nullable: true
enum: [youtube, instagram, twitter, facebook]
intent:
type: string
@@ -1791,124 +1857,151 @@ components:
enum: [positive, negative, neutral, mixed]
dialect:
type: string
+ nullable: true
enum: [gulf, egyptian, levantine, msa]
- toxicity:
- type: string
- enum: [none, mild, moderate, severe]
created_at:
type: string
format: date-time
- channel_id:
- type: string
Pagination:
type: object
properties:
+ total:
+ type: integer
+ description: Total matching comments
+ limit:
+ type: integer
+ description: Page size used
+ offset:
+ type: integer
+ description: Current offset
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: integer
+ 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:
+ properties:
+ from:
+ type: string
+ nullable: true
+ to:
+ type: string
+ nullable: true
+ by_endpoint:
type: array
items:
type: object
properties:
- period:
+ endpoint:
type: string
- total:
+ requests:
type: integer
- sentiment_score:
+ cost:
type: number
- top_intent:
- type: string
+ avg_latency_ms:
+ type: integer
+ description: "Dynamic key: by_endpoint, by_day, or by_provider based on group_by"
+ # OldAnalyticsPlaceholder removed - replaced by AnalyticsResult above
HealthResult:
type: object
+ description: "Note: health endpoint returns this directly, not wrapped in the success envelope."
properties:
status:
type: string
enum: [healthy, degraded]
version:
type: string
+ description: API version (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]
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [healthy, degraded, down]
+ latency_ms:
+ type: integer
+ redis:
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [healthy, degraded, down]
+ latency_ms:
+ type: integer
+ nagl:
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [healthy, degraded, down]
timestamp:
type: string
format: date-time
+ latency_ms:
+ type: integer
+ description: Total health check duration in milliseconds
UsageResult:
type: object
properties:
- period:
+ usage:
type: object
properties:
- from:
- type: string
- format: date-time
- to:
- type: string
- format: date-time
- total_requests:
- type: integer
- total_cost:
- type: number
- description: Total cost in USD
- cache_hit_rate:
- type: number
- minimum: 0
- maximum: 1
+ total_requests:
+ type: integer
+ total_cost:
+ type: number
+ 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
+ nullable: true
+ to:
+ type: string
+ nullable: true
by_endpoint:
type: array
+ description: "Dynamic key: by_endpoint or by_day based on group_by"
items:
type: object
properties:
@@ -1920,17 +2013,6 @@ components:
type: number
cache_hits:
type: integer
- avg_latency_ms:
- type: integer
- balance:
- type: object
- properties:
- current:
- type: number
- credits_purchased:
- type: number
- credits_used:
- type: number
WebhookEndpoint:
type: object
diff --git a/webhooks.mdx b/webhooks.mdx
index 2ef4922..7eea37b 100644
--- a/webhooks.mdx
+++ b/webhooks.mdx
@@ -12,8 +12,6 @@ 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 |