From 6928cdb69844d32ff43e55f6c4b5733393be564a Mon Sep 17 00:00:00 2001
From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com>
Date: Thu, 16 Apr 2026 19:51:06 +0000
Subject: [PATCH] fix: align API docs with supabase edge function handlers
- /comments/reply: change from path-param to body-param (text required),
update response schema to match handler envelope
- /feedback: replace request_id/field/expected_value params with
classification_id/rating/corrected_intent/corrected_sentiment
- /rubric/classify: update rubric categories from string[] to
{name, description}[] objects, add multi_label and confidence_threshold
- /webhooks: remove unsupported comment.new and comment.replied events
- Add webhook management API reference page (POST/GET/DELETE)
- Fix pre-existing YAML indentation error in /usage parameters
- All curl, Python, and TypeScript examples updated
Generated-By: mintlify-agent
---
api-reference/comments-reply.mdx | 199 +++++++++++++++---
api-reference/feedback.mdx | 86 +++++---
api-reference/rubric-classify.mdx | 203 ++++++++++++++-----
api-reference/webhooks-manage.mdx | 210 +++++++++++++++++++
docs.json | 1 +
openapi.yaml | 323 +++++++++++++++++++-----------
webhooks.mdx | 2 -
7 files changed, 797 insertions(+), 227 deletions(-)
create mode 100644 api-reference/webhooks-manage.mdx
diff --git a/api-reference/comments-reply.mdx b/api-reference/comments-reply.mdx
index d6321d7..ebb9c98 100644
--- a/api-reference/comments-reply.mdx
+++ b/api-reference/comments-reply.mdx
@@ -1,60 +1,80 @@
---
-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: "Classify and reply"
+sidebarTitle: "POST /v1/comments/reply"
+description: "Classify a comment and generate a contextual reply in a single API 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 a context-aware reply in one request. 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).
## Request
-### Path parameters
+### Headers
+
+| Header | Required | Description |
+|--------|----------|-------------|
+| `Authorization` | Yes | `Bearer nawa_live_sk_xxx` or `Bearer nawa_test_sk_xxx` |
+| `Content-Type` | Yes | `application/json` |
+
+### Query parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `id` | string | Yes | The comment ID to reply to. |
+| `provider` | string | No | Force a specific AI provider for A/B testing: `claude`, `gemini`, `allam`. |
### 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 classify and reply to. |
+| `tone` | string | No | Reply tone: `friendly`, `professional`, `casual`, `formal`. Default: `professional`. |
+| `max_length` | integer | No | Maximum reply length in characters (1-2000). Default: 500. |
### Example request
```bash cURL
-curl -X POST https://api.trynawa.com/v1/comments/cmt_abc123/reply \
+curl -X POST https://api.trynawa.com/v1/comments/reply \
-H "Authorization: Bearer nawa_test_sk_xxx" \
-H "Content-Type: application/json" \
-d '{
+ "text": "متى الجزء الثاني؟",
"tone": "friendly",
- "context": "Tech review channel focused on smartphones"
+ "max_length": 500
}'
```
```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
})
+
+console.log(data.reply.text)
```
```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
)
+
+print(result.data.reply["text"])
```
@@ -67,25 +87,144 @@ 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": "sdaia/allam-1-13b-instruct",
+ "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-RateLimit-Limit` | Rate limit ceiling for current window |
+| `X-RateLimit-Remaining` | Requests remaining in current window |
+| `X-RateLimit-Reset` | Window reset time (RFC 3339) |
+| `X-NAWA-Balance` | Current credit balance in USD |
+
### 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 | Reply ID (`rpl_nw_xxx` format) |
+| `object` | string | Always `comment_reply` |
+| `classification` | object | Classification of the input comment |
+| `classification.intent` | string[] | Detected intents |
+| `classification.sentiment` | string | `positive`, `negative`, `neutral`, `mixed` |
+| `classification.priority` | string | `high`, `medium`, `low` |
+| `classification.requires_response` | boolean | Whether the comment warrants a reply |
+| `reply` | object | Generated reply |
+| `reply.text` | string | The generated reply text |
+| `reply.direction` | string | Text direction: `ltr` or `rtl` |
+| `reply.tone` | string | The tone used for the reply |
+| `provider` | string | AI provider used |
+| `model` | string | Model used for generation |
+| `cost_usd` | number | Cost of this request 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 |
+
+### More examples
+
+
+
+ ```bash
+ curl -X POST https://api.trynawa.com/v1/comments/reply \
+ -H "Authorization: Bearer nawa_test_sk_xxx" \
+ -H "Content-Type: application/json" \
+ -d '{"text": "The audio quality is terrible in this video", "tone": "professional"}'
+ ```
+
+ Response:
+ ```json
+ {
+ "success": true,
+ "result": {
+ "id": "rpl_nw_b2c3d4e5f6g7",
+ "object": "comment_reply",
+ "classification": {
+ "intent": ["complaint"],
+ "sentiment": "negative",
+ "priority": "high",
+ "requires_response": true
+ },
+ "reply": {
+ "text": "Thank you for the feedback! We're aware of the audio issue and are working on improving it for future videos.",
+ "direction": "ltr",
+ "tone": "professional"
+ },
+ "provider": "claude",
+ "model": "claude-haiku-4-5-20251001",
+ "cost_usd": 0.008,
+ "credits_used": 8
+ },
+ "errors": [],
+ "request_id": "req_en_reply_001"
+ }
+ ```
+
+
+
+ ```bash
+ 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": "casual"}'
+ ```
+
+ Response:
+ ```json
+ {
+ "success": true,
+ "result": {
+ "id": "rpl_nw_c3d4e5f6g7h8",
+ "object": "comment_reply",
+ "classification": {
+ "intent": ["complaint"],
+ "sentiment": "negative",
+ "priority": "high",
+ "requires_response": true
+ },
+ "reply": {
+ "text": "نأسف على تجربتك! نحب نعرف وش بالضبط ما عجبك عشان نحسنه",
+ "direction": "rtl",
+ "tone": "casual"
+ },
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "cost_usd": 0.008,
+ "credits_used": 8
+ },
+ "errors": [],
+ "request_id": "req_ar_reply_001"
+ }
+ ```
+
+
diff --git a/api-reference/feedback.mdx b/api-reference/feedback.mdx
index 4cddf8b..2d2723c 100644
--- a/api-reference/feedback.mdx
+++ b/api-reference/feedback.mdx
@@ -1,14 +1,14 @@
---
-title: "Submit Feedback"
+title: "Submit feedback"
sidebarTitle: "POST /v1/feedback"
-description: "Submit RLHF feedback to improve classification accuracy over time."
+description: "Submit RLHF feedback on a classification result to improve accuracy over time."
api: "POST https://api.trynawa.com/v1/feedback"
---
-Submit reinforcement learning from human feedback (RLHF) to continuously improve NAWA's classification accuracy.
+Submit feedback on a classification result to improve NAWA's accuracy through reinforcement learning from human feedback (RLHF). This endpoint is free for all tiers.
- This endpoint is **free** -- 0 credits, no cost.
+ This endpoint is **free** - 0 credits, no cost.
@@ -17,14 +17,22 @@ Submit reinforcement learning from human feedback (RLHF) to continuously improve
## Request
+### Headers
+
+| Header | Required | Description |
+|--------|----------|-------------|
+| `Authorization` | Yes | `Bearer nawa_live_sk_xxx` or `Bearer nawa_test_sk_xxx` |
+| `Content-Type` | Yes | `application/json` |
+
### Body parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `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 (`cls_nw_xxx`) from a `/v1/classify` response. |
+| `rating` | string | Yes | `correct`, `incorrect`, or `partial`. |
+| `corrected_intent` | string[] | No | The correct intent(s) if the classification was wrong. |
+| `corrected_sentiment` | string | No | The correct sentiment: `positive`, `negative`, `neutral`, `mixed`. |
+| `comment` | string | No | Free-text explanation of why this correction is needed. |
### Example request
@@ -35,28 +43,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 about availability, 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 about availability, 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 about availability, not praise"
)
```
@@ -70,11 +89,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_a1b2c3d4e5f6",
+ "object": "feedback",
+ "classification_id": "cls_nw_a1b2c3d4e5f6",
+ "rating": "incorrect",
+ "acknowledged": true
},
"errors": [],
"request_id": "req_fb_abc123"
}
```
+
+### Result fields
+
+| Field | Type | Description |
+|-------|------|-------------|
+| `id` | string | Feedback ID (`fb_nw_xxx` format) |
+| `object` | string | Always `feedback` |
+| `classification_id` | string | The classification that was corrected |
+| `rating` | string | The rating you submitted |
+| `acknowledged` | boolean | Always `true` on success |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `classification_id`, invalid `rating` |
+| 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/rubric-classify.mdx b/api-reference/rubric-classify.mdx
index 4489175..3db26b8 100644
--- a/api-reference/rubric-classify.mdx
+++ b/api-reference/rubric-classify.mdx
@@ -1,27 +1,43 @@
---
-title: "Rubric Classify"
+title: "Rubric classify"
sidebarTitle: "POST /v1/rubric/classify"
-description: "Classify a comment against a custom rubric with predefined scoring criteria."
+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, descriptions, and confidence thresholds 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).
## Request
+### Headers
+
+| Header | Required | Description |
+|--------|----------|-------------|
+| `Authorization` | Yes | `Bearer nawa_live_sk_xxx` or `Bearer nawa_test_sk_xxx` |
+| `Content-Type` | Yes | `application/json` |
+
+### Query parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `provider` | string | No | Force a specific AI provider for A/B testing: `claude`, `gemini`, `allam`. |
+
### Body parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
-| `text` | string | Yes | The comment text to classify. Max 5,000 characters. |
-| `rubric` | object | Yes | The rubric definition with categories and criteria. |
-| `rubric.categories` | string[] | Yes | List of category names to classify against. |
-| `rubric.descriptions` | object | No | Category descriptions to guide the model. Keys are category names, values are description strings. |
-| `platform` | string | No | Source platform for context. |
+| `text` | string | Yes | The text to classify. Max 5,000 characters. |
+| `rubric` | object | Yes | The rubric definition. |
+| `rubric.categories` | object[] | Yes | Array of category objects. Min 1, max 20. |
+| `rubric.categories[].name` | string | Yes | Category name. |
+| `rubric.categories[].description` | string | No | Description to guide the model. |
+| `rubric.multi_label` | boolean | No | If `true`, return all categories above the threshold. Default: `false`. |
+| `rubric.confidence_threshold` | number | No | Minimum confidence to include a category (0-1). Default: `0.5`. |
+| `platform` | string | No | Source platform: `youtube`, `instagram`, `twitter`, `facebook`. |
### Example request
@@ -34,48 +50,63 @@ curl -X POST https://api.trynawa.com/v1/rubric/classify \
-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"
- }
+ "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"
}'
```
```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' }
+ ],
+ multiLabel: false,
+ confidenceThreshold: 0.5
},
platform: 'youtube'
})
+
+console.log(data.categories)
```
```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",
)
+
+print(result.data.categories)
```
@@ -88,34 +119,106 @@ result = nawa.rubric.classify(
{
"success": true,
"result": {
- "text": "وين الترجمة العربية؟ مافهمت شي",
- "category": "translation_request",
- "category_confidence": 0.94,
- "scores": {
- "translation_request": 0.94,
- "technical_issue": 0.03,
- "content_feedback": 0.02,
- "off_topic": 0.01
- },
- "dialect": "gulf",
- "dialect_confidence": 0.91,
+ "id": "rcl_nw_a1b2c3d4e5f6",
+ "object": "rubric_classification",
+ "categories": [
+ {"name": "translation_request", "confidence": 0.94}
+ ],
+ "multi_label": false,
"language": "ar",
- "model": "nagl-v1",
- "cached": false
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "cost_usd": 0.003,
+ "credits_used": 3
},
"errors": [],
"request_id": "req_rub123abc456"
}
```
+### Response headers
+
+| Header | Description |
+|--------|-------------|
+| `X-Request-Id` | Unique request identifier for debugging |
+| `X-NAWA-Provider` | AI provider used for this request |
+| `X-RateLimit-Limit` | Rate limit ceiling for current window |
+| `X-RateLimit-Remaining` | Requests remaining in current window |
+| `X-RateLimit-Reset` | Window reset time (RFC 3339) |
+| `X-NAWA-Balance` | Current credit balance in USD |
+
### 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 | Classification ID (`rcl_nw_xxx` format) |
+| `object` | string | Always `rubric_classification` |
+| `categories` | object[] | Matched categories with confidence scores |
+| `categories[].name` | string | Category name |
+| `categories[].confidence` | number | Confidence score (0-1) |
+| `multi_label` | boolean | Whether multi-label mode was used |
+| `language` | string | Detected language: `ar`, `en`, `mixed` |
+| `provider` | string | AI provider used |
+| `model` | string | Model used for classification |
+| `fallback_used` | boolean | Whether a fallback provider was used |
+| `cost_usd` | number | Cost of this request in USD |
+| `credits_used` | integer | Credits deducted |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `text` or `rubric`, too many categories (max 20), empty category name |
+| 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 |
+
+### More examples
+
+
+
+ ```bash
+ 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": [
+ {"name": "translation_request"},
+ {"name": "technical_issue"},
+ {"name": "content_feedback"}
+ ],
+ "multi_label": true,
+ "confidence_threshold": 0.3
+ }
+ }'
+ ```
+
+ Response:
+ ```json
+ {
+ "success": true,
+ "result": {
+ "id": "rcl_nw_d4e5f6g7h8i9",
+ "object": "rubric_classification",
+ "categories": [
+ {"name": "technical_issue", "confidence": 0.88},
+ {"name": "translation_request", "confidence": 0.72}
+ ],
+ "multi_label": true,
+ "language": "ar",
+ "provider": "allam",
+ "model": "sdaia/allam-1-13b-instruct",
+ "fallback_used": false,
+ "cost_usd": 0.003,
+ "credits_used": 3
+ },
+ "errors": [],
+ "request_id": "req_rub_multi_001"
+ }
+ ```
+
+
diff --git a/api-reference/webhooks-manage.mdx b/api-reference/webhooks-manage.mdx
new file mode 100644
index 0000000..fbdbcd9
--- /dev/null
+++ b/api-reference/webhooks-manage.mdx
@@ -0,0 +1,210 @@
+---
+title: "Webhook management"
+sidebarTitle: "Webhooks"
+description: "Create, list, and deactivate webhook endpoints for real-time event notifications."
+---
+
+Manage webhook endpoints for your NAWA account. You can register up to 10 active endpoints per account.
+
+
+ Webhook management is **free** - 0 credits, no cost.
+
+
+## Create a webhook endpoint
+
+`POST /v1/webhooks`
+
+Register a new webhook endpoint for event notifications. The signing secret is returned only once at creation.
+
+### Body parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `url` | string | Yes | HTTPS endpoint URL. Must use `https://`. |
+| `events` | string[] | Yes | Event types to subscribe to. |
+
+### Supported events
+
+| Event | Description |
+|-------|-------------|
+| `classification.completed` | A classification request succeeded |
+| `classification.failed` | A classification request failed |
+| `credits.low` | Credit balance dropped below $5 |
+| `credits.exhausted` | Credit balance reached $0 |
+
+### Example request
+
+
+
+```bash cURL
+curl -X POST https://api.trynawa.com/v1/webhooks \
+ -H "Authorization: Bearer nawa_live_sk_xxx" \
+ -H "Content-Type: application/json" \
+ -d '{
+ "url": "https://example.com/webhooks/nawa",
+ "events": ["classification.completed", "credits.low"]
+ }'
+```
+
+```typescript TypeScript
+import { Nawa } from '@nawalabs/sdk'
+
+const nawa = new Nawa({ apiKey: process.env.NAWA_API_KEY })
+
+const { data, error } = await nawa.webhooks.create({
+ url: 'https://example.com/webhooks/nawa',
+ events: ['classification.completed', 'credits.low']
+})
+
+// Store the signing secret securely - it is shown only once
+console.log(data.signing_secret)
+```
+
+```python Python
+from nawa import Nawa
+
+nawa = Nawa(api_key="your_api_key")
+
+result = nawa.webhooks.create(
+ url="https://example.com/webhooks/nawa",
+ events=["classification.completed", "credits.low"]
+)
+
+# Store the signing secret securely - it is shown only once
+print(result.data.signing_secret)
+```
+
+
+
+### Success response (201)
+
+```json
+{
+ "success": true,
+ "result": {
+ "id": "550e8400-e29b-41d4-a716-446655440000",
+ "object": "webhook_endpoint",
+ "url": "https://example.com/webhooks/nawa",
+ "events": ["classification.completed", "credits.low"],
+ "signing_secret": "nawa_wh_a1b2c3d4e5f6...",
+ "active": true,
+ "created_at": "2026-01-15T12:00:00Z"
+ },
+ "errors": [],
+ "request_id": "req_wh_abc123"
+}
+```
+
+
+ The `signing_secret` is shown only at creation time. Store it securely. See [webhook signature verification](/webhooks#signature-verification) for usage.
+
+
+---
+
+## List webhook endpoints
+
+`GET /v1/webhooks`
+
+Retrieve all webhook endpoints for your account.
+
+### Example request
+
+
+
+```bash cURL
+curl https://api.trynawa.com/v1/webhooks \
+ -H "Authorization: Bearer nawa_live_sk_xxx"
+```
+
+```typescript TypeScript
+const { data, error } = await nawa.webhooks.list()
+```
+
+```python Python
+result = nawa.webhooks.list()
+```
+
+
+
+### Success response (200)
+
+```json
+{
+ "success": true,
+ "result": {
+ "object": "list",
+ "data": [
+ {
+ "id": "550e8400-e29b-41d4-a716-446655440000",
+ "object": "webhook_endpoint",
+ "url": "https://example.com/webhooks/nawa",
+ "events": ["classification.completed", "credits.low"],
+ "active": true,
+ "failure_count": 0,
+ "last_delivery_at": "2026-01-15T14:30:00Z",
+ "disabled_at": null,
+ "created_at": "2026-01-15T12:00:00Z"
+ }
+ ]
+ },
+ "errors": [],
+ "request_id": "req_wh_list_abc123"
+}
+```
+
+---
+
+## Deactivate a webhook endpoint
+
+`DELETE /v1/webhooks?id={endpoint_id}`
+
+Deactivate a webhook endpoint. Deactivated endpoints stop receiving events.
+
+### Query parameters
+
+| Parameter | Type | Required | Description |
+|-----------|------|----------|-------------|
+| `id` | string (UUID) | Yes | The webhook endpoint ID to deactivate. |
+
+### Example request
+
+
+
+```bash cURL
+curl -X DELETE "https://api.trynawa.com/v1/webhooks?id=550e8400-e29b-41d4-a716-446655440000" \
+ -H "Authorization: Bearer nawa_live_sk_xxx"
+```
+
+```typescript TypeScript
+const { data, error } = await nawa.webhooks.delete('550e8400-e29b-41d4-a716-446655440000')
+```
+
+```python Python
+result = nawa.webhooks.delete("550e8400-e29b-41d4-a716-446655440000")
+```
+
+
+
+### Success response (200)
+
+```json
+{
+ "success": true,
+ "result": {
+ "id": "550e8400-e29b-41d4-a716-446655440000",
+ "object": "webhook_endpoint",
+ "deleted": true
+ },
+ "errors": [],
+ "request_id": "req_wh_del_abc123"
+}
+```
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing `url` or `events`, invalid URL scheme, too many endpoints |
+| 401 | `authentication_error` | Invalid or missing API key |
+| 404 | `not_found_error` | Webhook endpoint not found (DELETE only) |
+| 429 | `rate_limit_error` | Rate limit exceeded |
diff --git a/docs.json b/docs.json
index 8f8f901..7b775f6 100644
--- a/docs.json
+++ b/docs.json
@@ -59,6 +59,7 @@
"api-reference/comments-reply",
"api-reference/report",
"api-reference/feedback",
+ "api-reference/webhooks-manage",
"api-reference/comments-list",
"api-reference/analytics",
"api-reference/health",
diff --git a/openapi.yaml b/openapi.yaml
index 17706f2..f00d812 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -492,13 +492,26 @@ 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
+ type: object
+ required: [name]
+ properties:
+ name:
+ type: string
+ description: Category name
+ description:
+ type: string
+ description: Optional description to guide the model
+ description: List of categories to classify against
+ multi_label:
+ type: boolean
+ default: false
+ description: "If true, return all matching categories above the threshold. If false, return only the best match."
+ confidence_threshold:
+ type: number
+ minimum: 0
+ maximum: 1
+ default: 0.5
+ description: Minimum confidence score to include a category
platform:
type: string
enum: [youtube, instagram, twitter, facebook]
@@ -506,12 +519,17 @@ paths:
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"
+ 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"
responses:
"200":
@@ -541,19 +559,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_a1b2c3d4e5f6
+ object: rubric_classification
+ categories:
+ - name: translation_request
+ confidence: 0.94
+ multi_label: false
language: ar
- model: nagl-v1
- cached: false
+ provider: allam
+ model: sdaia/allam-1-13b-instruct
+ fallback_used: false
+ cost_usd: 0.003
+ credits_used: 3
errors: []
request_id: req_rub123abc456
"400":
@@ -567,37 +584,46 @@ paths:
"500":
$ref: "#/components/responses/InternalError"
- /comments/{id}/reply:
+ /comments/reply:
post:
- operationId: generateReply
- summary: Generate reply to comment
+ operationId: commentsReply
+ summary: Classify and generate reply
description: |
- Generate a culturally-aware, dialect-matched reply to an Arabic comment.
- Replies match the commenter's dialect and are contextually appropriate.
+ Classify a comment and generate a contextual reply in a single call.
+ Supports tone control and max length configuration. For Arabic comments,
+ replies match the detected dialect. For English comments, replies are
+ natural and platform-appropriate.
tags: [Reply]
x-nawa-cost: "$0.008"
- x-nawa-cache: true
+ x-nawa-cache: false
x-nawa-free: false
parameters:
- - name: id
- in: path
- required: true
- description: The comment ID to reply to
+ - name: provider
+ in: query
+ required: false
+ description: Force a specific AI provider (for A/B testing)
schema:
type: string
- examples:
- - "cmt_abc123"
+ enum: [claude, gemini, allam]
requestBody:
- required: false
+ required: true
content:
application/json:
schema:
type: object
+ required: [text]
properties:
+ text:
+ type: string
+ minLength: 1
+ description: Comment text to classify and reply to
+ examples:
+ - "هذا المنتج سيء جداً ولا أنصح به"
+ - "Great video, when is the next one?"
tone:
type: string
enum: [friendly, professional, casual, formal]
- default: friendly
+ default: professional
description: Reply tone
max_length:
type: integer
@@ -605,27 +631,18 @@ paths:
maximum: 2000
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)."
example:
+ text: "متى الجزء الثاني؟"
tone: "friendly"
- context: "Tech review channel focused on smartphones"
+ max_length: 500
responses:
"200":
- description: Generated reply
+ description: Classification and reply result
headers:
X-Request-Id:
$ref: "#/components/headers/X-Request-Id"
X-NAWA-Provider:
$ref: "#/components/headers/X-NAWA-Provider"
- X-NAWA-Cache:
- $ref: "#/components/headers/X-NAWA-Cache"
X-NAWA-Balance:
$ref: "#/components/headers/X-NAWA-Balance"
X-RateLimit-Limit:
@@ -644,12 +661,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_a1b2c3d4e5f6
+ object: comment_reply
+ classification:
+ intent: [question]
+ sentiment: neutral
+ priority: medium
+ requires_response: true
+ reply:
+ text: "إن شاء الله الجزء الثاني قريب! تابعنا عشان ما يفوتك 🔔"
+ direction: rtl
+ tone: friendly
+ provider: allam
+ model: sdaia/allam-1-13b-instruct
+ cost_usd: 0.008
+ credits_used: 8
errors: []
request_id: req_rep789xyz012
"400":
@@ -681,13 +707,14 @@ 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
+ description: Feedback acknowledged
headers:
X-Request-Id:
$ref: "#/components/headers/X-Request-Id"
@@ -697,13 +724,15 @@ paths:
$ref: "#/components/schemas/FeedbackSuccessResponse"
examples:
accepted:
- summary: Feedback accepted
+ summary: Feedback acknowledged
value:
success: true
result:
- feedback_id: fb_xyz789
- status: accepted
- message: "Thank you! Your feedback helps improve NAWA's accuracy."
+ id: fb_nw_a1b2c3d4e5f6
+ object: feedback
+ classification_id: cls_nw_a1b2c3d4e5f6
+ rating: incorrect
+ acknowledged: true
errors: []
request_id: req_fb_abc123
"400":
@@ -996,25 +1025,25 @@ paths:
- name: from
in: query
required: false
+ description: "Start date (ISO 8601). Default: start of current month."
schema:
type: string
format: date-time
- description: Start date (ISO 8601). Default: start of current month.
- name: to
in: query
required: false
+ description: "End date (ISO 8601). Default: now."
schema:
type: string
format: date-time
- description: End date (ISO 8601). Default: now.
- name: group_by
in: query
required: false
+ description: Group results by time period or endpoint
schema:
type: string
enum: [day, week, month, endpoint]
default: day
- description: Group results by time period or endpoint
responses:
"200":
description: Usage statistics
@@ -1097,8 +1126,6 @@ paths:
enum:
- classification.completed
- classification.failed
- - comment.new
- - comment.replied
- credits.low
- credits.exhausted
responses:
@@ -1687,91 +1714,143 @@ components:
RubricResult:
type: object
+ required: [id, object, categories, multi_label, language, provider, model, cost_usd, credits_used]
properties:
- text:
- type: string
- description: The original input text
- category:
+ id:
type: string
- description: The top matching category from the rubric
- category_confidence:
- type: number
- minimum: 0
- maximum: 1
- scores:
- type: object
- additionalProperties:
- type: number
- description: Confidence scores for all rubric categories
- dialect:
+ description: 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
+ required: [name, confidence]
+ properties:
+ name:
+ type: string
+ description: Category name
+ 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:
+ 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 input comment
+ properties:
+ intent:
+ type: array
+ items:
+ type: string
+ description: Detected intents
+ sentiment:
+ type: string
+ enum: [positive, negative, neutral, mixed]
+ priority:
+ type: string
+ enum: [high, medium, low]
+ requires_response:
+ type: boolean
+ reply:
+ type: object
+ description: Generated reply
+ properties:
+ text:
+ type: string
+ description: The generated reply text
+ direction:
+ type: string
+ enum: [ltr, rtl]
+ description: Text direction of the reply
+ 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 used for generation
+ cost_usd:
+ type: number
+ description: Cost in USD
+ credits_used:
+ type: integer
+ description: Credits deducted
FeedbackInput:
type: object
- required: [request_id, field, expected_value]
+ required: [classification_id, rating]
properties:
- request_id:
+ classification_id:
type: string
- description: The request_id from the original classification response
- field:
+ description: The classification ID (cls_nw_xxx) from a classify response
+ rating:
type: string
- enum: [intent, sentiment, dialect, toxicity, category]
- description: The field to correct
- expected_value:
+ enum: [correct, incorrect, partial]
+ description: Whether the classification was correct, incorrect, or partially correct
+ corrected_intent:
+ type: array
+ items:
+ type: string
+ description: The correct intent(s) if the classification was wrong
+ corrected_sentiment:
type: string
- description: The correct value the model should have returned
+ enum: [positive, negative, neutral, mixed]
+ description: The correct sentiment if the classification was wrong
comment:
type: string
description: Optional explanation of why this correction is needed
FeedbackResult:
type: object
+ required: [id, object, classification_id, rating, acknowledged]
properties:
- feedback_id:
+ id:
type: string
- status:
+ description: Feedback ID (fb_nw_xxx format)
+ object:
type: string
- enum: [accepted]
- message:
+ const: feedback
+ classification_id:
type: string
+ rating:
+ type: string
+ enum: [correct, incorrect, partial]
+ acknowledged:
+ type: boolean
+ const: true
CommentItem:
type: object
diff --git a/webhooks.mdx b/webhooks.mdx
index 2ef4922..7eea37b 100644
--- a/webhooks.mdx
+++ b/webhooks.mdx
@@ -12,8 +12,6 @@ Receive real-time notifications when events occur in your NAWA account. Webhooks
|-------|-------------|---------|
| `classification.completed` | A classification request succeeded | After `/v1/classify` completes |
| `classification.failed` | A classification request failed | On provider or internal errors |
-| `comment.new` | A new comment was ingested | When a connected platform receives a comment |
-| `comment.replied` | A reply was posted | After `/v1/comments/:id/reply` succeeds |
| `credits.low` | Credit balance below threshold | Balance drops below $5 |
| `credits.exhausted` | Credit balance is $0 | Balance reaches $0 |