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