Skip to content

Client API

Overview

The Jaxon platform provides multiple ways to integrate with your applications:

  1. Python Client Library - High-level Python API wrapping the REST endpoints
  2. REST API - Direct HTTP/REST access using curl or any HTTP client
  3. WebSocket API - Real-time streaming for advanced use cases
  4. Direct Kafka - For services running inside the Docker stack

Installation

Python Client

The client is distributed as a Python wheel file:

pip install jaxon-<version>-py3-none-any.whl

Replace <version> with the current platform version (e.g., 1.2.0).

REST API

No installation required - use curl, httpie, or any HTTP client library.

This is the simpler approach for most use cases, requiring only a URL and optional authentication token.

Basic Setup

from jaxon import Client
from jaxon.vendor.data_model import EntailmentFrame

# Connect to the platform via HTTP
api_url = "https://your-domain.com:8443/client-api"
client = Client(
    api_base_url=api_url,
    verbose=False
)

# Use the client...

# Always close when done
client.close()

With Authentication

If authentication is enabled on your platform deployment:

from jaxon import Client, ServiceAccountSession
from jaxon.vendor.data_model import EntailmentFrame
import os

# Get authentication token
token = ServiceAccountSession(
    service_account=os.getenv("SERVICE_ACCOUNT_USERNAME"),
    service_token=os.getenv("SERVICE_ACCOUNT_TOKEN"),
    session_file=os.getenv("AUTH_SESSION_FILE")
).ensure_service_account_session(debug=False)

# Create authenticated client
client = Client(
    api_base_url="https://your-domain.com:8443/client-api",
    auth_token=token,
    verbose=False
)

Direct Mode (For Internal Services)

For services running inside the Docker stack, you can use direct Kafka and etcd access:

from jaxon import Client

client = Client(
    group="my-application-client",
    bus_host="bus",         # Internal Docker service name
    bus_port=9092,          # Internal Kafka port
    config_host="config",   # Internal etcd service name
    config_port=2379,
    verbose=False
)

Sending Requests

All requests are packaged as EntailmentFrame objects:

from jaxon.vendor.data_model import EntailmentFrame

# Create an entailment frame
frame = EntailmentFrame(
    C=["Context document 1", "Context document 2"],  # Context
    Q="Your question here",                          # Question
    A="Answer to validate",                          # Answer (optional for some guardrails)
    P=[],                                            # Proof (populated by policy rules)
    E=None                                           # Evaluation (populated by guardrails)
)

# Send to a guardrail
guardrail_id = "your-guardrail-id"  # From the Web UI
response_topic = "my-response-topic"
_, trace_id = client.send_message(guardrail_id, response_topic, frame)

Receiving Responses

After sending a request, poll for the response:

# Poll for response with timeout
timeout = 120000  # milliseconds
response = client.get_response(trace_id, response_topic, timeout)

# Response is a dictionary containing the evaluated frame
print(response['entailment_frame'])  # JSON string with evaluation results

Complete Example

from jaxon import Client
from jaxon.vendor.data_model import EntailmentFrame

# Create client
client = Client(
    api_base_url="https://your-domain.com:8443/client-api",
    verbose=False
)

try:
    # Create request frame
    frame = EntailmentFrame(
        C=["All dogs go to heaven.", "Fido is a cat."],
        Q="Does Fido go to heaven?",
        A="Yes"
    )

    # Send request
    guardrail_id = "d79c0e0d"
    response_topic = "my-app-response"
    _, trace_id = client.send_message(guardrail_id, response_topic, frame)

    # Get response
    response = client.get_response(trace_id, response_topic, timeout=120000)

    # Parse result
    import json
    frame_data = json.loads(response['entailment_frame'])
    evaluation = frame_data['E']
    print(f"Conclusion: {evaluation['conclusion']}")
    print(f"Confidence: {evaluation['confidence']}")

finally:
    client.close()

REST API (Direct HTTP Access)

The Python client is a lightweight wrapper around the REST API. You can access the platform directly using HTTP requests and WebSocket.

