Skip to content

Latest commit

 

History

History
1158 lines (944 loc) · 32 KB

File metadata and controls

1158 lines (944 loc) · 32 KB

IntendedOps API Guide

This guide covers authentication, common operations, error handling, and integration patterns for the IntendedOps API.

Table of Contents


Authentication

All API requests require authentication via API key.

API Key Format

IntendedOps API keys follow the format:

intendedops_<tenantId>_<secretPart>

For example: intendedops_tenant123_xK9mN2pQ7rS4tU6v

The tenant ID is extracted from the key automatically.

Including the API Key

Include your API key in the x-api-key header:

curl -H "x-api-key: intendedops_tenant123_xK9mN2pQ7rS4tU6v" \
  https://api.intendedops.dev/v1/stats

Alternative: Separate Tenant ID

You can also provide the tenant ID via the x-tenant-id header:

curl -H "x-api-key: your-api-key" \
     -H "x-tenant-id: tenant123" \
  https://api.intendedops.dev/v1/stats

Obtaining an API Key

API keys are created through the IntendedOps Console:

  1. Navigate to Settings > API Keys
  2. Click Create API Key
  3. Choose the permissions scope
  4. Copy the key (it won't be shown again)

Quick Start

1. Verify Connectivity

curl https://api.intendedops.dev/health

Response:

{
  "data": {
    "status": "healthy",
    "timestamp": "2024-01-15T10:30:00.000Z"
  }
}

2. Get Dashboard Stats

curl -H "x-api-key: intendedops_tenant123_xK9mN2pQ7rS4tU6v" \
  https://api.intendedops.dev/v1/stats

Response:

{
  "data": {
    "intentsToday": 145,
    "pendingApprovals": 12,
    "activeAgents": 5,
    "valueGenerated": 5420.00
  }
}

3. Register an Agent

curl -X POST \
  -H "x-api-key: intendedops_tenant123_xK9mN2pQ7rS4tU6v" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Billing Agent",
    "description": "Handles billing operations",
    "domains": ["billing"],
    "intentTypes": ["billing.send_invoice", "billing.collect_payment"]
  }' \
  https://api.intendedops.dev/v1/agents

4. Submit an Intent

curl -X POST \
  -H "x-api-key: intendedops_tenant123_xK9mN2pQ7rS4tU6v" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "billing.send_invoice",
    "domain": "billing",
    "agentId": "billing-agent",
    "params": {
      "customerId": "cust_123",
      "amount": 99.99
    },
    "description": "Send monthly subscription invoice",
    "idempotencyKey": "invoice-cust_123-2024-01"
  }' \
  https://api.intendedops.dev/v1/intents

Response:

{
  "data": {
    "intentId": "int_01HN4X7KQZ8Y1A2B3C4D5E6F7G",
    "status": "pending",
    "createdAt": "2024-01-15T10:30:00.000Z"
  }
}

Domains & Intent Types

IntendedOps organizes agent capabilities into domains. Each domain has specific intent types that agents can submit.

Available Domains

Domain Description Agent Package
billing Invoice, payment, subscription management @intendedops/billing-agent
support Ticket management, SLA tracking, escalation @intendedops/support-agent
sales Lead management, pipeline, opportunities @intendedops/sales-agent
compliance Controls, evidence, DSAR, risk assessment @intendedops/compliance-agent
vendors Vendor onboarding, assessment, contracts @intendedops/vendors-agent
contracts Contract lifecycle, signatures, renewals @intendedops/contracts-agent
identity User provisioning, access reviews, SSO @intendedops/identity-agent

Billing Intent Types

Intent Type Description Typical Value
billing.send_invoice Generate and send invoice Efficiency
billing.collect_payment Process payment collection Revenue
billing.process_refund Issue refund to customer -
billing.update_subscription Modify subscription plan Revenue
billing.apply_credit Apply credit to account -
billing.generate_statement Create account statement Efficiency
billing.retry_payment Retry failed payment Revenue
billing.cancel_subscription Cancel customer subscription -
billing.process_dunning Handle overdue payments Revenue
billing.reconcile_payments Reconcile payment records Efficiency

