API Documentation

Code Examples

Ready-to-use code examples for integrating with the CLE Engine API.

Table of Contents


cURL

Basic Request

curl -X POST https://api.cle-engine.com/v1/compute \
  -H "Content-Type: application/json" \
  -H "X-API-Key: cle_your_api_key_here" \
  -d '{
    "jurisdiction": "CA",
    "last_name": "Smith"
  }'

With Environment Variable

# Set your API key
export CLE_API_KEY="cle_your_api_key_here"

# Make requests
curl -X POST https://api.cle-engine.com/v1/compute \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $CLE_API_KEY" \
  -d '{"jurisdiction": "NY", "reporting_period_end": "2026-12-31"}'

Health Check

curl -X GET https://api.cle-engine.com/health

Save Response to File

curl -X POST https://api.cle-engine.com/v1/compute \
  -H "Content-Type: application/json" \
  -H "X-API-Key: $CLE_API_KEY" \
  -d '{"jurisdiction": "TX", "birth_date": "1985-06-15", "admission_date": "2015-11-01"}' \
  -o response.json

Python

Basic Usage

import requests

API_KEY = "cle_your_api_key_here"
BASE_URL = "https://api.cle-engine.com"

def compute_cle_deadline(jurisdiction, **kwargs):
    """Calculate CLE deadline for a jurisdiction."""
    response = requests.post(
        f"{BASE_URL}/v1/compute",
        headers={
            "Content-Type": "application/json",
            "X-API-Key": API_KEY
        },
        json={
            "jurisdiction": jurisdiction,
            **kwargs
        }
    )
    response.raise_for_status()
    return response.json()

# Example usage
result = compute_cle_deadline("CA", last_name="Smith")
print(f"Due date: {result['due_date']}")
print(f"Compliance group: {result['reporting_group']}")

With Error Handling

import requests
from requests.exceptions import HTTPError, Timeout, RequestException

API_KEY = "cle_your_api_key_here"
BASE_URL = "https://api.cle-engine.com"

def compute_cle_deadline(jurisdiction, **kwargs):
    """Calculate CLE deadline with comprehensive error handling."""
    try:
        response = requests.post(
            f"{BASE_URL}/v1/compute",
            headers={
                "Content-Type": "application/json",
                "X-API-Key": API_KEY
            },
            json={"jurisdiction": jurisdiction, **kwargs},
            timeout=30
        )

        if response.status_code == 200:
            data = response.json()

            # Check for missing required fields
            if data.get("missing_fields"):
                missing = ", ".join(data["missing_fields"])
                return {"error": f"Missing required fields: {missing}", "data": data}

            return {"success": True, "data": data}

        elif response.status_code == 401:
            return {"error": "Invalid API key"}
        elif response.status_code == 429:
            return {"error": "Rate limit exceeded. Please wait and retry."}
        else:
            return {"error": f"API error: {response.status_code}"}

    except Timeout:
        return {"error": "Request timed out"}
    except RequestException as e:
        return {"error": f"Network error: {str(e)}"}

# Example usage
result = compute_cle_deadline("CA", last_name="Garcia")

if result.get("success"):
    print(f"Due date: {result['data']['due_date']}")
else:
    print(f"Error: {result['error']}")

Using a Class

import requests
import os
from dataclasses import dataclass
from typing import Optional, List
from datetime import date

@dataclass
class CLEDeadline:
    due_date: Optional[str]
    cycle_start: Optional[str]
    cycle_end: Optional[str]
    credits_required: Optional[int]
    reporting_group: Optional[str]
    cle_required: Optional[bool]
    required_fields: List[str]
    missing_fields: List[str]
    notes: Optional[str]

