From 675757ba573c35e5df847b5d127532ed9ed1898b Mon Sep 17 00:00:00 2001
From: "mintlify[bot]" <109931778+mintlify[bot]@users.noreply.github.com>
Date: Sun, 19 Apr 2026 15:20:06 +0000
Subject: [PATCH] Update API docs for balance gate, 402 error fields, and
redacted 500s
Generated-By: mintlify-agent
---
api-reference/classify.mdx | 4 ++--
api-reference/comments-reply.mdx | 24 ++++++++++++++++++++++++
api-reference/detect.mdx | 8 +++++---
api-reference/moderate.mdx | 8 +++++---
api-reference/report.mdx | 14 ++++++++++----
billing.mdx | 14 +++++++++-----
errors.mdx | 29 ++++++++++++++++++++++-------
openapi.yaml | 24 +++++++++++++++++++-----
8 files changed, 96 insertions(+), 29 deletions(-)
diff --git a/api-reference/classify.mdx b/api-reference/classify.mdx
index a22db4b..9e7be9e 100644
--- a/api-reference/classify.mdx
+++ b/api-reference/classify.mdx
@@ -143,8 +143,8 @@ result = nawa.classify(text="متى الجزء الثاني؟")
|--------|------|------|
| 400 | `invalid_request_error` | Missing or empty `text`, invalid `provider` query param, malformed JSON body |
| 401 | `authentication_error` | Missing, malformed, revoked, or expired API key |
-| 402 | `insufficient_credits` | Live key has insufficient credit balance for this request. Response body includes `balance_credits` and `required_credits` on the error object. |
-| 429 | `rate_limit_error` | Per-minute rate limit exceeded, or sandbox lifetime quota (100 requests) exhausted |
+| 402 | `insufficient_credits` | Live key has insufficient credit balance. Checked before any AI work runs (but after the cache check, so cache hits remain free). Response body includes `balance_credits` and `required_credits`. |
+| 429 | `rate_limit_error` | Per-minute rate limit exceeded, or free key lifetime quota (100 requests) exhausted |
| 500 | `api_error` | Unexpected internal failure. The response `message` is a generic constant; quote `request_id` to support for the underlying cause. |
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..195550d 100644
--- a/api-reference/comments-reply.mdx
+++ b/api-reference/comments-reply.mdx
@@ -89,3 +89,27 @@ result = nawa.comments.reply(
| `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 |
+
+### Response headers
+
+| Header | Description |
+|--------|-------------|
+| `X-Request-Id` | Unique request identifier. Quote this to support when reporting an issue. |
+| `X-NAWA-Cache` | `HIT` if served from the semantic cache (free, no credit charge), `MISS` otherwise. |
+| `X-NAWA-Provider` | Which provider produced the response: `claude`, `allam`, `gemini`. |
+| `X-NAWA-Balance` | Current credit balance in USD. |
+| `X-RateLimit-Limit` | Your tier's per-minute request ceiling. |
+| `X-RateLimit-Remaining` | Requests remaining in the current one-minute window. |
+| `X-RateLimit-Reset` | RFC 3339 timestamp when the window resets. |
+
+### Error responses
+
+| Status | Type | When |
+|--------|------|------|
+| 400 | `invalid_request_error` | Missing comment ID, invalid `tone` or `language`, malformed JSON body |
+| 401 | `authentication_error` | Missing, malformed, revoked, or expired API key |
+| 402 | `insufficient_credits` | Live key has insufficient credit balance. Checked before any AI work runs. Response body includes `balance_credits` and `required_credits`. |
+| 429 | `rate_limit_error` | Per-minute rate limit exceeded, or free key lifetime quota (100 requests) exhausted |
+| 500 | `api_error` | Unexpected internal failure. The response `message` is a generic constant; quote `request_id` to support for the underlying cause. |
+
+See [Errors](/errors) for the full envelope shape and all error codes.
diff --git a/api-reference/detect.mdx b/api-reference/detect.mdx
index 2169b2c..f11da3b 100644
--- a/api-reference/detect.mdx
+++ b/api-reference/detect.mdx
@@ -149,6 +149,8 @@ curl -X POST https://api.trynawa.com/v1/detect \
|--------|------|------|
| 400 | `invalid_request_error` | Missing `text`. Text too long. |
| 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 error |
+| 402 | `insufficient_credits` | Live key has insufficient credit balance. Checked before any AI work runs (but after the cache check, so cache hits remain free). Response body includes `balance_credits` and `required_credits`. |
+| 429 | `rate_limit_error` | Per-minute rate limit exceeded, or free key lifetime quota (100 requests) exhausted |
+| 500 | `api_error` | Unexpected internal failure. The response `message` is a generic constant; quote `request_id` to support for the underlying cause. |
+
+See [Errors](/errors) for the full envelope shape and all error codes.
diff --git a/api-reference/moderate.mdx b/api-reference/moderate.mdx
index 947d30e..7171084 100644
--- a/api-reference/moderate.mdx
+++ b/api-reference/moderate.mdx
@@ -267,6 +267,8 @@ curl -X POST https://api.trynawa.com/v1/moderate \
|--------|------|------|
| 400 | `invalid_request_error` | Missing `text`. Invalid strictness or platform. Text too long. |
| 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 |
+| 402 | `insufficient_credits` | Live key has insufficient credit balance. Checked before any AI work runs. Response body includes `balance_credits` and `required_credits`. |
+| 429 | `rate_limit_error` | Per-minute rate limit exceeded, or free key lifetime quota (100 requests) exhausted |
+| 500 | `api_error` | Unexpected internal failure. The response `message` is a generic constant; quote `request_id` to support for the underlying cause. |
+
+See [Errors](/errors) for the full envelope shape and all error codes.
diff --git a/api-reference/report.mdx b/api-reference/report.mdx
index 74662dc..902c27f 100644
--- a/api-reference/report.mdx
+++ b/api-reference/report.mdx
@@ -8,9 +8,13 @@ api: "POST https://api.trynawa.com/v1/report"
Generate a structured audience intelligence report from comment data. The report analyzes audience clusters, dominant narratives, sentiment patterns, spam/bot detection, and produces data-backed strategic recommendations.
- Cost: **$0.50** per Basic report (500 credits). **$1.50** per Pro report (1500 credits). Pro includes content brief, repeat commenter analysis, and engagement playbook.
+ Cost: **$0.50** per Basic report (500 credits). **$1.50** per Pro report (1,500 credits). Pro includes content brief, repeat commenter analysis, and engagement playbook.
+
+ Reports require a **live API key** (`nawa_live_sk_`). Free keys are blocked because the minimum cost is 500 credits. Your balance is checked before any AI work runs, so you are never charged for a request that fails due to insufficient credits.
+
+
## Request
### Headers
@@ -194,9 +198,11 @@ 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) |
-| 500 | `api_error` | Internal or provider error |
+| 402 | `insufficient_credits` | Live key has insufficient credit balance (need 500 for Basic, 1,500 for Pro). Checked before any AI work runs. Response body includes `balance_credits` and `required_credits`. |
+| 429 | `rate_limit_error` | Rate limit exceeded (5 reports/minute, 20/day) |
+| 500 | `api_error` | Unexpected internal failure. The response `message` is a generic constant; quote `request_id` to support for the underlying cause. |
+
+See [Errors](/errors) for the full envelope shape and all error codes.
### Report tier comparison
diff --git a/billing.mdx b/billing.mdx
index e28bb54..f094c6c 100644
--- a/billing.mdx
+++ b/billing.mdx
@@ -78,15 +78,19 @@ Every API response includes balance information in headers:
## What happens when credits run out
-When your credit balance reaches 0:
+NAWA runs a pre-call balance gate on every paid endpoint. Your balance is checked **before** any AI model work runs, so you are never charged for a request that fails due to insufficient credits.
-- All paid endpoints return `402 insufficient_credits`
-- Free endpoints continue to work normally
-- Free keys (`nawa_test_sk_`) are unaffected -- they use their own 100-request pool
+When your credit balance is too low for a request:
+
+- The endpoint returns `402 insufficient_credits` with `balance_credits` and `required_credits` in the error body
+- No AI work executes and no credits are deducted
+- Free endpoints (health, usage, analytics, comments, feedback) continue to work normally
+- Free keys (`nawa_test_sk_`) are unaffected -- they use their own 100-request pool and do not consume credit balance
+- Semantic cache hits remain free even on paid endpoints. A cache hit never triggers the balance gate.
- Purchase any credit pack to restore access immediately
- There is no grace period. When credits hit zero, paid endpoints stop immediately. Enable monitoring on your `X-NAWA-Balance` header to avoid service interruption.
+ There is no grace period. When credits are insufficient, paid endpoints stop immediately. Enable monitoring on your `X-NAWA-Balance` header to avoid service interruption.
## Cost estimation
diff --git a/errors.mdx b/errors.mdx
index fdcf365..9a98583 100644
--- a/errors.mdx
+++ b/errors.mdx
@@ -181,16 +181,28 @@ Your account balance is insufficient for the requested operation.
- **Cause:** Your credit balance has reached $0. There is no grace period -- paid endpoints stop immediately.
+ **Cause:** Your credit balance is too low for the requested operation. The balance is checked before any work runs, so you are never charged for a request that fails with this error.
- **Fix:** Purchase a credit pack from the dashboard to restore access instantly.
+ **Fix:** Purchase a credit pack from the dashboard to restore access instantly. Free keys are unaffected; they use a separate 100-request pool.
+
+ The error object includes two extra fields so your client can render a precise upgrade prompt without a second round trip:
+
+ | Field | Type | Description |
+ |-------|------|-------------|
+ | `balance_credits` | number | Your current credit balance |
+ | `required_credits` | number | Credits this request would have consumed |
```json
{
"type": "insufficient_credits",
"code": "balance_exhausted",
- "message": "Credit balance exhausted. Purchase credits to continue.",
- "suggested_action": "Buy a credit pack at trynawa.com/developers/keys."
+ "message": "Insufficient credits: balance 3, required 6",
+ "display_message": "Your credit balance is insufficient for this request.",
+ "balance_credits": 3,
+ "required_credits": 6,
+ "param": null,
+ "doc_url": "https://developers.trynawa.com/errors#insufficient_credits",
+ "suggested_action": "Purchase credits at trynawa.com/developers/keys."
}
```
@@ -242,14 +254,15 @@ An unexpected error occurred on the NAWA side.
**Cause:** An unexpected internal failure.
- **Fix:** Retry the request. If the issue persists, contact support with the `request_id`.
+ **Fix:** Retry the request. If the issue persists, contact support with the `request_id`. The `message` field is always a generic constant for security. Raw error details are never exposed to API clients. NAWA logs the underlying cause server-side and correlates it with your `request_id`, so support can investigate without you needing to share sensitive data.
```json
{
"type": "api_error",
"code": "internal_error",
- "message": "An unexpected error occurred. Please retry.",
- "suggested_action": "Retry the request. Contact support if the issue persists."
+ "message": "An unexpected error occurred. If this persists, contact support with the request_id.",
+ "display_message": "Something went wrong processing your request.",
+ "suggested_action": "Contact support if this persists."
}
```
@@ -295,6 +308,7 @@ if (error) {
console.log('Rate limited, retrying after:', error.retryAfter)
break
case 'insufficient_credits':
+ console.error(`Balance: ${error.balance_credits}, need: ${error.required_credits}`)
console.error('Buy credits:', error.suggested_action)
break
default:
@@ -317,6 +331,7 @@ if result.error:
elif result.error.type == "rate_limit_error":
print(f"Rate limited, retry after: {result.error.retry_after}")
elif result.error.type == "insufficient_credits":
+ print(f"Balance: {result.error.balance_credits}, need: {result.error.required_credits}")
print(f"Buy credits: {result.error.suggested_action}")
else:
print(f"API error: {result.error.message}")
diff --git a/openapi.yaml b/openapi.yaml
index 2c6201d..5a4cf6d 100644
--- a/openapi.yaml
+++ b/openapi.yaml
@@ -2258,6 +2258,7 @@ components:
enum:
- authentication_error
- permission_error
+ - insufficient_credits
- rate_limit_error
- invalid_request_error
- not_found_error
@@ -2325,13 +2326,15 @@ components:
example:
type: error
error:
- type: permission_error
- code: insufficient_credits
- message: Credit balance is $0.00
- display_message: Your account has insufficient credits.
+ type: insufficient_credits
+ code: balance_exhausted
+ message: "Insufficient credits: balance 3, required 6"
+ display_message: Your credit balance is insufficient for this request.
param: null
- doc_url: https://developers.trynawa.com/billing
+ doc_url: https://developers.trynawa.com/errors#insufficient_credits
suggested_action: Purchase credits at trynawa.com/developers/keys.
+ balance_credits: 3
+ required_credits: 6
request_id: req_nw_a1b2c3d4e5f67890
RateLimited:
@@ -2376,3 +2379,14 @@ components:
application/json:
schema:
$ref: "#/components/schemas/ErrorEnvelope"
+ example:
+ type: error
+ error:
+ type: api_error
+ code: internal_error
+ message: "An unexpected error occurred. If this persists, contact support with the request_id."
+ display_message: Something went wrong processing your request.
+ param: null
+ doc_url: https://developers.trynawa.com/api/errors
+ suggested_action: Contact support if this persists.
+ request_id: req_nw_a1b2c3d4e5f67890