Support Intent Types

Intent Type Description Typical Value
support.create_ticket Create support ticket Efficiency
support.assign_ticket Route ticket to agent Efficiency
support.escalate_ticket Escalate to higher tier Risk Mitigation
support.resolve_ticket Mark ticket resolved Efficiency
support.send_response Send customer response Efficiency
support.update_priority Change ticket priority Risk Mitigation
support.merge_tickets Combine duplicate tickets Efficiency
support.create_knowledge_article Document solution Cost Savings
support.request_feedback Request CSAT survey -
support.auto_categorize AI-powered categorization Efficiency

Sales Intent Types

Intent Type Description Typical Value
sales.create_lead Create new lead Revenue
sales.qualify_lead Qualify/disqualify lead Efficiency
sales.create_opportunity Create sales opportunity Revenue
sales.update_pipeline Update deal stage Revenue
sales.schedule_call Schedule sales call Efficiency
sales.send_proposal Send proposal/quote Revenue
sales.create_quote Generate price quote Revenue
sales.assign_territory Assign rep territory Efficiency
sales.forecast_revenue Generate forecast -
sales.log_activity Log sales activity -

Compliance Intent Types

Intent Type Description Typical Value
compliance.check_control Run control assessment Risk Mitigation
compliance.record_evidence Document compliance evidence Risk Mitigation
compliance.generate_report Create compliance report Efficiency
compliance.process_dsar Handle data subject request Risk Mitigation
compliance.update_risk_assessment Update risk register Risk Mitigation
compliance.schedule_audit Schedule compliance audit Risk Mitigation
compliance.track_remediation Track finding remediation Risk Mitigation
compliance.assess_vendor Vendor compliance check Risk Mitigation
compliance.review_policy Policy review workflow Risk Mitigation
compliance.train_employee Compliance training Risk Mitigation

Vendors Intent Types

Intent Type Description Typical Value
vendors.onboard_vendor Start vendor onboarding Efficiency
vendors.request_assessment Request vendor assessment Risk Mitigation
vendors.review_contract Review vendor contract Risk Mitigation
vendors.update_risk_score Update vendor risk rating Risk Mitigation
vendors.schedule_review Schedule periodic review Risk Mitigation
vendors.request_documents Request compliance docs Risk Mitigation
vendors.approve_vendor Approve vendor status Efficiency
vendors.terminate_vendor Offboard vendor Risk Mitigation
vendors.track_performance Monitor vendor KPIs Cost Savings
vendors.renew_contract Process contract renewal -

Contracts Intent Types

Intent Type Description Typical Value
contracts.create_agreement Create new contract Efficiency
contracts.send_for_signature Send for e-signature Efficiency
contracts.generate_amendment Create amendment Efficiency
contracts.notify_expiration Send expiration notice Risk Mitigation
contracts.archive_contract Archive completed contract Efficiency
contracts.request_approval Route for internal approval Efficiency
contracts.track_obligation Track contract obligations Risk Mitigation
contracts.extract_terms AI extraction of key terms Efficiency
contracts.compare_versions Compare contract versions Efficiency
contracts.auto_renew Process auto-renewal Revenue

Identity Intent Types

Intent Type Description Typical Value
identity.provision_user Create user account Efficiency
identity.deprovision_user Remove user access Risk Mitigation
identity.assign_role Assign role to user Efficiency
identity.revoke_role Remove role from user Risk Mitigation
identity.initiate_access_review Start access certification Risk Mitigation
identity.complete_access_review Complete review cycle Risk Mitigation
identity.reset_credentials Reset password/MFA Efficiency
identity.enable_mfa Enable multi-factor auth Risk Mitigation
identity.create_group Create security group Efficiency
identity.sync_directory Sync with IdP Efficiency
identity.audit_permissions Audit user permissions Risk Mitigation
identity.request_access Self-service access request Efficiency
identity.approve_access Approve access request Efficiency
identity.detect_anomaly Flag suspicious activity Risk Mitigation
identity.rotate_credentials Rotate service credentials Risk Mitigation

Intent Examples by Domain

Sales - Create Opportunity:

