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