Base URL

https://your-domain.com:8443/client-api

Note: All client API endpoints are prefixed with /client-api/ when accessed through the web interface.

Authentication

If authentication is enabled, you'll need a service account token:

# Set your service account credentials
USERNAME="your-service-account"
SERVICE_TOKEN="your-service-token"

# Exchange for JWT token (stored in TOKEN variable)
TOKEN=$(curl -s -k -X POST "https://your-domain.com:8443/client-api/auth/token" \
  -d "username=$USERNAME&service_token=$SERVICE_TOKEN" | jq -r '.token')

Complete HTTP + WebSocket Example

Here's a complete bash script that sends a request and waits for the response:

#!/bin/bash
set -e

API_URL="https://your-domain.com:8443"
GUARDRAIL_ID="your-guardrail-id"
TRACE_ID=$(uuidgen || echo "$(date +%s)-$$-$RANDOM")
TOKEN="your-service-account-token-here"

# Send request
curl -s -k -X POST "$API_URL/client-api/bus/send?target_id=$GUARDRAIL_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"trace_id": "'$TRACE_ID'", "response_topic": ["response"], "entailment_frame": {"C": ["Context document here"], "Q": "Your question?", "A": "Answer to validate", "P": [], "E": null}}' > /dev/null

# Receive response (requires websocat: brew install websocat OR apt install websocat OR yum install websocat)
WS_URL="${API_URL/https:/wss:}"
WS_URL="${WS_URL/http:/ws:}"
exec 3< <((echo '{"action":"subscribe","topic":"response","trace_id":"'$TRACE_ID'"}'; sleep 120) | \
  websocat -k "$WS_URL/client-api/bus/stream?token=$TOKEN" 2>/dev/null)

while IFS= read -r line <&3; do
  if echo "$line" | grep -q '"type":"response"'; then
    echo "$line" | jq -r '.data.entailment_frame | fromjson'
    exec 3<&-
    pkill -P $$ websocat 2>/dev/null
    exit 0
  fi
done

This script: 1. Sends the request via HTTP POST to /client-api/bus/send?target_id=... 2. Connects to WebSocket at /client-api/bus/stream?token=... 3. Subscribes to the response topic with the trace_id 4. Waits for the response message 5. Prints the pretty-printed entailment frame result 6. Exits automatically when response is received

Manual Steps

If you prefer to understand each step:

1. Send Request

TRACE_ID=$(uuidgen)

curl -k -X POST "https://your-domain.com:8443/client-api/bus/send?target_id=$GUARDRAIL_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "trace_id": "'$TRACE_ID'",
    "response_topic": ["response"],
    "entailment_frame": {
      "C": ["All dogs go to heaven.", "Fido is a cat."],
      "Q": "Does Fido go to heaven?",
      "A": "Yes",
      "P": [],
      "E": null
    }
  }'

Response:

{
  "status": "sent",
  "topic": "qa_policy_rules",
  "trace_id": "123e4567-e89b-12d3-a456-426614174000"
}

2. Receive Response via WebSocket

Connect to the WebSocket and subscribe to the response topic:

# Using websocat (brew install websocat / apt install websocat / yum install websocat)
echo '{"action":"subscribe","topic":"response","trace_id":"'$TRACE_ID'"}' | \
  websocat -k "wss://your-domain.com:8443/client-api/bus/stream?token=$TOKEN"

You'll receive: 1. An acknowledgment: {"type":"ack","action":"subscribe","success":true,...} 2. The response: {"type":"response","trace_id":"...","data":{...}}

The response data contains the evaluated entailment frame:

{
  "type": "response",
  "trace_id": "123e4567-e89b-12d3-a456-426614174000",
  "data": {
    "entailment_frame": "{\"C\":[...],\"E\":{\"conclusion\":\"NO\",\"confidence\":0.95}}",
    "trace_id": "123e4567-e89b-12d3-a456-426614174000",
    "rail_id": "your-guardrail-id"
  }
}