curl -X POST \
  -H "x-api-key: $INTENDEDOPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "sales.create_opportunity",
    "domain": "sales",
    "agentId": "sales-agent",
    "params": {
      "leadId": "lead_123",
      "dealName": "Enterprise License - Acme Corp",
      "amount": 50000,
      "stage": "qualification",
      "closeDate": "2026-03-31"
    },
    "description": "Convert qualified lead to opportunity"
  }' \
  https://api.intendedops.dev/v1/intents

Compliance - Process DSAR:

curl -X POST \
  -H "x-api-key: $INTENDEDOPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "compliance.process_dsar",
    "domain": "compliance",
    "agentId": "compliance-agent",
    "params": {
      "requestType": "access",
      "subjectEmail": "user@example.com",
      "deadline": "2026-03-01"
    },
    "description": "Process GDPR data access request",
    "justification": "Article 15 request received via privacy portal"
  }' \
  https://api.intendedops.dev/v1/intents

Identity - Initiate Access Review:

curl -X POST \
  -H "x-api-key: $INTENDEDOPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "identity.initiate_access_review",
    "domain": "identity",
    "agentId": "identity-agent",
    "params": {
      "reviewType": "quarterly",
      "scope": ["engineering", "finance"],
      "reviewers": ["manager_123", "security_456"],
      "deadline": "2026-02-28"
    },
    "description": "Q1 quarterly access certification"
  }' \
  https://api.intendedops.dev/v1/intents

Contracts - Send for Signature:

curl -X POST \
  -H "x-api-key: $INTENDEDOPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "contracts.send_for_signature",
    "domain": "contracts",
    "agentId": "contracts-agent",
    "params": {
      "contractId": "contract_456",
      "signers": [
        {"email": "legal@acme.com", "name": "Jane Smith", "order": 1},
        {"email": "ceo@acme.com", "name": "John Doe", "order": 2}
      ],
      "deadline": "2026-02-15",
      "reminderDays": [7, 3, 1]
    },
    "description": "Send MSA for customer signature"
  }' \
  https://api.intendedops.dev/v1/intents

Metrics

IntendedOps provides built-in metrics for monitoring agent operations. Use these to track intent processing, trust progression, and value generation.

Standard Metrics

Metric Type Description
intendedops_intents_submitted_total Counter Total intents submitted
intendedops_intents_evaluated_total Counter Intents evaluated by governance
intendedops_intents_executed_total Counter Successfully executed intents
intendedops_intents_blocked_total Counter Intents blocked by policy
intendedops_intents_pending_approval Gauge Currently pending human approval
intendedops_intent_evaluation_duration_seconds Histogram Time to evaluate intent
intendedops_intent_execution_duration_seconds Histogram Time to execute intent
intendedops_trust_progressions_total Counter Trust tier promotions
intendedops_trust_demotions_total Counter Trust tier demotions
intendedops_trust_score Gauge Current trust score by agent
intendedops_agents_by_tier Gauge Agent count per trust tier
intendedops_value_generated_cents_total Counter Total value generated (cents)
intendedops_value_by_type_cents_total Counter Value by type (efficiency, revenue, etc.)
intendedops_jobs_processed_total Counter Background jobs processed
intendedops_jobs_failed_total Counter Failed background jobs
intendedops_webhooks_received_total Counter Webhooks received
intendedops_governance_decisions_total Counter Governance decisions made

Metric Labels

All metrics support the following labels:

Label Description
tenantId Tenant identifier
agentId Agent identifier
domain Intent domain (billing, support, etc.)
intentType Specific intent type
status Outcome status
trustTier Trust tier (T0, T1, T2, etc.)

Using Metrics in Code

import { metrics, IntendedOps_METRICS, getMetricsCollector } from "@intendedops/core";

// Record intent submission
metrics.intentSubmitted({
  tenantId: "tenant_123",
  agentId: "billing-agent",
  domain: "billing",
  intentType: "billing.send_invoice",
});

// Record with timing
const stopTimer = metrics.startTimer(
  IntendedOps_METRICS.INTENT_EXECUTION_DURATION,
  { agentId: "billing-agent" }
);
// ... do work ...
const durationSeconds = stopTimer();

