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