diff --git a/api-reference/analytics.mdx b/api-reference/analytics.mdx
index c2879c7..b1506e8 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 hit rates."
api: "GET https://api.trynawa.com/v1/analytics"
---
-Retrieve aggregated analytics across your classified comments. This endpoint is **free**.
+Retrieve aggregated analytics across your API usage, including request counts, costs, cache performance, and error rates. 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`. |
+| `from` | string | No | Start date (ISO 8601). |
+| `to` | string | No | End date (ISO 8601). |
+| `group_by` | string | No | Group results 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=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.get({
+ 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.get(
+ from_date="2025-01-01T00:00:00Z",
+ to_date="2025-01-31T23:59:59Z",
+ group_by="endpoint"
+)
+```
+
+
+
## Response
### Success response (200)
@@ -34,45 +60,41 @@ 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": 2831,
+ "cache_hit_rate": 0.23,
+ "avg_latency_ms": 820,
+ "success_rate": 0.98,
+ "error_count": 249,
+ "endpoint_distribution": {
+ "/v1/classify": 8500,
+ "/v1/translate": 1200,
+ "/v1/comments/reply": 1750,
+ "/v1/detect": 1000
},
- "dialect": {
- "gulf": 2015,
- "egyptian": 1302,
- "levantine": 789,
- "msa": 415
+ "provider_distribution": {
+ "allam": 9200,
+ "claude": 3250
},
- "toxicity": {
- "none": 3950,
- "mild": 320,
- "moderate": 180,
- "severe": 71
+ "period": {
+ "from": "2025-01-01T00:00:00Z",
+ "to": "2025-01-31T23:59:59Z"
}
},
- "trends": [
+ "by_endpoint": [
{
- "period": "2025-01-06/2025-01-12",
- "total": 1123,
- "sentiment_score": 0.72,
- "top_intent": "praise"
+ "endpoint": "/v1/classify",
+ "requests": 8500,
+ "cost": 51.00,
+ "avg_latency_ms": 780
+ },
+ {
+ "endpoint": "/v1/comments/reply",
+ "requests": 1750,
+ "cost": 14.00,
+ "avg_latency_ms": 1200
}
]
},
@@ -80,3 +102,32 @@ curl "https://api.trynawa.com/v1/analytics?platform=youtube&from=2025-01-01T00:0
"request_id": "req_ana_abc123"
}
```
+
+
+ The breakdown key changes based on `group_by`: `by_endpoint`, `by_day`, or `by_provider`.
+
+
+### 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` | integer | Average response latency in milliseconds |
+| `analytics.success_rate` | number | Ratio of successful (2xx) requests (0-1) |
+| `analytics.error_count` | integer | Number of error (4xx/5xx) responses |
+| `analytics.endpoint_distribution` | object | Request count by endpoint |
+| `analytics.provider_distribution` | object | Request count by AI provider |
+| `analytics.period` | object | The date range queried |
+| `by_endpoint` | array | Breakdown by the selected `group_by` dimension |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Invalid `group_by` value |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal error |
diff --git a/api-reference/comments-list.mdx b/api-reference/comments-list.mdx
index 1e18d08..2f37577 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 previously classified comments with filtering and offset-based pagination. 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,51 @@ for comment in nawa.comments.list(platform="youtube"):
"result": {
"comments": [
{
- "id": "cmt_abc123",
+ "id": "cls_abc123",
+ "request_id": "req_nw_a1b2c3",
"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"
}
```
+
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `comments` | array | Array of classified comment objects |
+| `comments[].id` | string | Classification record ID |
+| `comments[].request_id` | string | Original API request ID |
+| `comments[].text` | string | The classified text |
+| `comments[].platform` | string | Source platform |
+| `comments[].intent` | string | Detected intent |
+| `comments[].sentiment` | string | Detected sentiment |
+| `comments[].dialect` | string | Detected dialect |
+| `comments[].created_at` | string | ISO 8601 timestamp |
+| `pagination.total` | integer | Total matching comments |
+| `pagination.limit` | integer | Results per page |
+| `pagination.offset` | integer | Current offset |
+| `pagination.has_more` | boolean | Whether more results are available |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Invalid filter values |
+| 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/comments-reply.mdx b/api-reference/comments-reply.mdx
index d6321d7..635274d 100644
--- a/api-reference/comments-reply.mdx
+++ b/api-reference/comments-reply.mdx
@@ -1,11 +1,11 @@
---
-title: "Reply to Comment"
-sidebarTitle: "POST /v1/comments/:id/reply"
+title: "Reply to comment"
+sidebarTitle: "POST /v1/comments/reply"
description: "Generate a context-aware reply to a comment in Arabic or English."
-api: "POST https://api.trynawa.com/v1/comments/{id}/reply"
+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.
+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.
Cost: **$0.008** per request (8 credits). Semantic cache hits are free (`X-NAWA-Cache: HIT`).
@@ -13,47 +13,62 @@ Generate an AI-powered reply that matches the commenter's language and cultural
## Request
-### Path parameters
-
-| Parameter | Type | Required | Description |
-|-----------|------|----------|-------------|
-| `id` | string | Yes | The comment ID to reply to. |
-
### Body parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `tone` | string | No | Reply tone: `friendly`, `professional`, `casual`, `formal`. Default: `friendly`. |
-| `max_length` | integer | No | Maximum reply length in characters. Default: 500. |
-| `context` | string | No | Additional context about the channel or video to improve reply relevance. |
-| `language` | string | No | Force reply language: `ar`, `en`, `auto`. Default: `auto` (matches commenter's language). |
+| `text` | string | Yes | The comment text to generate a reply for. |
+| `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 to improve reply relevance. |
+| `context.platform` | string | No | Source platform (e.g. `youtube`, `instagram`). |
+| `context.brand_voice` | string | No | Description of the brand voice or channel personality. |
### Example request
```bash cURL
-curl -X POST https://api.trynawa.com/v1/comments/cmt_abc123/reply \
+curl -X POST https://api.trynawa.com/v1/comments/reply \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
+ "text": "متى الجزء الثاني؟",
"tone": "friendly",
- "context": "Tech review channel focused on smartphones"
+ "context": {
+ "platform": "youtube",
+ "brand_voice": "Tech review channel focused on smartphones"
+ }
}'
```
```typescript TypeScript
-const { data, error } = await nawa.comments.reply('cmt_abc123', {
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.comments.reply({
+ text: 'متى الجزء الثاني؟',
tone: 'friendly',
- context: 'Tech review channel focused on smartphones'
+ context: {
+ platform: 'youtube',
+ brandVoice: 'Tech review channel focused on smartphones'
+ }
})
```
```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
result = nawa.comments.reply(
- comment_id="cmt_abc123",
+ text="متى الجزء الثاني؟",
tone="friendly",
- context="Tech review channel focused on smartphones"
+ context={
+ "platform": "youtube",
+ "brand_voice": "Tech review channel focused on smartphones"
+ }
)
```
@@ -67,25 +82,64 @@ 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": "medium",
+ "requires_response": true
+ },
+ "reply": {
+ "text": "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔",
+ "direction": "rtl",
+ "tone": "friendly"
+ },
+ "provider": "allam",
+ "model": "nagl-v1",
+ "cost_usd": 0.008,
+ "credits_used": 8
},
"errors": [],
"request_id": "req_rep789xyz012"
}
```
+### Response headers
+
+| Header | Description |
+|--------|-------------|
+| `X-Request-Id` | Unique request identifier for debugging |
+| `X-NAWA-Provider` | AI provider used for this request |
+| `X-NAWA-Latency` | Processing time in milliseconds |
+| `X-RateLimit-Limit` | Rate limit ceiling for current window |
+| `X-RateLimit-Remaining` | Requests remaining in current window |
+| `X-RateLimit-Reset` | Window reset time (RFC 3339) |
+
### Result fields
| Field | Type | Description |
|-------|------|-------------|
-| `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_` prefix) |
+| `object` | string | Always `comment_reply` |
+| `classification.intent` | string | Detected intent of the original comment |
+| `classification.sentiment` | string | Detected sentiment of the original comment |
+| `classification.priority` | string | Priority level (`low`, `medium`, `high`) |
+| `classification.requires_response` | boolean | Whether the comment warrants a reply |
+| `reply.text` | string | The generated reply text |
+| `reply.direction` | string | Text direction: `rtl` for Arabic, `ltr` for English |
+| `reply.tone` | string | The tone used for the reply |
+| `provider` | string | AI provider used (`allam` or `claude`) |
+| `model` | string | Model version used |
+| `cost_usd` | number | Cost in USD |
+| `credits_used` | integer | Credits deducted |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `text`, invalid `tone`, `max_length` out of range |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 402 | `insufficient_credits` | No credits remaining |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal or provider error |
diff --git a/api-reference/feedback.mdx b/api-reference/feedback.mdx
index 4cddf8b..95983c7 100644
--- a/api-reference/feedback.mdx
+++ b/api-reference/feedback.mdx
@@ -1,11 +1,11 @@
---
-title: "Submit Feedback"
+title: "Submit feedback"
sidebarTitle: "POST /v1/feedback"
-description: "Submit RLHF feedback to improve classification accuracy over time."
+description: "Submit feedback on a classification result to improve NAWA accuracy."
api: "POST https://api.trynawa.com/v1/feedback"
---
-Submit reinforcement learning from human feedback (RLHF) to continuously improve NAWA's classification accuracy.
+Submit feedback on a classification result to help improve NAWA's accuracy over time. You can rate a classification as correct, incorrect, or partial, and optionally provide the corrected values.
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 classification ID from the original response (e.g. `cls_nw_abc123`). |
+| `rating` | string | Yes | Your assessment: `correct`, `incorrect`, `partial`. |
+| `corrected_intent` | string[] | No | Array of correct intent labels if the classification was wrong. |
+| `corrected_sentiment` | string | No | Correct sentiment: `positive`, `negative`, `neutral`, `mixed`. |
+| `comment` | string | No | Free-text explanation of why this correction is needed. |
### Example request
@@ -35,28 +36,39 @@ curl -X POST https://api.trynawa.com/v1/feedback \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
- "request_id": "req_abc123def456",
- "field": "dialect",
- "expected_value": "levantine",
- "comment": "This is Lebanese Arabic, not Gulf"
+ "classification_id": "cls_nw_a1b2c3d4e5f6",
+ "rating": "incorrect",
+ "corrected_intent": ["question"],
+ "corrected_sentiment": "neutral",
+ "comment": "This is a question, 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_nw_a1b2c3d4e5f6',
+ rating: 'incorrect',
+ correctedIntent: ['question'],
+ correctedSentiment: 'neutral',
+ comment: 'This is a question, 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_nw_a1b2c3d4e5f6",
+ rating="incorrect",
+ corrected_intent=["question"],
+ corrected_sentiment="neutral",
+ comment="This is a question, not praise"
)
```
@@ -70,11 +82,32 @@ result = nawa.feedback.submit(
{
"success": true,
"result": {
- "feedback_id": "fb_xyz789",
- "status": "accepted",
- "message": "Thank you! Your feedback helps improve NAWA's accuracy."
+ "id": "fb_nw_xyz789",
+ "object": "feedback",
+ "classification_id": "cls_nw_a1b2c3d4e5f6",
+ "rating": "incorrect",
+ "acknowledged": true
},
"errors": [],
"request_id": "req_fb_abc123"
}
```
+
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Unique feedback identifier |
+| `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 |
+
+### 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..17efe2d 100644
--- a/api-reference/health.mdx
+++ b/api-reference/health.mdx
@@ -1,5 +1,5 @@
---
-title: "Health Check"
+title: "Health check"
sidebarTitle: "GET /v1/health"
description: "Check the operational status of the NAWA API and its dependencies."
api: "GET https://api.trynawa.com/v1/health"
@@ -9,54 +9,90 @@ Check the operational status of the NAWA API. This endpoint is **free** and does
## Request
-```bash
+
+
+```bash cURL
curl https://api.trynawa.com/v1/health
```
+```typescript TypeScript
+const response = await fetch('https://api.trynawa.com/v1/health')
+const data = await response.json()
+```
+
+```python Python
+import requests
+
+response = requests.get("https://api.trynawa.com/v1/health")
+data = response.json()
+```
+
+
+
## Response
+The health endpoint returns a flat JSON object (not wrapped in a `success`/`result` envelope).
+
### Healthy response (200)
```json
{
- "success": true,
- "result": {
- "status": "healthy",
- "version": "1.0.0",
- "services": {
- "api": "operational",
- "classification": "operational",
- "database": "operational",
- "cache": "operational"
+ "status": "healthy",
+ "version": "v1",
+ "services": {
+ "database": {
+ "status": "healthy",
+ "latency_ms": 12
+ },
+ "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:00Z",
+ "latency_ms": 15
}
```
-### Degraded response (200)
+### Degraded response (503)
```json
{
- "success": true,
- "result": {
- "status": "degraded",
- "version": "1.0.0",
- "services": {
- "api": "operational",
- "classification": "degraded",
- "database": "operational",
- "cache": "operational"
+ "status": "degraded",
+ "version": "v1",
+ "services": {
+ "database": {
+ "status": "healthy",
+ "latency_ms": 12
},
- "timestamp": "2025-01-15T12:00:00Z"
+ "redis": {
+ "status": "down"
+ },
+ "nagl": {
+ "status": "healthy"
+ }
},
- "errors": [],
- "request_id": "req_hlt_def456"
+ "timestamp": "2025-01-15T12:00:00Z",
+ "latency_ms": 5012
}
```
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `status` | string | Overall status: `healthy` or `degraded` |
+| `version` | string | API version (currently `v1`) |
+| `services` | object | Individual service statuses |
+| `services.database` | object | Database connectivity. `status` is `healthy`, `degraded`, or `down`. |
+| `services.redis` | object | Redis cache connectivity. `status` is `healthy`, `degraded`, or `down`. |
+| `services.nagl` | object | NAGL classification engine. `status` is `healthy` or `down`. |
+| `timestamp` | string | ISO 8601 timestamp of the check |
+| `latency_ms` | integer | Total time to run all health checks |
+
- For real-time status monitoring, visit [status.trynawa.com](https://status.trynawa.com).
+ The API returns HTTP 200 when all services are healthy, and HTTP 503 when any service is degraded or down. 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
index bbfc559..18148af 100644
--- a/api-reference/openapi.yaml
+++ b/api-reference/openapi.yaml
@@ -130,7 +130,7 @@ paths:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
- /v1/rubric/classify:
+ /v1/rubric-classify:
post:
operationId: rubricClassify
summary: Rubric-based classification
diff --git a/api-reference/rubric-classify.mdx b/api-reference/rubric-classify.mdx
index 4489175..2e6ad79 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 text against a custom rubric with user-defined categories and scoring criteria."
+api: "POST https://api.trynawa.com/v1/rubric-classify"
---
-Classify a comment against a custom rubric. Define your own categories and scoring criteria for domain-specific classification.
+Classify text against a custom rubric. Define your own categories with descriptions for domain-specific classification. Supports single-label and multi-label modes.
- 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 (`X-NAWA-Cache: HIT`).
## Request
@@ -17,64 +17,72 @@ 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. |
+| `text` | string | Yes | The text to classify. Max 5,000 characters. |
+| `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 | When `true`, returns all categories above the confidence threshold. Default: `false` (single best match). |
+| `rubric.confidence_threshold` | number | No | Minimum confidence to include a category (0-1). Default: `0.5`. |
### Example request
```bash cURL
-curl -X POST https://api.trynawa.com/v1/rubric/classify \
+curl -X POST https://api.trynawa.com/v1/rubric-classify \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
"text": "وين الترجمة العربية؟ مافهمت شي",
"rubric": {
- "categories": ["translation_request", "technical_issue", "content_feedback", "off_topic"],
- "descriptions": {
- "translation_request": "User is asking for subtitles or translation",
- "technical_issue": "Audio, video, or playback problems",
- "content_feedback": "Comments about the content quality",
- "off_topic": "Not related to the video"
- }
- },
- "platform": "youtube"
+ "categories": [
+ {"name": "translation_request", "description": "User is asking for subtitles or translation"},
+ {"name": "technical_issue", "description": "Audio, video, or playback problems"},
+ {"name": "content_feedback", "description": "Comments about the content quality"},
+ {"name": "off_topic", "description": "Not related to the video"}
+ ],
+ "multi_label": false,
+ "confidence_threshold": 0.5
+ }
}'
```
```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
const { data, error } = await nawa.rubric.classify({
text: 'وين الترجمة العربية؟ مافهمت شي',
rubric: {
- categories: ['translation_request', 'technical_issue', 'content_feedback', 'off_topic'],
- descriptions: {
- translation_request: 'User is asking for subtitles or translation',
- technical_issue: 'Audio, video, or playback problems',
- content_feedback: 'Comments about the content quality',
- off_topic: 'Not related to the video'
- }
- },
- platform: 'youtube'
+ categories: [
+ { name: 'translation_request', description: 'User is asking for subtitles or translation' },
+ { name: 'technical_issue', description: 'Audio, video, or playback problems' },
+ { name: 'content_feedback', description: 'Comments about the content quality' },
+ { name: 'off_topic', description: 'Not related to the video' }
+ ],
+ multiLabel: false,
+ confidenceThreshold: 0.5
+ }
})
```
```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
result = nawa.rubric.classify(
text="وين الترجمة العربية؟ مافهمت شي",
rubric={
- "categories": ["translation_request", "technical_issue", "content_feedback", "off_topic"],
- "descriptions": {
- "translation_request": "User is asking for subtitles or translation",
- "technical_issue": "Audio, video, or playback problems",
- "content_feedback": "Comments about the content quality",
- "off_topic": "Not related to the video",
- },
+ "categories": [
+ {"name": "translation_request", "description": "User is asking for subtitles or translation"},
+ {"name": "technical_issue", "description": "Audio, video, or playback problems"},
+ {"name": "content_feedback", "description": "Comments about the content quality"},
+ {"name": "off_topic", "description": "Not related to the video"},
+ ],
+ "multi_label": False,
+ "confidence_threshold": 0.5,
},
- platform="youtube",
)
```
@@ -88,34 +96,57 @@ result = nawa.rubric.classify(
{
"success": true,
"result": {
- "text": "وين الترجمة العربية؟ مافهمت شي",
- "category": "translation_request",
- "category_confidence": 0.94,
- "scores": {
- "translation_request": 0.94,
- "technical_issue": 0.03,
- "content_feedback": 0.02,
- "off_topic": 0.01
- },
- "dialect": "gulf",
- "dialect_confidence": 0.91,
+ "id": "rcl_nw_a1b2c3d4e5f6",
+ "object": "rubric_classification",
+ "categories": [
+ {"name": "translation_request", "confidence": 0.94},
+ {"name": "technical_issue", "confidence": 0.03}
+ ],
+ "multi_label": false,
"language": "ar",
+ "provider": "allam",
"model": "nagl-v1",
- "cached": false
+ "fallback_used": false,
+ "cost_usd": 0.003,
+ "credits_used": 3
},
"errors": [],
"request_id": "req_rub123abc456"
}
```
+### Response headers
+
+| Header | Description |
+|--------|-------------|
+| `X-Request-Id` | Unique request identifier for debugging |
+| `X-NAWA-Provider` | AI provider used for this request |
+| `X-NAWA-Latency` | Processing time in milliseconds |
+| `X-RateLimit-Limit` | Rate limit ceiling for current window |
+| `X-RateLimit-Remaining` | Requests remaining in current window |
+| `X-RateLimit-Reset` | Window reset time (RFC 3339) |
+
### Result fields
| Field | Type | Description |
|-------|------|-------------|
-| `category` | string | The top matching category from your rubric |
-| `category_confidence` | number | Confidence score (0–1) for the top category |
-| `scores` | object | Confidence scores for all rubric categories |
-| `dialect` | string | Detected Arabic dialect |
-| `dialect_confidence` | number | Dialect confidence score (0–1) |
-| `language` | string | Detected language code |
-| `cached` | boolean | Whether served from semantic cache |
+| `id` | string | Unique classification identifier (`rcl_nw_` prefix) |
+| `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 (`ar`, `en`, `mixed`) |
+| `provider` | string | AI provider used (`allam` or `claude`) |
+| `model` | string | Model version used |
+| `fallback_used` | boolean | Whether the fallback provider was used |
+| `cost_usd` | number | Cost in USD |
+| `credits_used` | integer | Credits deducted |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `text` or `rubric`, invalid categories, more than 20 categories |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 402 | `insufficient_credits` | No credits remaining |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal or provider error |
diff --git a/api-reference/usage.mdx b/api-reference/usage.mdx
index d4b1b6b..9221455 100644
--- a/api-reference/usage.mdx
+++ b/api-reference/usage.mdx
@@ -13,17 +13,43 @@ 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`, `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.get({
+ from: '2025-01-01T00:00:00Z',
+ groupBy: 'endpoint'
+})
+```
+
+```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.usage.get(
+ from_date="2025-01-01T00:00:00Z",
+ group_by="endpoint"
+)
+```
+
+
+
## Response
### Success response (200)
@@ -32,53 +58,62 @@ 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": 2831,
+ "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
+ "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"
}
```
+
+ When `group_by=day`, the breakdown key is `by_day` and each item has a `date` field (e.g. `2025-01-15`) instead of `endpoint`.
+
+
### Result 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 |
+| `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 | The date range queried (`from` and `to`) |
+| `by_endpoint` | array | Breakdown by the selected `group_by` dimension |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Invalid `group_by` value |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal error |
diff --git a/billing.mdx b/billing.mdx
index e28bb54..3f11a18 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.
@@ -110,6 +110,13 @@ Credit purchases are non-refundable once credits have been consumed. For billing
Refunds for unused credits are processed to the original payment method within 5-10 business days.
+## Account deletion and billing
+
+When you request account deletion, NAWA automatically cancels your active Stripe subscription. Your subscription is set to cancel at the end of the current billing period, so you retain full access until then but are not charged again.
+
+- If you cancel the deletion within the 30-day grace period, your account stays active but the subscription remains set to cancel at period end. You can re-subscribe from your dashboard.
+- If the automatic cancellation fails for any reason, the NAWA team will cancel it manually after receiving your deletion confirmation email.
+
## FAQ
diff --git a/errors.mdx b/errors.mdx
index fdcf365..06d30c4 100644
--- a/errors.mdx
+++ b/errors.mdx
@@ -57,7 +57,7 @@ The request was malformed or missing required parameters.
**Common triggers:**
- Missing `text` field in `/v1/classify`
- - Missing `comment_id` in `/v1/comments/:id/reply`
+ - Missing `text` in `/v1/comments/reply`
**Fix:** Check the API reference for required parameters and include them all.
diff --git a/guides/batch-processing.mdx b/guides/batch-processing.mdx
index b938ea3..b351ecf 100644
--- a/guides/batch-processing.mdx
+++ b/guides/batch-processing.mdx
@@ -159,7 +159,7 @@ Estimate batch cost before processing:
| Endpoint | Cost | 1,000 comments | 10,000 comments | 100,000 comments |
|----------|------|----------------|-----------------|-------------------|
| `/v1/classify` | $0.006 | $6.00 | $60.00 | $600.00 |
-| `/v1/rubric/classify` | $0.003 | $3.00 | $30.00 | $300.00 |
+| `/v1/rubric-classify` | $0.003 | $3.00 | $30.00 | $300.00 |
With typical cache hit rates of 20–30%, actual costs are 20–30% lower than the estimates above.
diff --git a/guides/english-comment-classification.mdx b/guides/english-comment-classification.mdx
index ba58202..bc78032 100644
--- a/guides/english-comment-classification.mdx
+++ b/guides/english-comment-classification.mdx
@@ -75,9 +75,7 @@ for (const text of comments) {
## Reply generation
-The `/v1/comments/:id/reply` endpoint also handles English. Arabic replies match the detected dialect. English replies are natural and platform-appropriate.
-
-Set `language: "auto"` (default) to let NAWA match the commenter's language, or force a language with `language: "en"` or `language: "ar"`.
+The `/v1/comments/reply` endpoint also handles English. Pass the comment text in the `text` field, and NAWA auto-detects the language. Arabic replies match the detected dialect. English replies are natural and platform-appropriate.
## Pricing
diff --git a/openapi.yaml b/openapi.yaml
index 17706f2..83cc01b 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -457,13 +457,14 @@ 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 your own categories with
+ descriptions for domain-specific classification. Supports single-label and
+ multi-label modes.
tags: [Classification]
x-nawa-cost: "$0.003"
x-nawa-cache: true
@@ -480,7 +481,7 @@ paths:
type: string
minLength: 1
maxLength: 5000
- description: The comment text to classify
+ description: The text to classify
examples:
- "وين الترجمة العربية؟ مافهمت شي"
rubric:
@@ -492,27 +493,40 @@ 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: Category name
+ description:
+ type: string
+ description: Category description to guide the model
+ description: Array of category objects with name and optional description
+ multi_label:
+ type: boolean
+ default: false
+ description: "When true, returns all categories above the confidence threshold"
+ 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
@@ -536,26 +550,27 @@ paths:
schema:
$ref: "#/components/schemas/RubricSuccessResponse"
examples:
- translation_request:
- summary: Gulf Arabic translation request
- 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
- language: ar
- model: nagl-v1
- cached: false
- errors: []
- request_id: req_rub123abc456
+ translation_request:
+ summary: Gulf Arabic translation request
+ value:
+ success: true
+ result:
+ id: rcl_nw_a1b2c3d4e5f6
+ object: rubric_classification
+ categories:
+ - name: translation_request
+ confidence: 0.94
+ - name: technical_issue
+ confidence: 0.03
+ multi_label: false
+ language: ar
+ provider: allam
+ model: nagl-v1
+ fallback_used: false
+ cost_usd: 0.003
+ credits_used: 3
+ errors: []
+ request_id: req_rub123abc456
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -567,37 +582,36 @@ paths:
"500":
$ref: "#/components/responses/InternalError"
- /comments/{id}/reply:
+ /comments/reply:
post:
operationId: generateReply
summary: Generate reply to comment
description: |
- Generate a culturally-aware, dialect-matched reply to an Arabic comment.
+ Generate a culturally-aware, dialect-matched reply to a comment.
+ Classifies the comment and generates a 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
- schema:
- type: string
- examples:
- - "cmt_abc123"
requestBody:
- required: false
+ required: true
content:
application/json:
schema:
type: object
+ required: [text]
properties:
+ text:
+ type: string
+ minLength: 1
+ description: The comment text to generate a reply for
+ examples:
+ - "متى الجزء الثاني؟"
tone:
type: string
enum: [friendly, professional, casual, formal]
- default: friendly
+ default: professional
description: Reply tone
max_length:
type: integer
@@ -606,16 +620,21 @@ 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 to improve reply relevance
+ properties:
+ platform:
+ type: string
+ description: Source platform (e.g. youtube, instagram)
+ brand_voice:
+ type: string
+ description: Description of the brand voice or channel personality
example:
+ text: "متى الجزء الثاني؟"
tone: "friendly"
- context: "Tech review channel focused on smartphones"
+ context:
+ platform: "youtube"
+ brand_voice: "Tech review channel focused on smartphones"
responses:
"200":
description: Generated reply
@@ -639,19 +658,28 @@ paths:
schema:
$ref: "#/components/schemas/ReplySuccessResponse"
examples:
- gulf_reply:
- summary: Friendly reply to Gulf Arabic question
- value:
- success: true
- result:
- comment_id: cmt_abc123
- reply_text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔"
- reply_dialect: gulf
- tone: friendly
- original_intent: question
- original_dialect: gulf
- errors: []
- request_id: req_rep789xyz012
+ gulf_reply:
+ summary: Friendly reply to Gulf Arabic question
+ value:
+ success: true
+ result:
+ id: rpl_nw_a1b2c3d4e5f6
+ object: comment_reply
+ classification:
+ intent: question
+ sentiment: neutral
+ priority: medium
+ requires_response: true
+ reply:
+ text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔"
+ direction: rtl
+ tone: friendly
+ provider: allam
+ model: nagl-v1
+ cost_usd: 0.008
+ credits_used: 8
+ errors: []
+ request_id: req_rep789xyz012
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -681,10 +709,11 @@ paths:
schema:
$ref: "#/components/schemas/FeedbackInput"
example:
- request_id: "req_abc123def456"
- field: "dialect"
- expected_value: "levantine"
- comment: "This is Lebanese Arabic, not Gulf"
+ classification_id: "cls_nw_a1b2c3d4e5f6"
+ rating: "incorrect"
+ corrected_intent: ["question"]
+ corrected_sentiment: "neutral"
+ comment: "This is a question, not praise"
responses:
"200":
description: Feedback accepted
@@ -696,16 +725,18 @@ paths:
schema:
$ref: "#/components/schemas/FeedbackSuccessResponse"
examples:
- accepted:
- summary: Feedback accepted
- value:
- success: true
- result:
- feedback_id: fb_xyz789
- status: accepted
- message: "Thank you! Your feedback helps improve NAWA's accuracy."
- errors: []
- request_id: req_fb_abc123
+ accepted:
+ summary: Feedback accepted
+ value:
+ success: true
+ result:
+ id: fb_nw_xyz789
+ object: feedback
+ classification_id: cls_nw_a1b2c3d4e5f6
+ rating: incorrect
+ acknowledged: true
+ errors: []
+ request_id: req_fb_abc123
"400":
$ref: "#/components/responses/BadRequest"
"401":
@@ -726,12 +757,6 @@ paths:
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 +785,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 +794,14 @@ 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
+ minimum: 0
+ default: 0
+ description: Number of results to skip for pagination
responses:
"200":
description: List of classified comments
@@ -807,27 +813,27 @@ paths:
schema:
$ref: "#/components/schemas/CommentsListSuccessResponse"
examples:
- youtube_questions:
- summary: YouTube questions
- value:
- success: true
- result:
- comments:
- - id: cmt_abc123
- text: "متى الجزء الثاني؟"
- platform: youtube
- intent: question
- sentiment: neutral
- dialect: gulf
- toxicity: none
- created_at: "2025-01-15T10:30:00Z"
- channel_id: ch_123
- pagination:
- has_more: true
- next_cursor: cur_def456
- total_count: 1523
- errors: []
- request_id: req_lst_abc123
+ youtube_questions:
+ summary: YouTube questions
+ value:
+ success: true
+ result:
+ comments:
+ - id: cls_abc123
+ request_id: req_nw_a1b2c3
+ text: "متى الجزء الثاني؟"
+ platform: youtube
+ intent: question
+ sentiment: neutral
+ dialect: gulf
+ created_at: "2025-01-15T10:30:00Z"
+ pagination:
+ total: 1523
+ limit: 25
+ offset: 0
+ has_more: true
+ errors: []
+ request_id: req_lst_abc123
"401":
$ref: "#/components/responses/Unauthorized"
"429":
@@ -847,19 +853,6 @@ paths:
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 +872,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 +886,40 @@ paths:
schema:
$ref: "#/components/schemas/AnalyticsSuccessResponse"
examples:
- monthly:
- summary: Monthly YouTube analytics
- 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
- errors: []
- request_id: req_ana_abc123
+ by_endpoint:
+ summary: Analytics grouped by endpoint
+ value:
+ success: true
+ result:
+ analytics:
+ total_requests: 12450
+ total_cost: 68.70
+ cache_hits: 2831
+ cache_hit_rate: 0.23
+ avg_latency_ms: 820
+ success_rate: 0.98
+ error_count: 249
+ 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: 780
+ - endpoint: "/v1/comments/reply"
+ requests: 1750
+ cost: 14.00
+ avg_latency_ms: 1200
+ errors: []
+ request_id: req_ana_abc123
"401":
$ref: "#/components/responses/Unauthorized"
"429":
@@ -957,29 +945,30 @@ paths:
content:
application/json:
schema:
- $ref: "#/components/schemas/HealthSuccessResponse"
+ $ref: "#/components/schemas/HealthResult"
examples:
- healthy:
- summary: All services operational
- value:
- success: true
- result:
- status: healthy
- version: "1.0.0"
- services:
- api: operational
- classification: operational
- database: operational
- cache: operational
- timestamp: "2025-01-15T12:00:00Z"
- errors: []
- request_id: req_hlt_abc123
+ healthy:
+ summary: All services healthy
+ value:
+ status: healthy
+ version: v1
+ services:
+ database:
+ status: healthy
+ latency_ms: 12
+ redis:
+ status: healthy
+ latency_ms: 3
+ nagl:
+ status: healthy
+ timestamp: "2025-01-15T12:00:00Z"
+ latency_ms: 15
"503":
- description: One or more services degraded
+ description: One or more services degraded or down
content:
application/json:
schema:
- $ref: "#/components/schemas/HealthSuccessResponse"
+ $ref: "#/components/schemas/HealthResult"
/usage:
get:
@@ -999,22 +988,22 @@ paths:
schema:
type: string
format: date-time
- description: Start date (ISO 8601). Default: start of current month.
+ description: "Start date (ISO 8601). Default: start of current month."
- name: to
in: query
required: false
schema:
type: string
format: date-time
- description: End date (ISO 8601). Default: now.
+ description: "End date (ISO 8601). Default: now."
- name: group_by
in: query
required: false
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
@@ -1026,39 +1015,34 @@ paths:
schema:
$ref: "#/components/schemas/UsageSuccessResponse"
examples:
- by_endpoint:
- summary: Usage grouped by endpoint
- 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
- by_endpoint:
- - endpoint: "/v1/classify"
- requests: 8500
- cost: 51.00
- cache_hits: 1955
- avg_latency_ms: 820
- - endpoint: "/v1/translate"
- requests: 1200
- cost: 6.00
- cache_hits: 276
- avg_latency_ms: 1100
- - endpoint: "/v1/comments/:id/reply"
- requests: 1750
- cost: 14.00
- cache_hits: 0
- avg_latency_ms: 1200
- balance:
- current: 31.30
- credits_purchased: 100.00
- credits_used: 68.70
- errors: []
- request_id: req_usg_abc123
+ by_endpoint:
+ summary: Usage grouped by endpoint
+ value:
+ success: true
+ result:
+ usage:
+ total_requests: 12450
+ total_cost: 68.70
+ cache_hits: 2831
+ cache_hit_rate: 0.23
+ period:
+ from: "2025-01-01T00:00:00Z"
+ to: null
+ by_endpoint:
+ - endpoint: "/v1/classify"
+ requests: 8500
+ cost: 51.00
+ cache_hits: 1955
+ - endpoint: "/v1/translate"
+ requests: 1200
+ cost: 6.00
+ cache_hits: 276
+ - endpoint: "/v1/comments/reply"
+ requests: 1750
+ cost: 14.00
+ cache_hits: 0
+ errors: []
+ request_id: req_usg_abc123
"401":
$ref: "#/components/responses/Unauthorized"
"429":
@@ -1687,97 +1671,141 @@ components:
RubricResult:
type: object
+ required: [id, object, categories, multi_label, language, provider, model, fallback_used, 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: Classification ID (rcl_nw_xxx format)
+ object:
type: string
- enum: [gulf, egyptian, levantine, msa]
- dialect_confidence:
- type: number
- minimum: 0
- maximum: 1
+ const: rubric_classification
+ categories:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ confidence:
+ type: number
+ minimum: 0
+ maximum: 1
+ description: 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
+ description: AI provider used (allam or claude)
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_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
+ description: Classification of the original comment
+ properties:
+ intent:
+ type: string
+ enum: [question, complaint, praise, suggestion, spam, other]
+ sentiment:
+ type: string
+ enum: [positive, negative, neutral, mixed]
+ priority:
+ type: string
+ enum: [low, medium, high]
+ requires_response:
+ type: boolean
+ reply:
+ type: object
+ description: The generated reply
+ properties:
+ text:
+ type: string
+ description: The generated reply text
+ direction:
+ type: string
+ enum: [rtl, ltr]
+ description: Text direction (rtl for Arabic, ltr for English)
+ tone:
+ type: string
+ enum: [friendly, professional, casual, formal]
+ provider:
type: string
- enum: [question, complaint, praise, suggestion, spam, other]
- description: Detected intent of the original comment
- original_dialect:
+ description: AI provider used (allam or claude)
+ 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 from the original response
+ rating:
type: string
- enum: [intent, sentiment, dialect, toxicity, category]
- description: The field to correct
- expected_value:
+ enum: [correct, incorrect, partial]
+ description: Your assessment of the classification accuracy
+ corrected_intent:
+ type: array
+ items:
+ type: string
+ description: Array of correct intent labels if the classification was 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: Free-text explanation of why this correction is needed
FeedbackResult:
type: object
properties:
- feedback_id:
+ id:
type: string
- status:
+ object:
+ type: string
+ const: feedback
+ classification_id:
type: string
- enum: [accepted]
- message:
+ rating:
type: string
+ enum: [correct, incorrect, partial]
+ acknowledged:
+ type: boolean
CommentItem:
type: object
properties:
id:
type: string
+ request_id:
+ type: string
+ description: Original API request ID
text:
type: string
platform:
@@ -1792,72 +1820,67 @@ components:
dialect:
type: string
enum: [gulf, egyptian, levantine, msa]
- toxicity:
- type: string
- enum: [none, mild, moderate, severe]
created_at:
type: string
format: date-time
- channel_id:
- type: string
Pagination:
type: object
properties:
+ total:
+ type: integer
+ description: Total matching comments
+ limit:
+ type: integer
+ description: Results per page
+ 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
+ description: Total cost in USD
+ cache_hits:
+ type: integer
+ cache_hit_rate:
+ type: number
+ minimum: 0
+ maximum: 1
+ avg_latency_ms:
+ type: integer
+ success_rate:
+ type: number
+ minimum: 0
+ maximum: 1
+ 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
+ nullable: true
+ to:
+ type: string
+ nullable: true
HealthResult:
type: object
@@ -1870,67 +1893,61 @@ components:
services:
type: object
properties:
- api:
- type: string
- enum: [operational, degraded, down]
- classification:
- type: string
- enum: [operational, degraded, down]
database:
- type: string
- enum: [operational, degraded, down]
- cache:
- type: string
- enum: [operational, degraded, down]
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [healthy, degraded, down]
+ latency_ms:
+ type: integer
+ redis:
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [healthy, degraded, down]
+ latency_ms:
+ type: integer
+ nagl:
+ type: object
+ properties:
+ status:
+ type: string
+ enum: [healthy, down]
timestamp:
type: string
format: date-time
+ latency_ms:
+ type: integer
+ 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
- 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:
- 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
+ nullable: true
+ to:
+ type: string
+ nullable: true
WebhookEndpoint:
type: object
diff --git a/webhooks.mdx b/webhooks.mdx
index 2ef4922..4838a40 100644
--- a/webhooks.mdx
+++ b/webhooks.mdx
@@ -13,7 +13,7 @@ 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 |
+| `comment.replied` | A reply was posted | After `/v1/comments/reply` succeeds |
| `credits.low` | Credit balance below threshold | Balance drops below $5 |
| `credits.exhausted` | Credit balance is $0 | Balance reaches $0 |