REST API Reference

POST /client-api/bus/send

Send a message to a guardrail or application.

URL Parameters: - target_id (required): Guardrail ID or Application ID

Request Body:

{
  "trace_id": "string",
  "response_topic": ["string"],
  "entailment_frame": {
    "C": ["string"],        // Context (array of strings)
    "Q": "string",          // Question (optional)
    "A": "string",          // Answer (optional)
    "P": [],                // Proof steps (populated by guardrail)
    "E": null               // Evaluation (populated by guardrail)
  }
}

Response:

{
  "status": "sent",
  "topic": "string",        // Kafka topic message was sent to
  "trace_id": "string"      // Your trace ID for tracking the response
}

Example:

curl -k -X POST "https://your-domain.com:8443/client-api/bus/send?target_id=d79c0e0d" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "trace_id": "550e8400-e29b-41d4-a716-446655440000",
    "response_topic": ["response"],
    "entailment_frame": {
      "C": ["Context here"],
      "Q": "Question here",
      "A": "Answer here",
      "P": [],
      "E": null
    }
  }'

POST /client-api/auth/token

Exchange service account credentials for a JWT token.

Request Parameters: - username: Service account username - service_token: Service account token

Response:

{
  "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expires_in": 3600
}

WebSocket /client-api/bus/stream

WebSocket endpoint for real-time message streaming.

Connection: wss://your-domain.com:8443/client-api/bus/stream?token=YOUR_TOKEN

Protocol:

Client sends (subscribe to topic):

{"action": "subscribe", "topic": "response", "trace_id": "550e8400-e29b-41d4-a716-446655440000"}

Server responds (acknowledgment):

{"type": "ack", "action": "subscribe", "success": true, "topic": "response", "trace_id": "550e8400-e29b-41d4-a716-446655440000"}

Server sends (when response is available):

{
  "type": "response",
  "trace_id": "550e8400-e29b-41d4-a716-446655440000",
  "data": {
    "entailment_frame": "{\"C\":[...],\"E\":{\"conclusion\":\"YES\",\"confidence\":0.95}}",
    "trace_id": "550e8400-e29b-41d4-a716-446655440000",
    "rail_id": "d79c0e0d"
  }
}

Response Structure

The response is a dictionary with the following structure:

{
    'entailment_frame': '<JSON string>',  # Evaluated EntailmentFrame
    'id': '<message-id>',
    'trace_id': '<trace-id>',
    'response_topic': [],
    'rail_id': '<guardrail-id>'
}

The entailment_frame field contains a JSON-encoded EntailmentFrame with populated E (evaluation) and optionally P (proof) fields:

{
    "C": ["context..."],
    "Q": "question...",
    "A": "answer...",
    "P": [...],
    "E": {
        "conclusion": "YES",
        "confidence": 0.95
    },
    "Error": null
}

Best Practices

Client Lifecycle

  • Create one client instance per application/service
  • Reuse the client for multiple requests
  • Always call client.close() when done (use try/finally or context managers)

Timeouts

  • Set appropriate timeouts based on your guardrail complexity
  • Policy rules guardrails: 30-60 seconds
  • Agentic guardrails (consistency, critique): 120-300 seconds
  • Complex DAGs: 300+ seconds

Response Topics

  • Use descriptive, application-specific response topic names
  • Avoid generic names like "response" when running multiple applications
  • Topics are automatically created if they don't exist

Error Handling

try:
    response = client.get_response(trace_id, response_topic, timeout)
    frame_data = json.loads(response['entailment_frame'])

    if frame_data.get('Error'):
        print(f"Guardrail error: {frame_data['Error']}")
    else:
        evaluation = frame_data['E']
        # Process evaluation...

except TimeoutError:
    print("Request timed out")
except Exception as e:
    print(f"Client error: {e}")
finally:
    client.close()

See Also

  • Tutorials - Step-by-step guides with client examples
  • Guardrails - Available guardrail types and their configurations
  • Applications - Creating applications and DAGs