class CLEEngineClient:
    """Client for the CLE Engine API."""

    def __init__(self, api_key: Optional[str] = None):
        self.api_key = api_key or os.environ.get("CLE_API_KEY")
        self.base_url = "https://api.cle-engine.com"

        if not self.api_key:
            raise ValueError("API key required. Set CLE_API_KEY environment variable.")

    def compute(
        self,
        jurisdiction: str,
        profession: str = "lawyer",
        last_name: Optional[str] = None,
        birth_date: Optional[date] = None,
        admission_date: Optional[date] = None,
        reporting_category: Optional[str] = None,
        reporting_period_end: Optional[date] = None,
        renewal_year: Optional[int] = None
    ) -> CLEDeadline:
        """Calculate CLE deadline for a jurisdiction."""

        payload = {"jurisdiction": jurisdiction, "profession": profession}

        if last_name:
            payload["last_name"] = last_name
        if birth_date:
            payload["birth_date"] = birth_date.isoformat()
        if admission_date:
            payload["admission_date"] = admission_date.isoformat()
        if reporting_category:
            payload["reporting_category"] = reporting_category
        if reporting_period_end:
            payload["reporting_period_end"] = reporting_period_end.isoformat()
        if renewal_year:
            payload["renewal_year"] = renewal_year

        response = requests.post(
            f"{self.base_url}/v1/compute",
            headers={
                "Content-Type": "application/json",
                "X-API-Key": self.api_key
            },
            json=payload
        )
        response.raise_for_status()

        data = response.json()
        return CLEDeadline(
            due_date=data.get("due_date"),
            cycle_start=data.get("cycle_start"),
            cycle_end=data.get("cycle_end"),
            credits_required=data.get("credits_required"),
            reporting_group=data.get("reporting_group"),
            cle_required=data.get("cle_required"),
            required_fields=data.get("required_fields", []),
            missing_fields=data.get("missing_fields", []),
            notes=data.get("notes")
        )

    def health_check(self) -> bool:
        """Check API health status."""
        response = requests.get(f"{self.base_url}/health")
        return response.status_code == 200

# Example usage
client = CLEEngineClient()

# Check API health
if client.health_check():
    print("API is healthy")

# Calculate deadline
deadline = client.compute(
    jurisdiction="CO",
    admission_date=date(2020, 6, 15)
)

if deadline.missing_fields:
    print(f"Missing: {deadline.missing_fields}")
else:
    print(f"Due date: {deadline.due_date}")
    print(f"Notes: {deadline.notes}")

JavaScript/Node.js

Using Fetch (Node.js 18+)

const API_KEY = 'cle_your_api_key_here';
const BASE_URL = 'https://api.cle-engine.com';

async function computeCLEDeadline(jurisdiction, options = {}) {
  const response = await fetch(`${BASE_URL}/v1/compute`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY
    },
    body: JSON.stringify({
      jurisdiction,
      ...options
    })
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(error.detail || `HTTP ${response.status}`);
  }

  return response.json();
}

// Example usage
async function main() {
  try {
    const result = await computeCLEDeadline('CA', { last_name: 'Smith' });
    console.log('Due date:', result.due_date);
    console.log('Reporting group:', result.reporting_group);
  } catch (error) {
    console.error('Error:', error.message);
  }
}

main();

Using Axios

const axios = require('axios');

const client = axios.create({
  baseURL: 'https://api.cle-engine.com',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.CLE_API_KEY
  }
});

async function computeCLEDeadline(jurisdiction, options = {}) {
  try {
    const { data } = await client.post('/v1/compute', {
      jurisdiction,
      ...options
    });

    if (data.missing_fields && data.missing_fields.length > 0) {
      console.warn('Missing fields:', data.missing_fields.join(', '));
    }

    return data;
  } catch (error) {
    if (error.response) {
      // API returned an error
      console.error('API Error:', error.response.data.detail);
    } else {
      // Network or other error
      console.error('Request failed:', error.message);
    }
    throw error;
  }
}

// Example: Batch processing multiple attorneys
async function processAttorneys(attorneys) {
  const results = [];

  for (const attorney of attorneys) {
    const result = await computeCLEDeadline(attorney.jurisdiction, {
      last_name: attorney.lastName,
      admission_date: attorney.admissionDate
    });
    results.push({
      attorney: attorney.name,
      ...result
    });

    // Add small delay to avoid rate limiting
    await new Promise(resolve => setTimeout(resolve, 100));
  }

  return results;
}

TypeScript

