Skip to main content

Overview

The Junis External API uses conventional HTTP status codes to indicate the success or failure of a request. Error responses include a structured JSON body with details about the error, making it easy to debug and handle errors programmatically. Base URL: https://api.junis.ai

Error Response Format

Standard Error Response

All error responses follow this consistent structure:
{
  "detail": "Human-readable error message"
}

Structured Error Response

For more complex errors (like rate limiting or scope validation), the API returns additional context:
{
  "error": "error_code",
  "message": "Human-readable error message",
  "code": "SPECIFIC_ERROR_CODE",
  "details": {
    // Additional error-specific details
  }
}

HTTP Status Codes

Status CodeNameDescription
200OKRequest succeeded.
201CreatedResource successfully created.
202AcceptedRequest accepted for processing (async operations).
204No ContentRequest succeeded with no response body.
Status CodeNameDescription
400Bad RequestInvalid request parameters or malformed JSON.
401UnauthorizedMissing or invalid API key.
403ForbiddenAuthenticated but insufficient permissions (scopes).
404Not FoundResource does not exist.
413Payload Too LargeRequest body exceeds size limit.
429Too Many RequestsRate limit exceeded.
Status CodeNameDescription
500Internal Server ErrorUnexpected server error.
502Bad GatewayUpstream service unavailable.
503Service UnavailableServer temporarily overloaded or down.
504Gateway TimeoutUpstream service timeout.

Common Error Codes

Authentication Errors (401)

Description: No API key provided in request headers.Causes:
  • Missing X-API-Key header
  • Empty X-API-Key header value
Response Example:
{
  "detail": "API key required. Include X-API-Key header with your request."
}
Response Headers:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: ApiKey
Solution:
# Include X-API-Key header
curl https://api.junis.ai/api/external/v1/chat/completions \
  -H "X-API-Key: jns_live_YOUR_API_KEY"
Description: API key not found, revoked, or expired.Causes:
  • API key deleted by organization admin
  • API key revoked
  • Wrong API key format
  • API key expired (if expiration was set)
Response Example:
{
  "detail": "Invalid or expired API key"
}
Solution:
  • Verify API key in Admin → API Keys
  • Generate new API key if revoked or expired
  • Check API key format: jns_live_ prefix + 32 characters

Authorization Errors (403)

Description: API key lacks required scopes for the operation.Causes:
  • API key created without required scopes
  • API key missing orchestrator:invoke scope (required for chat completions)
  • API key missing sessions:read or sessions:messages scope
Response Example:
{
  "error": "insufficient_scopes",
  "message": "API key does not have required permissions",
  "required_scopes": ["orchestrator:invoke"],
  "missing_scopes": ["orchestrator:invoke"],
  "available_scopes": ["sessions:read", "sessions:messages"]
}
Solution:
  • Check API key scopes in Admin → API Keys
  • Create new API key with required scopes:
    • orchestrator:invoke - Chat completions (both streaming and non-streaming)
    • sessions:read - List sessions and get session status
    • sessions:messages - Get session messages
Description: API key belongs to a different organization.Causes:
  • Trying to access sessions created by another organization
  • Trying to access resources not owned by your organization
Response Example:
{
  "detail": "Access denied. Resource belongs to a different organization."
}
Solution:
  • Verify you’re using the correct API key for your organization
  • Each API key can only access its own organization’s resources

Validation Errors (400)

Description: Request body validation failed.Causes:
  • Missing required fields
  • Invalid field types
  • Failed Pydantic validation
Response Example:
{
  "detail": [
    {
      "loc": ["body", "messages"],
      "msg": "field required",
      "type": "value_error.missing"
    },
    {
      "loc": ["body", "model"],
      "msg": "str type expected",
      "type": "type_error.str"
    }
  ]
}
Solution:
  • Check API documentation for required fields
  • Verify data types match schema
  • Example valid request:
{
  "messages": [
    {"role": "user", "content": "Hello"}
  ]
}
Description: Malformed JSON in request body.Causes:
  • Syntax error in JSON
  • Unescaped special characters
  • Trailing commas
Response Example:
{
  "detail": "Expecting property name enclosed in double quotes"
}
Solution:
  • Validate JSON syntax using a linter
  • Escape special characters properly
  • Remove trailing commas

Resource Errors (404)

Description: Chat session does not exist.Causes:
  • Session deleted
  • Wrong session_id
  • Session belongs to different organization
Response Example:
{
  "detail": "Session not found"
}
Solution:
  • Verify session_id is correct
  • Create new session via Chat Completions API
  • List available sessions: GET /api/external/sessions
Description: Message does not exist in session.Causes:
  • Invalid message index
  • Session has no messages yet
Response Example:
{
  "detail": "Message not found in session"
}
Solution:
  • Check session messages: GET /api/external/sessions/{id}/messages
  • Verify message index is within range

Rate Limit Errors (429)

