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