// Record value generated
metrics.valueGenerated(9900, {
  tenantId: "tenant_123",
  domain: "billing",
});

// Get all metrics (for export to Prometheus, etc.)
const collector = getMetricsCollector();
const allMetrics = collector.getMetrics();

Custom Metrics Collector

Implement MetricsCollector to integrate with your monitoring system:

import { MetricsCollector, setMetricsCollector } from "@intendedops/core";
import { Counter, Gauge, Histogram, Registry } from "prom-client";

class PrometheusCollector implements MetricsCollector {
  private registry = new Registry();
  private counters = new Map<string, Counter>();
  private gauges = new Map<string, Gauge>();
  private histograms = new Map<string, Histogram>();

  increment(name: string, labels = {}, value = 1) {
    let counter = this.counters.get(name);
    if (!counter) {
      counter = new Counter({ name, help: name, labelNames: Object.keys(labels) });
      this.registry.registerMetric(counter);
      this.counters.set(name, counter);
    }
    counter.inc(labels, value);
  }

  // ... implement other methods
}

// Set as global collector
setMetricsCollector(new PrometheusCollector());

Error Handling

Error Response Format

All errors follow a consistent structure:

{
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable description",
    "details": { }
  }
}

Common Error Codes

Code HTTP Status Description
UNAUTHORIZED 401 API key missing or invalid
FORBIDDEN 403 Insufficient permissions for operation
NOT_FOUND 404 Resource not found
VALIDATION_ERROR 400 Request body validation failed
ALREADY_RESOLVED 400 Approval already approved/rejected
AGENT_NOT_FOUND 404 Agent specified in intent doesn't exist
RATE_LIMITED 429 Too many requests
INTERNAL_ERROR 500 Unexpected server error

Validation Errors

Validation errors include details about which fields failed:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "type: Intent type is required; agentId: Agent ID is required",
    "details": [
      {
        "path": ["type"],
        "message": "Intent type is required"
      },
      {
        "path": ["agentId"],
        "message": "Agent ID is required"
      }
    ]
  }
}

Handling Errors in Code

JavaScript/TypeScript:

async function submitIntent(intent: CreateIntentRequest): Promise<Intent> {
  const response = await fetch('https://api.intendedops.dev/v1/intents', {
    method: 'POST',
    headers: {
      'x-api-key': process.env.INTENDEDOPS_API_KEY!,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(intent),
  });

  const result = await response.json();

  if (!response.ok) {
    const error = result.error;
    switch (error.code) {
      case 'VALIDATION_ERROR':
        throw new ValidationError(error.message, error.details);
      case 'AGENT_NOT_FOUND':
        throw new NotFoundError('Agent not found');
      case 'RATE_LIMITED':
        // Wait and retry
        await sleep(60000);
        return submitIntent(intent);
      default:
        throw new ApiError(error.code, error.message);
    }
  }

  return result.data;
}

Python:

import requests
import time

def submit_intent(intent: dict) -> dict:
    response = requests.post(
        'https://api.intendedops.dev/v1/intents',
        headers={
            'x-api-key': os.environ['INTENDEDOPS_API_KEY'],
            'Content-Type': 'application/json',
        },
        json=intent
    )

    result = response.json()

    if not response.ok:
        error = result.get('error', {})
        code = error.get('code', 'UNKNOWN')
        message = error.get('message', 'Unknown error')

        if code == 'RATE_LIMITED':
            time.sleep(60)
            return submit_intent(intent)
        elif code == 'VALIDATION_ERROR':
            raise ValidationError(message, error.get('details'))
        else:
            raise ApiError(code, message)

    return result['data']

Rate Limiting

Default Limits

  • 1000 requests per minute per API key
  • Limits reset at the start of each minute window

Rate Limit Response

When rate limited, the API returns HTTP 429:

{
  "error": {
    "code": "RATE_LIMITED",
    "message": "Too many requests"
  }
}

Best Practices

  1. Implement exponential backoff:
async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3
): Promise<T> {
  let delay = 1000;

  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (error.code === 'RATE_LIMITED' && attempt < maxRetries) {
        await sleep(delay);
        delay *= 2; // Exponential backoff
        continue;
      }
      throw error;
    }
  }
  throw new Error('Max retries exceeded');
}
  1. Use batch operations where available

  2. Cache frequently accessed data locally

  3. Monitor your usage in the IntendedOps Console