Description: Too many requests in time window.Response Example:
{
  "error": "rate_limit_exceeded",
  "message": "Rate limit exceeded: 60 requests per minute",
  "limit": 60,
  "window": "minute",
  "reset_at": 1699564800,
  "retry_after": 45
}
Response Headers:
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit-Minute: 60
X-RateLimit-Remaining-Minute: 0
X-RateLimit-Reset-Minute: 1699564800
X-RateLimit-Limit-Hour: 1000
X-RateLimit-Remaining-Hour: 950
X-RateLimit-Reset-Hour: 1699564800
Retry-After: 45
Solution:
  • Wait for retry_after seconds before retrying
  • Implement exponential backoff
  • Monitor X-RateLimit-Remaining-* headers
  • Request higher rate limits in Admin → API Keys
  • Cache responses to reduce request frequency
See Rate Limits for more details.

Payload Errors (413)

Description: Request body exceeds size limit.Causes:
  • Large message content
  • Too many messages in array
  • Maximum payload: 10MB
Response Example:
{
  "detail": "Request payload too large. Maximum size: 10MB"
}
Solution:
  • Split large requests into smaller chunks
  • Summarize long messages
  • Use pagination for message history

Server Errors (500)

Description: Unexpected server error occurred.Causes:
  • Database connection failure
  • External service timeout
  • Unhandled exception
Response Example:
{
  "detail": "Internal server error"
}
Solution:
  • Retry request after a few seconds with exponential backoff
  • Check Status Page for incidents
  • Contact support if error persists: [email protected]
Description: LLM provider (Anthropic, OpenAI) unavailable.Causes:
  • Anthropic API outage
  • OpenAI API rate limits
  • Network timeout to LLM provider
Response Example:
{
  "detail": "LLM service temporarily unavailable. Please try again."
}
Solution:
  • Retry request with exponential backoff
  • Check Anthropic Status
  • Wait a few minutes and retry
Description: Agent orchestration failed.Causes:
  • Agent not found
  • Agent configuration error
  • Tool execution failure
Response Example:
{
  "detail": "Orchestrator processing error. Please try again."
}
Solution:
  • Retry request
  • Simplify prompt if issue persists
  • Contact support with session_id for debugging

Error Handling Best Practices

Retry Logic

Implement exponential backoff for transient errors (429, 500+ status codes):
  • 429 Rate Limit: Use the Retry-After header value to wait before retrying
  • 5xx Server Errors: Use exponential backoff (1s, 2s, 4s, etc.)
  • Max Retries: Limit to 3-5 attempts to avoid infinite loops
  • Client Errors (4xx): Do not retry, fix the request instead

Error Logging

Log errors with sufficient context for debugging:
  • Include: Timestamp, status code, error message, request URL, method
  • Exclude: Full API key (log only prefix like jns_live_abc...)
  • Structured Logging: Use JSON format for easier parsing

User-Friendly Error Messages

Present errors to users in a helpful way:
  • 401 Unauthorized: “Invalid API key. Please check your credentials.”
  • 403 Forbidden: “Permission denied. Check your API key scopes.”
  • 429 Rate Limit: “Too many requests. Please wait seconds.”
  • 5xx Server Errors: “Our servers are experiencing issues. Please try again.”
  • Generic Fallback: Use the detail field from the error response

Debugging Tools

Check API Status

curl https://api.junis.ai/api/health
Healthy Response:
{
  "status": "healthy",
  "version": "1.0.0",
  "timestamp": "2025-11-14T10:00:00Z"
}

Test API Key Authentication

curl https://api.junis.ai/api/external/sessions \
  -H "X-API-Key: jns_live_YOUR_API_KEY"
Success Response (200 OK):
{
  "sessions": [],
  "total": 0,
  "limit": 50,
  "offset": 0
}
Error Response (401 Unauthorized):
{
  "detail": "Invalid or expired API key"
}

Inspect Rate Limits

curl -I https://api.junis.ai/api/external/v1/chat/completions \
  -X POST \
  -H "X-API-Key: jns_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"test"}]}'
Response Headers:
HTTP/1.1 200 OK
X-RateLimit-Limit-Minute: 60
X-RateLimit-Remaining-Minute: 55
X-RateLimit-Reset-Minute: 1699564800
X-RateLimit-Limit-Hour: 1000
X-RateLimit-Remaining-Hour: 950
X-RateLimit-Reset-Hour: 1699564800

Monitoring and Alerts

Track Error Rates

Monitor these metrics for your API integration:

Success Rate

Track 2xx responses vs total requestsTarget: > 99% success rate

4xx Errors

Client errors (auth, validation, rate limits)Alert: > 5% of requests

5xx Errors

Server errors (outages, timeouts)Alert: Any occurrence

Response Time

P50, P95, P99 latencyTarget: P95 < 2 seconds

Sample Monitoring Code

import prometheus_client as prom

# Define metrics
api_requests = prom.Counter('api_requests_total', 'Total API requests', ['status'])
api_latency = prom.Histogram('api_latency_seconds', 'API request latency')

# Track request
with api_latency.time():
    response = requests.post(url, headers=headers, json=payload)
    api_requests.labels(status=response.status_code).inc()

Need Help?