import axios, { AxiosInstance } from 'axios';

interface ComputeRequest {
  jurisdiction: string;
  profession?: string;
  last_name?: string;
  birth_date?: string;
  admission_date?: string;
  reporting_category?: string;
  reporting_period_end?: string;
  renewal_year?: number;
}

interface Citation {
  source_id: string;
  excerpt_id?: string;
  url?: string;
}

interface ComputeResponse {
  due_date: string | null;
  cycle_start: string | null;
  cycle_end: string | null;
  credits_required: number | null;
  reporting_group: string | null;
  cle_required: boolean | null;
  required_fields: string[];
  missing_fields: string[];
  citations: Citation[];
  notes: string | null;
}

class CLEEngineClient {
  private client: AxiosInstance;

  constructor(apiKey: string) {
    this.client = axios.create({
      baseURL: 'https://api.cle-engine.com',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': apiKey
      }
    });
  }

  async compute(request: ComputeRequest): Promise<ComputeResponse> {
    const { data } = await this.client.post<ComputeResponse>('/v1/compute', request);
    return data;
  }

  async healthCheck(): Promise<boolean> {
    try {
      const { data } = await this.client.get<{ status: string }>('/health');
      return data.status === 'ok';
    } catch {
      return false;
    }
  }
}

// Example usage
async function main(): Promise<void> {
  const client = new CLEEngineClient(process.env.CLE_API_KEY!);

  // Health check
  const isHealthy = await client.healthCheck();
  console.log('API healthy:', isHealthy);

  // Compute deadline
  const result = await client.compute({
    jurisdiction: 'NJ',
    birth_date: '1985-03-15'
  });

  if (result.missing_fields.length > 0) {
    console.log('Missing fields:', result.missing_fields);
  } else {
    console.log('Due date:', result.due_date);
    console.log('Group:', result.reporting_group);
  }
}

main().catch(console.error);

Ruby

require 'net/http'
require 'json'
require 'uri'

class CLEEngineClient
  BASE_URL = 'https://api.cle-engine.com'

  def initialize(api_key)
    @api_key = api_key
  end

  def compute(jurisdiction:, **options)
    uri = URI("#{BASE_URL}/v1/compute")

    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true

    request = Net::HTTP::Post.new(uri)
    request['Content-Type'] = 'application/json'
    request['X-API-Key'] = @api_key
    request.body = { jurisdiction: jurisdiction, **options }.to_json

    response = http.request(request)

    case response.code.to_i
    when 200
      JSON.parse(response.body)
    when 401
      raise 'Invalid API key'
    when 429
      raise 'Rate limit exceeded'
    else
      raise "API error: #{response.code}"
    end
  end

  def health_check
    uri = URI("#{BASE_URL}/health")
    response = Net::HTTP.get_response(uri)
    response.code == '200'
  end
end

# Example usage
client = CLEEngineClient.new(ENV['CLE_API_KEY'])

result = client.compute(
  jurisdiction: 'CA',
  last_name: 'Smith'
)

puts "Due date: #{result['due_date']}"
puts "Group: #{result['reporting_group']}"

PHP

<?php

class CLEEngineClient {
    private string $apiKey;
    private string $baseUrl = 'https://api.cle-engine.com';

    public function __construct(string $apiKey) {
        $this->apiKey = $apiKey;
    }

    public function compute(string $jurisdiction, array $options = []): array {
        $payload = array_merge(['jurisdiction' => $jurisdiction], $options);

        $ch = curl_init($this->baseUrl . '/v1/compute');
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($payload),
            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'X-API-Key: ' . $this->apiKey
            ]
        ]);

        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($httpCode === 401) {
            throw new Exception('Invalid API key');
        } elseif ($httpCode === 429) {
            throw new Exception('Rate limit exceeded');
        } elseif ($httpCode !== 200) {
            throw new Exception("API error: $httpCode");
        }

        return json_decode($response, true);
    }

    public function healthCheck(): bool {
        $ch = curl_init($this->baseUrl . '/health');
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return $httpCode === 200;
    }
}

// Example usage
$client = new CLEEngineClient($_ENV['CLE_API_KEY']);