Webhook Setup

IntendedOps can send webhooks to your endpoints when events occur. This is useful for:

  • Real-time notifications of intent status changes
  • Integrating with external workflow systems
  • Building custom dashboards

Configuring Webhooks

Configure webhooks in the IntendedOps Console or via the adapters system.

Webhook Payload Format

All webhooks follow this structure:

{
  "id": "whk_01HN4X7KQZ8Y1A2B3C4D5E6F7G",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "tenantId": "tenant123",
  "event": "intent.approved",
  "data": {
    "intentId": "int_abc123",
    "type": "billing.send_invoice",
    "status": "approved",
    "approvedBy": "user_456"
  }
}

Event Types

Event Description
intent.submitted New intent created
intent.approved Intent approved by human
intent.rejected Intent rejected by human
intent.completed Intent execution completed
intent.failed Intent execution failed
agent.registered New agent registered
agent.trust.changed Agent trust tier changed

Verifying Webhooks

Webhooks include a signature in the x-intendedops-signature header:

import crypto from 'crypto';

function verifyWebhook(
  payload: string,
  signature: string,
  secret: string
): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Express example
app.post('/webhooks/intendedops', (req, res) => {
  const signature = req.headers['x-intendedops-signature'];
  const payload = JSON.stringify(req.body);

  if (!verifyWebhook(payload, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  // Process webhook
  const event = req.body;
  console.log(`Received ${event.event}:`, event.data);

  res.status(200).send('OK');
});

Code Examples

cURL

List all intents:

curl -H "x-api-key: $INTENDEDOPS_API_KEY" \
  "https://api.intendedops.dev/v1/intents?page=1&pageSize=50"

Get pending approvals:

curl -H "x-api-key: $INTENDEDOPS_API_KEY" \
  https://api.intendedops.dev/v1/approvals

Approve an intent:

curl -X POST \
  -H "x-api-key: $INTENDEDOPS_API_KEY" \
  https://api.intendedops.dev/v1/approvals/apr_123/approve

Reject an intent with reason:

curl -X POST \
  -H "x-api-key: $INTENDEDOPS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Amount exceeds approved limit"}' \
  https://api.intendedops.dev/v1/approvals/apr_123/reject

Verify audit chain:

curl -H "x-api-key: $INTENDEDOPS_API_KEY" \
  https://api.intendedops.dev/v1/audit/verify

JavaScript / TypeScript

Full client implementation:

interface IntendedOpsConfig {
  apiKey: string;
  baseUrl?: string;
}

interface Intent {
  id: string;
  type: string;
  domain: string;
  status: string;
  params: Record<string, unknown>;
  createdAt: string;
}

interface CreateIntentRequest {
  type: string;
  domain: string;
  agentId: string;
  params?: Record<string, unknown>;
  description?: string;
  justification?: string;
  idempotencyKey?: string;
}

class IntendedOpsClient {
  private apiKey: string;
  private baseUrl: string;

  constructor(config: IntendedOpsConfig) {
    this.apiKey = config.apiKey;
    this.baseUrl = config.baseUrl || 'https://api.intendedops.dev';
  }

  private async request<T>(
    method: string,
    path: string,
    body?: unknown
  ): Promise<T> {
    const response = await fetch(`${this.baseUrl}${path}`, {
      method,
      headers: {
        'x-api-key': this.apiKey,
        'Content-Type': 'application/json',
      },
      body: body ? JSON.stringify(body) : undefined,
    });

    const result = await response.json();

    if (!response.ok) {
      throw new Error(result.error?.message || 'API error');
    }

    return result.data;
  }

  // Stats
  async getStats() {
    return this.request<{
      intentsToday: number;
      pendingApprovals: number;
      activeAgents: number;
      valueGenerated: number;
    }>('GET', '/v1/stats');
  }

  // Intents
  async listIntents(options?: {
    page?: number;
    pageSize?: number;
    status?: string;
    agentId?: string;
  }) {
    const params = new URLSearchParams();
    if (options?.page) params.set('page', String(options.page));
    if (options?.pageSize) params.set('pageSize', String(options.pageSize));
    if (options?.status) params.set('status', options.status);
    if (options?.agentId) params.set('agentId', options.agentId);

    const query = params.toString();
    return this.request<{ items: Intent[]; total: number }>(
      'GET',
      `/v1/intents${query ? `?${query}` : ''}`
    );
  }

  async createIntent(intent: CreateIntentRequest) {
    return this.request<{ intentId: string; status: string }>(
      'POST',
      '/v1/intents',
      intent
    );
  }

  async getIntent(intentId: string) {
    return this.request<Intent>('GET', `/v1/intents/${intentId}`);
  }

  // Agents
  async listAgents() {
    return this.request<{ items: unknown[] }>('GET', '/v1/agents');
  }

  async registerAgent(agent: {
    name: string;
    description?: string;
    domains?: string[];
    intentTypes?: string[];
  }) {
    return this.request<unknown>('POST', '/v1/agents', agent);
  }

  async getAgentMetrics(agentId: string) {
    return this.request<unknown>('GET', `/v1/agents/${agentId}/metrics`);
  }

  // Approvals
  async listApprovals() {
    return this.request<{ items: unknown[] }>('GET', '/v1/approvals');
  }

  async approveIntent(approvalId: string) {
    return this.request<unknown>(
      'POST',
      `/v1/approvals/${approvalId}/approve`
    );
  }

  async rejectIntent(approvalId: string, reason?: string) {
    return this.request<unknown>(
      'POST',
      `/v1/approvals/${approvalId}/reject`,
      { reason }
    );
  }

  // Audit
  async queryAudit(options?: {
    eventType?: string;
    fromDate?: string;
    toDate?: string;
  }) {
    const params = new URLSearchParams();
    if (options?.eventType) params.set('eventType', options.eventType);
    if (options?.fromDate) params.set('fromDate', options.fromDate);
    if (options?.toDate) params.set('toDate', options.toDate);

    const query = params.toString();
    return this.request<{ items: unknown[] }>(
      'GET',
      `/v1/audit${query ? `?${query}` : ''}`
    );
  }

  async verifyAuditChain() {
    return this.request<{ valid: boolean; eventCount: number }>(
      'GET',
      '/v1/audit/verify'
    );
  }
}

// Usage
const intendedops = new IntendedOpsClient({ apiKey: process.env.INTENDEDOPS_API_KEY! });

async function main() {
  // Get stats
  const stats = await intendedops.getStats();
  console.log(`Pending approvals: ${stats.pendingApprovals}`);

  // Submit an intent
  const result = await intendedops.createIntent({
    type: 'billing.send_invoice',
    domain: 'billing',
    agentId: 'billing-agent',
    params: { customerId: 'cust_123', amount: 99.99 },
    description: 'Monthly subscription invoice',
    idempotencyKey: `invoice-${Date.now()}`,
  });
  console.log(`Created intent: ${result.intentId}`);

  // Check for pending approvals
  const approvals = await intendedops.listApprovals();
  for (const approval of approvals.items) {
    console.log(`Pending approval: ${approval.id}`);
  }
}

main().catch(console.error);

Python

Full client implementation:

import os
import requests
from typing import Optional, Dict, Any, List
from dataclasses import dataclass
from datetime import datetime


@dataclass
class Intent:
    id: str
    type: str
    domain: str
    status: str
    params: Dict[str, Any]
    created_at: str


class IntendedOpsClient:
    def __init__(self, api_key: str, base_url: str = 'https://api.intendedops.dev'):
        self.api_key = api_key
        self.base_url = base_url
        self.session = requests.Session()
        self.session.headers.update({
            'x-api-key': api_key,
            'Content-Type': 'application/json',
        })

    def _request(self, method: str, path: str, **kwargs) -> Any:
        url = f'{self.base_url}{path}'
        response = self.session.request(method, url, **kwargs)
        result = response.json()

        if not response.ok:
            error = result.get('error', {})
            raise Exception(f"{error.get('code')}: {error.get('message')}")

        return result.get('data')

    # Stats
    def get_stats(self) -> Dict[str, Any]:
        return self._request('GET', '/v1/stats')

    # Intents
    def list_intents(
        self,
        page: int = 1,
        page_size: int = 20,
        status: Optional[str] = None,
        agent_id: Optional[str] = None,
    ) -> Dict[str, Any]:
        params = {'page': page, 'pageSize': page_size}
        if status:
            params['status'] = status
        if agent_id:
            params['agentId'] = agent_id
        return self._request('GET', '/v1/intents', params=params)

    def create_intent(
        self,
        intent_type: str,
        domain: str,
        agent_id: str,
        params: Optional[Dict[str, Any]] = None,
        description: Optional[str] = None,
        justification: Optional[str] = None,
        idempotency_key: Optional[str] = None,
    ) -> Dict[str, Any]:
        body = {
            'type': intent_type,
            'domain': domain,
            'agentId': agent_id,
        }
        if params:
            body['params'] = params
        if description:
            body['description'] = description
        if justification:
            body['justification'] = justification
        if idempotency_key:
            body['idempotencyKey'] = idempotency_key

        return self._request('POST', '/v1/intents', json=body)

    def get_intent(self, intent_id: str) -> Dict[str, Any]:
        return self._request('GET', f'/v1/intents/{intent_id}')

    # Agents
    def list_agents(self) -> Dict[str, Any]:
        return self._request('GET', '/v1/agents')

    def register_agent(
        self,
        name: str,
        description: Optional[str] = None,
        domains: Optional[List[str]] = None,
        intent_types: Optional[List[str]] = None,
    ) -> Dict[str, Any]:
        body = {'name': name}
        if description:
            body['description'] = description
        if domains:
            body['domains'] = domains
        if intent_types:
            body['intentTypes'] = intent_types

        return self._request('POST', '/v1/agents', json=body)

    def get_agent_metrics(self, agent_id: str) -> Dict[str, Any]:
        return self._request('GET', f'/v1/agents/{agent_id}/metrics')

    # Approvals
    def list_approvals(self) -> Dict[str, Any]:
        return self._request('GET', '/v1/approvals')

    def approve_intent(self, approval_id: str) -> Dict[str, Any]:
        return self._request('POST', f'/v1/approvals/{approval_id}/approve')

    def reject_intent(
        self,
        approval_id: str,
        reason: Optional[str] = None,
    ) -> Dict[str, Any]:
        body = {}
        if reason:
            body['reason'] = reason
        return self._request('POST', f'/v1/approvals/{approval_id}/reject', json=body)

    # Audit
    def query_audit(
        self,
        event_type: Optional[str] = None,
        from_date: Optional[str] = None,
        to_date: Optional[str] = None,
    ) -> Dict[str, Any]:
        params = {}
        if event_type:
            params['eventType'] = event_type
        if from_date:
            params['fromDate'] = from_date
        if to_date:
            params['toDate'] = to_date
        return self._request('GET', '/v1/audit', params=params)

    def verify_audit_chain(self) -> Dict[str, Any]:
        return self._request('GET', '/v1/audit/verify')


# Usage
if __name__ == '__main__':
    client = IntendedOpsClient(os.environ['INTENDEDOPS_API_KEY'])

    # Get stats
    stats = client.get_stats()
    print(f"Pending approvals: {stats['pendingApprovals']}")

    # Submit an intent
    result = client.create_intent(
        intent_type='billing.send_invoice',
        domain='billing',
        agent_id='billing-agent',
        params={'customerId': 'cust_123', 'amount': 99.99},
        description='Monthly subscription invoice',
        idempotency_key=f'invoice-{datetime.now().timestamp()}',
    )
    print(f"Created intent: {result['intentId']}")

    # Check for pending approvals
    approvals = client.list_approvals()
    for approval in approvals['items']:
        print(f"Pending approval: {approval['id']}")

Next Steps