From 4b61d8c6e50356abb6f235058d3ec5ba28f9bb4a Mon Sep 17 00:00:00 2001
From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com>
Date: Mon, 20 Apr 2026 18:23:10 +0000
Subject: [PATCH] fix(api-docs): sync all endpoint docs with current codebase
implementations
Reconcile 7 API reference pages and OpenAPI spec against the actual
edge function handlers in supabase/functions/. Key corrections:
- rubric-classify: categories is array of objects (name+description),
not strings; add multi_label and confidence_threshold params; fix
response shape to rubric_classification object
- comments/reply: endpoint takes text in body (not path param id);
default tone is professional not friendly; response nests
classification + reply objects
- analytics: group_by accepts endpoint/day/provider (not day/week/month);
response returns analytics summary with endpoint/provider distribution
- health: services are database/redis/nagl with latency_ms (not
api/classification/database/cache); version is v1; 503 for degraded
- usage: group_by accepts endpoint/day only; response shape uses
usage summary + by_endpoint/by_day breakdown
- comments-list: uses offset pagination (not cursor-based)
- report: sandbox/free keys blocked entirely; daily cap is 20 reports;
fix auth header example to use live key
- openapi.yaml: fix pre-existing YAML indentation bug on /usage
description field
Generated-By: mintlify-agent
---
api-reference/analytics.mdx | 146 +++++++---
api-reference/comments-list.mdx | 84 ++++--
api-reference/comments-reply.mdx | 118 +++++---
api-reference/health.mdx | 94 ++++---
api-reference/report.mdx | 10 +-
api-reference/rubric-classify.mdx | 160 +++++++----
api-reference/usage.mdx | 137 ++++++++--
openapi.yaml | 438 ++++++++++++++++++------------
8 files changed, 801 insertions(+), 386 deletions(-)
diff --git a/api-reference/analytics.mdx b/api-reference/analytics.mdx
index c2879c7..b5cb47e 100644
--- a/api-reference/analytics.mdx
+++ b/api-reference/analytics.mdx
@@ -1,11 +1,11 @@
---
title: "Analytics"
sidebarTitle: "GET /v1/analytics"
-description: "Aggregated analytics on comment trends, sentiment shifts, and engagement patterns."
+description: "Aggregated analytics on API usage, cache performance, and provider distribution."
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 including request counts, costs, cache hit rates, latency, and provider distribution. 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" \
- -H "Authorization: Bearer nawa_test_sk_xxx"
+
+
+```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_live_sk_xxx"
+```
+
+```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.analytics({
+ from: '2025-01-01T00:00:00Z',
+ to: '2025-01-31T23:59:59Z',
+ groupBy: 'endpoint',
+})
```
+```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.analytics(
+ from_date="2025-01-01T00:00:00Z",
+ to_date="2025-01-31T23:59:59Z",
+ group_by="endpoint",
+)
+```
+
+
+
## Response
### Success response (200)
@@ -34,45 +60,42 @@ 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
+ "analytics": {
+ "total_requests": 12450,
+ "total_cost": 68.70,
+ "cache_hits": 2231,
+ "cache_hit_rate": 0.18,
+ "avg_latency_ms": 820,
+ "success_rate": 0.98,
+ "error_count": 249,
+ "endpoint_distribution": {
+ "/v1/classify": 8500,
+ "/v1/translate": 1200,
+ "/v1/detect": 950,
+ "/v1/moderate": 800,
+ "/v1/comments/reply": 1000
},
- "sentiment": {
- "positive": 2100,
- "negative": 890,
- "neutral": 1231,
- "mixed": 300
+ "provider_distribution": {
+ "claude": 9200,
+ "allam": 3250
},
- "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": 8500,
+ "cost": 51.00,
+ "avg_latency_ms": 780
+ },
{
- "period": "2025-01-06/2025-01-12",
- "total": 1123,
- "sentiment_score": 0.72,
- "top_intent": "praise"
+ "endpoint": "/v1/translate",
+ "requests": 1200,
+ "cost": 6.00,
+ "avg_latency_ms": 1100
}
]
},
@@ -80,3 +103,38 @@ curl "https://api.trynawa.com/v1/analytics?platform=youtube&from=2025-01-01T00:0
"request_id": "req_ana_abc123"
}
```
+
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `analytics.total_requests` | integer | Total API requests in the period |
+| `analytics.total_cost` | number | Total cost in USD |
+| `analytics.cache_hits` | integer | Number of cache hits |
+| `analytics.cache_hit_rate` | number | Ratio of cache hits to total requests (0 to 1) |
+| `analytics.avg_latency_ms` | integer | Average response latency in milliseconds |
+| `analytics.success_rate` | number | Ratio of 2xx responses to total requests (0 to 1) |
+| `analytics.error_count` | integer | Number of 4xx/5xx responses |
+| `analytics.endpoint_distribution` | object | Request count per endpoint |
+| `analytics.provider_distribution` | object | Request count per AI provider |
+| `analytics.period` | object | The `from` and `to` dates applied (null if not specified) |
+
+The `by_endpoint`, `by_day`, or `by_provider` array (matching your `group_by` choice) contains objects with:
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `endpoint` / `day` / `provider` | string | The group key |
+| `requests` | integer | Request count for this group |
+| `cost` | number | Total cost in USD for this group |
+| `avg_latency_ms` | integer | Average latency for this group |
+
+### 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 |
+
+See [Errors](/errors) for the full envelope shape and all error codes.
diff --git a/api-reference/comments-list.mdx b/api-reference/comments-list.mdx
index 1e18d08..a32edd8 100644
--- a/api-reference/comments-list.mdx
+++ b/api-reference/comments-list.mdx
@@ -1,11 +1,11 @@
---
-title: "List Comments"
+title: "List comments"
sidebarTitle: "GET /v1/comments"
description: "Retrieve and filter classified comments with pagination."
api: "GET https://api.trynawa.com/v1/comments"
---
-Retrieve classified comments with filtering and pagination. This endpoint is **free**.
+Retrieve classified comments with filtering and offset-based pagination. 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 to 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" \
- -H "Authorization: Bearer nawa_test_sk_xxx"
+curl "https://api.trynawa.com/v1/comments?platform=youtube&intent=question&limit=10&offset=0" \
+ -H "Authorization: Bearer nawa_live_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)
```
@@ -71,23 +68,56 @@ for comment in nawa.comments.list(platform="youtube"):
"comments": [
{
"id": "cmt_abc123",
+ "request_id": "req_nw_9cfb228162d6",
"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 | Comment ID |
+| `comments[].request_id` | string | Original classification request ID |
+| `comments[].text` | string | Comment text |
+| `comments[].platform` | string | Source platform |
+| `comments[].intent` | string | Classified intent |
+| `comments[].sentiment` | string | Classified 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 exist |
+
+
+ Comment history storage may not be enabled on all accounts. If the underlying table does not exist yet, the endpoint returns an empty array with a `message` field explaining the status.
+
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Invalid `platform`, `intent`, `sentiment`, or `dialect` value |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal error |
+
+See [Errors](/errors) for the full envelope shape and all error codes.
diff --git a/api-reference/comments-reply.mdx b/api-reference/comments-reply.mdx
index d6321d7..438857b 100644
--- a/api-reference/comments-reply.mdx
+++ b/api-reference/comments-reply.mdx
@@ -1,59 +1,83 @@
---
-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 one call. 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`).
+ Cost: **$0.008** per request (8 credits). Balance is checked before request processing.
## Request
-### Path parameters
+### Body parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `id` | string | Yes | The comment ID to reply to. |
+| `text` | string | Yes | The comment text to classify and reply to. Must be non-empty. |
+| `tone` | string | No | Reply tone: `friendly`, `professional`, `casual`, `formal`. Default: `professional`. |
+| `max_length` | integer | No | Maximum reply length in characters (1 to 2000). Default: `500`. |
+| `context` | object | No | Additional context for reply generation. |
+| `context.platform` | string | No | Source platform (e.g. `youtube`, `instagram`). |
+| `context.brand_voice` | string | No | Brand voice description to match in the reply. |
-### Body parameters
+### Query parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `tone` | string | No | Reply tone: `friendly`, `professional`, `casual`, `formal`. Default: `friendly`. |
-| `max_length` | integer | No | Maximum reply length in characters. Default: 500. |
-| `context` | string | No | Additional context about the channel or video to improve reply relevance. |
-| `language` | string | No | Force reply language: `ar`, `en`, `auto`. Default: `auto` (matches commenter's language). |
+| `provider` | string | No | Force a specific AI provider for A/B testing. One of `claude`, `gemini`, `allam`. |
### Example request
```bash cURL
-curl -X POST https://api.trynawa.com/v1/comments/cmt_abc123/reply \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
+curl -X POST https://api.trynawa.com/v1/comments/reply \
+ -H "Authorization: Bearer nawa_live_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
+ "text": "متى الجزء الثاني؟",
"tone": "friendly",
- "context": "Tech review channel focused on smartphones"
+ "max_length": 500,
+ "context": {
+ "platform": "youtube",
+ "brand_voice": "Tech review channel focused on smartphones"
+ }
}'
```
```typescript TypeScript
-const { data, error } = await nawa.comments.reply('cmt_abc123', {
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.comments.reply({
+ text: 'متى الجزء الثاني؟',
tone: 'friendly',
- context: 'Tech review channel focused on smartphones'
+ maxLength: 500,
+ 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"
+ max_length=500,
+ context={
+ "platform": "youtube",
+ "brand_voice": "Tech review channel focused on smartphones",
+ },
)
```
@@ -67,12 +91,23 @@ 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_abc123def456",
+ "object": "comment_reply",
+ "classification": {
+ "intent": ["question"],
+ "sentiment": "neutral",
+ "priority": "medium",
+ "requires_response": true
+ },
+ "reply": {
+ "text": "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔",
+ "direction": "rtl",
+ "tone": "friendly"
+ },
+ "provider": "claude",
+ "model": "claude-sonnet-4-5-20250929",
+ "cost_usd": 0.008,
+ "credits_used": 8
},
"errors": [],
"request_id": "req_rep789xyz012"
@@ -83,9 +118,28 @@ result = nawa.comments.reply(
| Field | Type | Description |
|-------|------|-------------|
-| `comment_id` | string | The comment that was replied to |
-| `reply_text` | string | The generated reply text |
-| `reply_dialect` | string \| null | Dialect used in the reply (matches original). `null` for English replies. |
-| `tone` | string | The tone used for the reply |
-| `original_intent` | string | Detected intent of the original comment |
-| `original_dialect` | string | Detected dialect of the original comment |
+| `id` | string | Reply ID in `rpl_nw_xxx` format |
+| `object` | string | Always `"comment_reply"` |
+| `classification.intent` | string[] | Detected intent labels for the original comment |
+| `classification.sentiment` | string | Detected sentiment: `positive`, `negative`, `neutral`, `mixed` |
+| `classification.priority` | string | Triage priority: `low`, `medium`, `high` |
+| `classification.requires_response` | boolean | Whether the model judges this comment needs 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 |
+| `model` | string | Model version used |
+| `cost_usd` | number | Always `0.008` for this endpoint |
+| `credits_used` | integer | Always `8` for this endpoint |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing or empty `text`. Invalid `tone` or `max_length`. Invalid `provider`. |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 402 | `insufficient_credits` | Insufficient credit balance (checked before request processing) |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal or provider error |
+
+See [Errors](/errors) for the full envelope shape and all error codes.
diff --git a/api-reference/health.mdx b/api-reference/health.mdx
index ca110e8..9f39ba2 100644
--- a/api-reference/health.mdx
+++ b/api-reference/health.mdx
@@ -1,62 +1,94 @@
---
-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. This endpoint is **free** and does **not** require authentication. Returns HTTP 200 when all services are healthy, or HTTP 503 when any service is degraded or down.
## Request
-```bash
+
+
+```bash cURL
curl https://api.trynawa.com/v1/health
```
+```typescript TypeScript
+const response = await fetch('https://api.trynawa.com/v1/health')
+const health = await response.json()
+
+if (response.status === 503) {
+ console.warn('NAWA API degraded:', health.services)
+}
+```
+
+```python Python
+import requests
+
+response = requests.get("https://api.trynawa.com/v1/health")
+health = response.json()
+
+if response.status_code == 503:
+ print(f"NAWA API degraded: {health['services']}")
+```
+
+
+
## Response
### Healthy response (200)
```json
{
- "success": true,
- "result": {
- "status": "healthy",
- "version": "1.0.0",
- "services": {
- "api": "operational",
- "classification": "operational",
- "database": "operational",
- "cache": "operational"
- },
- "timestamp": "2025-01-15T12:00:00Z"
+ "status": "healthy",
+ "version": "v1",
+ "services": {
+ "database": {"status": "healthy", "latency_ms": 12},
+ "redis": {"status": "healthy", "latency_ms": 3},
+ "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)
```json
{
- "success": true,
- "result": {
- "status": "degraded",
- "version": "1.0.0",
- "services": {
- "api": "operational",
- "classification": "degraded",
- "database": "operational",
- "cache": "operational"
- },
- "timestamp": "2025-01-15T12:00:00Z"
+ "status": "degraded",
+ "version": "v1",
+ "services": {
+ "database": {"status": "healthy", "latency_ms": 14},
+ "redis": {"status": "down", "latency_ms": 5001},
+ "nagl": {"status": "healthy"}
},
- "errors": [],
- "request_id": "req_hlt_def456"
+ "timestamp": "2025-01-15T12:00:00.000Z",
+ "latency_ms": 5016
}
```
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `status` | string | Overall status: `healthy` or `degraded` |
+| `version` | string | API version (currently `v1`) |
+| `services.database` | object | Database connectivity status with `latency_ms` |
+| `services.redis` | object | Redis cache status with `latency_ms` |
+| `services.nagl` | object | NAGL classification engine status |
+| `timestamp` | string | ISO 8601 timestamp of the check |
+| `latency_ms` | integer | Total health check duration in milliseconds |
+
+Each service object contains:
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `status` | string | `healthy`, `degraded`, or `down` |
+| `latency_ms` | integer | Response time in milliseconds (when available) |
+
- For real-time status monitoring, visit [status.trynawa.com](https://status.trynawa.com).
+ The health endpoint returns `200` only when all services are `healthy`. Any `degraded` or `down` service triggers `503`. For real-time status monitoring, visit [status.trynawa.com](https://status.trynawa.com).
diff --git a/api-reference/report.mdx b/api-reference/report.mdx
index 74662dc..c63c719 100644
--- a/api-reference/report.mdx
+++ b/api-reference/report.mdx
@@ -17,9 +17,13 @@ Generate a structured audience intelligence report from comment data. The report
| Header | Required | Description |
|--------|----------|-------------|
-| `Authorization` | Yes | `Bearer nawa_live_sk_xxx` or `Bearer nawa_test_sk_xxx` |
+| `Authorization` | Yes | `Bearer nawa_live_sk_xxx` (live key required) |
| `Content-Type` | Yes | `application/json` |
+
+ Reports require a **live** API key. Free/sandbox keys (`nawa_test_sk_xxx`) are blocked from this endpoint because the minimum cost (500 credits) exceeds the free tier's 100-request lifetime cap. Purchase credits at [trynawa.com/developers/keys](https://trynawa.com/developers/keys) to get a live key.
+
+
### Body parameters
| Parameter | Type | Required | Description |
@@ -39,7 +43,7 @@ Generate a structured audience intelligence report from comment data. The report
```bash cURL
curl -X POST https://api.trynawa.com/v1/report \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
+ -H "Authorization: Bearer nawa_live_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
"comments": [
@@ -195,7 +199,7 @@ Every percentage in report tables carries one of three tags:
| 400 | `invalid_request_error` | Missing/invalid `comments`, wrong `tier`, fewer than 10 comments |
| 401 | `authentication_error` | Invalid or missing API key |
| 402 | `insufficient_credits` | Not enough credits (need 500 for Basic, 1500 for Pro) |
-| 429 | `rate_limit_error` | Rate limit exceeded (5 reports/minute) |
+| 429 | `rate_limit_error` | Rate limit exceeded (5 reports/minute, 20 reports/day) |
| 500 | `api_error` | Internal or provider error |
### Report tier comparison
diff --git a/api-reference/rubric-classify.mdx b/api-reference/rubric-classify.mdx
index 4489175..f31a5d0 100644
--- a/api-reference/rubric-classify.mdx
+++ b/api-reference/rubric-classify.mdx
@@ -1,5 +1,5 @@
---
-title: "Rubric Classify"
+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"
@@ -8,7 +8,7 @@ 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.
- 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,11 +17,19 @@ 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. |
+| `text` | string | Yes | The comment text to classify. |
| `rubric` | object | Yes | The rubric definition with categories and criteria. |
-| `rubric.categories` | string[] | Yes | List of category names to classify against. |
-| `rubric.descriptions` | object | No | Category descriptions to guide the model. Keys are category names, values are description strings. |
-| `platform` | string | No | Source platform for context. |
+| `rubric.categories` | object[] | Yes | Array of category objects. Each must have a `name` and optional `description`. Max 20 categories. |
+| `rubric.categories[].name` | string | Yes | Category name. Must be non-empty. |
+| `rubric.categories[].description` | string | No | Description to guide the model on what this category means. |
+| `rubric.multi_label` | boolean | No | When `true`, returns all matching categories above the confidence threshold. Default: `false` (single best match). |
+| `rubric.confidence_threshold` | number | No | Minimum confidence score (0 to 1) for a category to be included. Default: `0.5`. |
+
+### Query parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `provider` | string | No | Force a specific AI provider for A/B testing. One of `claude`, `gemini`, `allam`. |
### Example request
@@ -29,52 +37,60 @@ Classify a comment against a custom rubric. Define your own categories and scori
```bash cURL
curl -X POST https://api.trynawa.com/v1/rubric/classify \
- -H "Authorization: Bearer nawa_test_sk_xxx" \
+ -H "Authorization: Bearer nawa_live_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'
- }
+ 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'
})
```
```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,20 +104,18 @@ result = nawa.rubric.classify(
{
"success": true,
"result": {
- "text": "وين الترجمة العربية؟ مافهمت شي",
- "category": "translation_request",
- "category_confidence": 0.94,
- "scores": {
- "translation_request": 0.94,
- "technical_issue": 0.03,
- "content_feedback": 0.02,
- "off_topic": 0.01
- },
- "dialect": "gulf",
- "dialect_confidence": 0.91,
+ "id": "rcl_nw_pon1yom9bkfe",
+ "object": "rubric_classification",
+ "categories": [
+ {"name": "translation_request", "confidence": 0.94}
+ ],
+ "multi_label": false,
"language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "provider": "claude",
+ "model": "claude-sonnet-4-5-20250929",
+ "fallback_used": false,
+ "cost_usd": 0.003,
+ "credits_used": 3
},
"errors": [],
"request_id": "req_rub123abc456"
@@ -112,10 +126,54 @@ 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 | Classification ID in `rcl_nw_xxx` format |
+| `object` | string | Always `"rubric_classification"` |
+| `categories` | object[] | Matched categories with confidence scores. When `multi_label` is `false`, contains only the top match. |
+| `categories[].name` | string | Category name from your rubric |
+| `categories[].confidence` | number | Confidence score (0 to 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 version used |
+| `fallback_used` | boolean | Whether the fallback provider was used |
+| `cost_usd` | number | Always `0.003` for this endpoint |
+| `credits_used` | integer | Always `3` for this endpoint |
+
+### Multi-label example
+
+When `multi_label` is `true`, multiple categories can be returned:
+
+```json
+{
+ "success": true,
+ "result": {
+ "id": "rcl_nw_abc123def456",
+ "object": "rubric_classification",
+ "categories": [
+ {"name": "translation_request", "confidence": 0.91},
+ {"name": "content_feedback", "confidence": 0.62}
+ ],
+ "multi_label": true,
+ "language": "ar",
+ "provider": "claude",
+ "model": "claude-sonnet-4-5-20250929",
+ "fallback_used": false,
+ "cost_usd": 0.003,
+ "credits_used": 3
+ },
+ "errors": [],
+ "request_id": "req_rub456def789"
+}
+```
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `text` or `rubric`. Empty or invalid categories. More than 20 categories. Invalid `provider`. |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 402 | `insufficient_credits` | No credits remaining (balance checked before request processing) |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal or provider error |
+
+See [Errors](/errors) for the full envelope shape and all error codes.
diff --git a/api-reference/usage.mdx b/api-reference/usage.mdx
index d4b1b6b..6421b81 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,149 @@ 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. |
+| `from` | string | No | Start date (ISO 8601). Default: all time. |
| `to` | string | No | End date (ISO 8601). Default: now. |
-| `group_by` | string | No | Group by: `day`, `week`, `month`, `endpoint`. Default: `day`. |
+| `group_by` | string | No | Group by: `endpoint` 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"
+ -H "Authorization: Bearer nawa_live_sk_xxx"
+```
+
+```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.usage({
+ from: '2025-01-01T00:00:00Z',
+ groupBy: 'endpoint',
+})
+```
+
+```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.usage(
+ from_date="2025-01-01T00:00:00Z",
+ group_by="endpoint",
+)
```
+
+
## Response
-### Success response (200)
+### Grouped by endpoint (200)
```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": 2231,
+ "cache_hit_rate": 0.18,
+ "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
+ "cache_hits": 0
},
{
- "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"
}
```
+### Grouped by day (200)
+
+```json
+{
+ "success": true,
+ "result": {
+ "usage": {
+ "total_requests": 350,
+ "total_cost": 2.10,
+ "cache_hits": 63,
+ "cache_hit_rate": 0.18,
+ "period": {
+ "from": "2025-01-01T00:00:00Z",
+ "to": "2025-01-07T23:59:59Z"
+ }
+ },
+ "by_day": [
+ {
+ "date": "2025-01-07",
+ "requests": 82,
+ "cost": 0.49,
+ "cache_hits": 15
+ },
+ {
+ "date": "2025-01-06",
+ "requests": 65,
+ "cost": 0.39,
+ "cache_hits": 12
+ }
+ ]
+ },
+ "errors": [],
+ "request_id": "req_usg_def456"
+}
+```
+
### 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 | Ratio of cache hits to total requests (0 to 1) |
+| `usage.period` | object | The `from` and `to` dates applied (null if not specified) |
+
+The `by_endpoint` or `by_day` array (matching your `group_by` choice) contains:
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `endpoint` or `date` | string | The group key |
+| `requests` | integer | Request count |
+| `cost` | number | Total cost in USD |
+| `cache_hits` | integer | Number of cache hits |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Invalid `group_by` value (must be `endpoint` or `day`) |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 429 | `rate_limit_error` | Rate limit exceeded |
+| 500 | `api_error` | Internal error |
+
+See [Errors](/errors) for the full envelope shape and all error codes.
diff --git a/openapi.yaml b/openapi.yaml
index 2c6201d..ff36fa1 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -482,11 +482,20 @@ paths:
summary: Classify with custom rubric
description: |
Classify text against a user-defined rubric. Define your own categories and
- scoring criteria for domain-specific classification.
+ scoring criteria for domain-specific classification. Balance is checked
+ before request processing.
tags: [Classification]
x-nawa-cost: "$0.003"
- x-nawa-cache: true
+ x-nawa-cache: false
x-nawa-free: false
+ parameters:
+ - name: provider
+ in: query
+ required: false
+ schema:
+ type: string
+ enum: [claude, gemini, allam]
+ description: Force a specific AI provider for A/B testing.
requestBody:
required: true
content:
@@ -498,7 +507,6 @@ paths:
text:
type: string
minLength: 1
- maxLength: 5000
description: The comment text to classify
examples:
- "وين الترجمة العربية؟ مافهمت شي"
@@ -511,27 +519,41 @@ 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
+ minLength: 1
+ description: Category name
+ description:
+ type: string
+ description: Category description to guide the model
+ description: Array of category objects to classify against
+ multi_label:
+ type: boolean
+ default: false
+ description: When true, returns all matching categories above the confidence threshold
+ confidence_threshold:
+ type: number
+ minimum: 0
+ maximum: 1
+ default: 0.5
+ description: Minimum confidence score for a category to be included
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
@@ -540,10 +562,8 @@ paths:
$ref: "#/components/headers/X-Request-Id"
X-NAWA-Provider:
$ref: "#/components/headers/X-NAWA-Provider"
- X-NAWA-Cache:
- $ref: "#/components/headers/X-NAWA-Cache"
- X-NAWA-Balance:
- $ref: "#/components/headers/X-NAWA-Balance"
+ X-NAWA-Latency:
+ $ref: "#/components/headers/X-NAWA-Latency"
X-RateLimit-Limit:
$ref: "#/components/headers/X-RateLimit-Limit"
X-RateLimit-Remaining:
@@ -560,19 +580,18 @@ paths:
value:
success: true
result:
- text: "وين الترجمة العربية؟ مافهمت شي"
- category: translation_request
- category_confidence: 0.94
- scores:
- translation_request: 0.94
- technical_issue: 0.03
- content_feedback: 0.02
- off_topic: 0.01
- dialect: gulf
- dialect_confidence: 0.91
+ id: rcl_nw_pon1yom9bkfe
+ object: rubric_classification
+ categories:
+ - name: translation_request
+ confidence: 0.94
+ multi_label: false
language: ar
- model: nagl-v1
- cached: false
+ provider: claude
+ model: claude-sonnet-4-5-20250929
+ fallback_used: false
+ cost_usd: 0.003
+ credits_used: 3
errors: []
request_id: req_rub123abc456
"400":
@@ -598,8 +617,9 @@ paths:
Reports open with a TL;DR verdict, use plain-English section titles, and tag
every percentage as COUNT (exact from data) or ESTIMATE (analyst judgement).
- Requires a live API key. Sandbox/free keys are blocked (minimum cost is 500 credits).
- Rate limited to 5 reports per minute, 20 per day.
+ Requires a **live** API key. Free/sandbox keys are blocked because the
+ minimum cost (500 credits) exceeds the free tier's 100-request lifetime cap.
+ Rate limited to 5 reports per minute and 20 reports per day per user.
tags: [Intelligence]
x-nawa-cost: "$0.50 (basic) / $1.50 (pro)"
x-nawa-cache: false
@@ -759,37 +779,44 @@ paths:
"500":
$ref: "#/components/responses/InternalError"
- /comments/{id}/reply:
+ /comments/reply:
post:
operationId: generateReply
- summary: Generate reply to comment
+ summary: Classify and reply to comment
description: |
- Generate a culturally-aware, dialect-matched reply to an Arabic comment.
- Replies match the commenter's dialect and are contextually appropriate.
+ Classify a comment and generate a culturally-aware, dialect-matched reply
+ in a single call. For Arabic comments, replies match the detected dialect
+ (Gulf, Egyptian, Levantine, MSA). Balance is checked before request processing.
tags: [Reply]
x-nawa-cost: "$0.008"
- x-nawa-cache: true
+ x-nawa-cache: false
x-nawa-free: false
parameters:
- - name: id
- in: path
- required: true
- description: The comment ID to reply to
+ - name: provider
+ in: query
+ required: false
schema:
type: string
- examples:
- - "cmt_abc123"
+ enum: [claude, gemini, allam]
+ description: Force a specific AI provider for A/B testing.
requestBody:
- required: false
+ required: true
content:
application/json:
schema:
type: object
+ required: [text]
properties:
+ text:
+ type: string
+ minLength: 1
+ description: The comment text to classify and reply to
+ examples:
+ - "متى الجزء الثاني؟"
tone:
type: string
enum: [friendly, professional, casual, formal]
- default: friendly
+ default: professional
description: Reply tone
max_length:
type: integer
@@ -798,28 +825,32 @@ 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 for reply generation
+ properties:
+ platform:
+ type: string
+ description: Source platform (e.g. youtube, instagram)
+ brand_voice:
+ type: string
+ description: Brand voice description to match in the reply
example:
+ text: "متى الجزء الثاني؟"
tone: "friendly"
- context: "Tech review channel focused on smartphones"
+ max_length: 500
+ context:
+ platform: "youtube"
+ brand_voice: "Tech review channel focused on smartphones"
responses:
"200":
- description: Generated reply
+ description: Classification and generated reply
headers:
X-Request-Id:
$ref: "#/components/headers/X-Request-Id"
X-NAWA-Provider:
$ref: "#/components/headers/X-NAWA-Provider"
- X-NAWA-Cache:
- $ref: "#/components/headers/X-NAWA-Cache"
- X-NAWA-Balance:
- $ref: "#/components/headers/X-NAWA-Balance"
+ X-NAWA-Latency:
+ $ref: "#/components/headers/X-NAWA-Latency"
X-RateLimit-Limit:
$ref: "#/components/headers/X-RateLimit-Limit"
X-RateLimit-Remaining:
@@ -836,12 +867,21 @@ paths:
value:
success: true
result:
- comment_id: cmt_abc123
- reply_text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔"
- reply_dialect: gulf
- tone: friendly
- original_intent: question
- original_dialect: gulf
+ id: rpl_nw_abc123def456
+ object: comment_reply
+ classification:
+ intent: ["question"]
+ sentiment: neutral
+ priority: medium
+ requires_response: true
+ reply:
+ text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔"
+ direction: rtl
+ tone: friendly
+ provider: claude
+ model: claude-sonnet-4-5-20250929
+ cost_usd: 0.008
+ credits_used: 8
errors: []
request_id: req_rep789xyz012
"400":
@@ -1000,12 +1040,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
@@ -1050,26 +1092,13 @@ paths:
operationId: getAnalytics
summary: Get analytics
description: |
- Retrieve aggregated analytics on comment trends, sentiment shifts, and
- engagement patterns. Free endpoint.
+ Retrieve aggregated analytics on API usage including request counts, costs,
+ cache hit rates, latency, and provider distribution. Free endpoint.
tags: [Analytics]
x-nawa-cost: "Free"
x-nawa-cache: false
x-nawa-free: true
parameters:
- - name: channel_id
- in: query
- required: false
- schema:
- type: string
- description: Filter by channel ID
- - name: platform
- in: query
- required: false
- schema:
- type: string
- enum: [youtube, instagram, twitter, facebook]
- description: Filter by platform
- name: from
in: query
required: false
@@ -1089,9 +1118,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
@@ -1155,7 +1184,8 @@ paths:
summary: Health check
description: |
Check the operational status of the NAWA API and its dependencies.
- No authentication required.
+ No authentication required. Returns 200 when all services are healthy,
+ 503 when any service is degraded or down.
tags: [System]
x-nawa-cost: "Free"
x-nawa-cache: false
@@ -1167,29 +1197,47 @@ 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
+ 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:00.000Z"
+ 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"
+ examples:
+ degraded:
+ summary: Redis is down
+ value:
+ status: degraded
+ version: "v1"
+ services:
+ database:
+ status: healthy
+ latency_ms: 14
+ redis:
+ status: down
+ latency_ms: 5001
+ nagl:
+ status: healthy
+ timestamp: "2025-01-15T12:00:00.000Z"
+ latency_ms: 5016
/usage:
get:
@@ -1209,22 +1257,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
@@ -1943,62 +1991,96 @@ 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 in `rcl_nw_xxx` format
+ object:
type: string
- enum: [gulf, egyptian, levantine, msa]
- dialect_confidence:
- type: number
- minimum: 0
- maximum: 1
+ const: rubric_classification
+ categories:
+ type: array
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ description: Category name from the rubric
+ confidence:
+ type: number
+ minimum: 0
+ maximum: 1
+ description: Confidence score
+ 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
model:
type: string
- cached:
+ description: Model version used
+ 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 in `rpl_nw_xxx` format
+ object:
type: string
- enum: [friendly, professional, casual, formal]
- description: The tone used for the reply
- original_intent:
+ const: comment_reply
+ classification:
+ type: object
+ properties:
+ intent:
+ type: array
+ items:
+ type: string
+ description: Detected intent labels
+ sentiment:
+ type: string
+ enum: [positive, negative, neutral, mixed]
+ priority:
+ type: string
+ enum: [low, medium, high]
+ requires_response:
+ type: boolean
+ reply:
+ type: object
+ properties:
+ text:
+ type: string
+ description: The generated reply text
+ direction:
+ type: string
+ enum: [rtl, ltr]
+ description: Text direction for UI rendering
+ 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
+ model:
type: string
- enum: [gulf, egyptian, levantine, msa]
- description: Detected dialect of the original comment
+ description: Model version used
+ cost_usd:
+ type: number
+ credits_used:
+ type: integer
FeedbackInput:
type: object
@@ -2085,13 +2167,18 @@ components:
Pagination:
type: object
properties:
+ total:
+ type: integer
+ description: Total matching results
+ 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
+ description: Whether more results exist beyond the current page
AnalyticsResult:
type: object
@@ -2146,26 +2233,41 @@ components:
status:
type: string
enum: [healthy, degraded]
+ description: Overall status. `healthy` only when all services are healthy.
version:
type: string
+ description: API version (currently `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, down]
timestamp:
type: string
format: date-time
+ latency_ms:
+ type: integer
+ description: Total health check duration in milliseconds
UsageResult:
type: object