Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api-reference/classify.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
24 changes: 24 additions & 0 deletions api-reference/comments-reply.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
8 changes: 5 additions & 3 deletions api-reference/detect.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
8 changes: 5 additions & 3 deletions api-reference/moderate.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.
14 changes: 10 additions & 4 deletions api-reference/report.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<Note>
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.
</Note>

<Warning>
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.
</Warning>

## Request

### Headers
Expand Down Expand Up @@ -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

Expand Down
14 changes: 9 additions & 5 deletions billing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -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

<Warning>
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.
</Warning>

## Cost estimation
Expand Down
29 changes: 22 additions & 7 deletions errors.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -181,16 +181,28 @@ Your account balance is insufficient for the requested operation.

<AccordionGroup>
<Accordion title="balance_exhausted">
**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."
}
```
</Accordion>
Expand Down Expand Up @@ -242,14 +254,15 @@ An unexpected error occurred on the NAWA side.
<Accordion title="internal_error">
**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."
}
```
</Accordion>
Expand Down Expand Up @@ -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:
Expand All @@ -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}")
Expand Down
24 changes: 19 additions & 5 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2258,6 +2258,7 @@ components:
enum:
- authentication_error
- permission_error
- insufficient_credits
- rate_limit_error
- invalid_request_error
- not_found_error
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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