$result = $client->compute('CA', ['last_name' => 'Smith']);

echo "Due date: " . $result['due_date'] . "\n";
echo "Group: " . $result['reporting_group'] . "\n";

Go

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

const baseURL = "https://api.cle-engine.com"

type ComputeRequest struct {
	Jurisdiction       string `json:"jurisdiction"`
	Profession         string `json:"profession,omitempty"`
	LastName           string `json:"last_name,omitempty"`
	BirthDate          string `json:"birth_date,omitempty"`
	AdmissionDate      string `json:"admission_date,omitempty"`
	ReportingCategory  string `json:"reporting_category,omitempty"`
	ReportingPeriodEnd string `json:"reporting_period_end,omitempty"`
	RenewalYear        int    `json:"renewal_year,omitempty"`
}

type Citation struct {
	SourceID  string `json:"source_id"`
	ExcerptID string `json:"excerpt_id,omitempty"`
	URL       string `json:"url,omitempty"`
}

type ComputeResponse struct {
	DueDate         *string    `json:"due_date"`
	CycleStart      *string    `json:"cycle_start"`
	CycleEnd        *string    `json:"cycle_end"`
	CreditsRequired *int       `json:"credits_required"`
	ReportingGroup  *string    `json:"reporting_group"`
	CLERequired     *bool      `json:"cle_required"`
	RequiredFields  []string   `json:"required_fields"`
	MissingFields   []string   `json:"missing_fields"`
	Citations       []Citation `json:"citations"`
	Notes           *string    `json:"notes"`
}

type CLEClient struct {
	apiKey     string
	httpClient *http.Client
}

func NewCLEClient(apiKey string) *CLEClient {
	return &CLEClient{
		apiKey:     apiKey,
		httpClient: &http.Client{},
	}
}

func (c *CLEClient) Compute(req ComputeRequest) (*ComputeResponse, error) {
	body, err := json.Marshal(req)
	if err != nil {
		return nil, err
	}

	httpReq, err := http.NewRequest("POST", baseURL+"/v1/compute", bytes.NewBuffer(body))
	if err != nil {
		return nil, err
	}

	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("X-API-Key", c.apiKey)

	resp, err := c.httpClient.Do(httpReq)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode == 401 {
		return nil, fmt.Errorf("invalid API key")
	} else if resp.StatusCode == 429 {
		return nil, fmt.Errorf("rate limit exceeded")
	} else if resp.StatusCode != 200 {
		return nil, fmt.Errorf("API error: %d", resp.StatusCode)
	}

	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var result ComputeResponse
	if err := json.Unmarshal(respBody, &result); err != nil {
		return nil, err
	}

	return &result, nil
}

func main() {
	client := NewCLEClient(os.Getenv("CLE_API_KEY"))

	result, err := client.Compute(ComputeRequest{
		Jurisdiction: "CA",
		LastName:     "Smith",
	})

	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	if len(result.MissingFields) > 0 {
		fmt.Printf("Missing fields: %v\n", result.MissingFields)
	} else {
		fmt.Printf("Due date: %s\n", *result.DueDate)
		if result.ReportingGroup != nil {
			fmt.Printf("Group: %s\n", *result.ReportingGroup)
		}
	}
}

Common Patterns

Environment Variables

Always use environment variables for API keys:

# .env file
CLE_API_KEY=cle_your_api_key_here

Retry Logic

Implement retries for transient failures:

import time
from functools import wraps

def retry(max_attempts=3, backoff_factor=2):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            for attempt in range(max_attempts):
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    if attempt == max_attempts - 1:
                        raise
                    wait = backoff_factor ** attempt
                    time.sleep(wait)
        return wrapper
    return decorator

@retry(max_attempts=3)
def make_api_call():
    # Your API call here
    pass

Caching

Cache results to reduce API calls:

from functools import lru_cache
from datetime import date

@lru_cache(maxsize=1000)
def get_deadline(jurisdiction: str, **kwargs) -> dict:
    # Cache key is automatically generated from arguments
    return api.compute(jurisdiction, **kwargs)