- Created examples-javascript.md (20KB, 638 lines) - Authentication, Documents, Governance, Audit examples - Node.js and browser implementations - Complete TractatusClient class - Error handling and retry logic - Rate limiting documentation - Created examples-python.md (30KB, 983 lines) - Authentication, Documents, Governance, Audit examples - Type hints and data classes - Complete TractatusClient class - Error handling decorators and retry logic - Rate limiting documentation - Updated api-reference.html with code examples links - All examples deployed to production Task 12 (API Documentation) - Code examples complete
783 lines
20 KiB
Markdown
783 lines
20 KiB
Markdown
# JavaScript API Examples
|
||
|
||
Complete examples for integrating with the Tractatus Framework API using JavaScript (Node.js and Browser).
|
||
|
||
## Table of Contents
|
||
|
||
- [Authentication](#authentication)
|
||
- [Documents](#documents)
|
||
- [Governance Services](#governance-services)
|
||
- [Audit Logs](#audit-logs)
|
||
- [Error Handling](#error-handling)
|
||
|
||
---
|
||
|
||
## Authentication
|
||
|
||
### Login and Store Token (Node.js)
|
||
|
||
```javascript
|
||
const axios = require('axios');
|
||
|
||
const API_BASE = 'https://agenticgovernance.digital/api';
|
||
// For local development: const API_BASE = 'http://localhost:9000/api';
|
||
|
||
async function login(email, password) {
|
||
try {
|
||
const response = await axios.post(`${API_BASE}/auth/login`, {
|
||
email,
|
||
password
|
||
});
|
||
|
||
const { token, user } = response.data;
|
||
|
||
// Store token for subsequent requests
|
||
process.env.TRACTATUS_TOKEN = token;
|
||
|
||
console.log('Login successful:', user);
|
||
return { token, user };
|
||
} catch (error) {
|
||
if (error.response?.status === 429) {
|
||
console.error('Too many login attempts. Please wait 15 minutes.');
|
||
} else if (error.response?.status === 401) {
|
||
console.error('Invalid credentials');
|
||
} else {
|
||
console.error('Login failed:', error.message);
|
||
}
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// Usage
|
||
login('admin@tractatus.local', 'your_password')
|
||
.then(({ token }) => {
|
||
console.log('Token:', token);
|
||
});
|
||
```
|
||
|
||
### Login and Store Token (Browser)
|
||
|
||
```javascript
|
||
async function login(email, password) {
|
||
try {
|
||
const response = await fetch('https://agenticgovernance.digital/api/auth/login', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: JSON.stringify({ email, password })
|
||
});
|
||
|
||
if (!response.ok) {
|
||
if (response.status === 429) {
|
||
throw new Error('Too many login attempts. Please wait 15 minutes.');
|
||
}
|
||
throw new Error('Login failed');
|
||
}
|
||
|
||
const { token, user } = await response.json();
|
||
|
||
// Store token in localStorage
|
||
localStorage.setItem('tractatus_token', token);
|
||
localStorage.setItem('tractatus_user', JSON.stringify(user));
|
||
|
||
console.log('Login successful:', user);
|
||
return { token, user };
|
||
} catch (error) {
|
||
console.error('Login error:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// Usage
|
||
login('admin@tractatus.local', 'your_password')
|
||
.then(({ user }) => {
|
||
console.log('Logged in as:', user.email);
|
||
});
|
||
```
|
||
|
||
### Making Authenticated Requests (Node.js)
|
||
|
||
```javascript
|
||
const axios = require('axios');
|
||
|
||
// Create axios instance with authentication
|
||
function createAuthClient(token) {
|
||
return axios.create({
|
||
baseURL: 'https://agenticgovernance.digital/api',
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`,
|
||
'Content-Type': 'application/json'
|
||
}
|
||
});
|
||
}
|
||
|
||
// Usage
|
||
const token = process.env.TRACTATUS_TOKEN;
|
||
const client = createAuthClient(token);
|
||
|
||
// Now all requests include authentication
|
||
client.get('/governance/status')
|
||
.then(response => console.log(response.data));
|
||
```
|
||
|
||
### Making Authenticated Requests (Browser)
|
||
|
||
```javascript
|
||
async function authenticatedFetch(endpoint, options = {}) {
|
||
const token = localStorage.getItem('tractatus_token');
|
||
|
||
if (!token) {
|
||
throw new Error('Not authenticated. Please login first.');
|
||
}
|
||
|
||
const defaultOptions = {
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`,
|
||
'Content-Type': 'application/json',
|
||
...options.headers
|
||
}
|
||
};
|
||
|
||
const response = await fetch(`https://agenticgovernance.digital/api${endpoint}`, {
|
||
...options,
|
||
...defaultOptions
|
||
});
|
||
|
||
if (response.status === 401) {
|
||
// Token expired or invalid
|
||
localStorage.removeItem('tractatus_token');
|
||
localStorage.removeItem('tractatus_user');
|
||
throw new Error('Session expired. Please login again.');
|
||
}
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`API error: ${response.statusText}`);
|
||
}
|
||
|
||
return response.json();
|
||
}
|
||
|
||
// Usage
|
||
authenticatedFetch('/governance/status')
|
||
.then(data => console.log(data));
|
||
```
|
||
|
||
---
|
||
|
||
## Documents
|
||
|
||
### List All Documents
|
||
|
||
```javascript
|
||
async function listDocuments(options = {}) {
|
||
const { page = 1, limit = 50, quadrant } = options;
|
||
|
||
const params = new URLSearchParams({
|
||
page: page.toString(),
|
||
limit: limit.toString()
|
||
});
|
||
|
||
if (quadrant) {
|
||
params.append('quadrant', quadrant);
|
||
}
|
||
|
||
const response = await fetch(
|
||
`https://agenticgovernance.digital/api/documents?${params}`
|
||
);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Failed to fetch documents');
|
||
}
|
||
|
||
return response.json();
|
||
}
|
||
|
||
// Usage
|
||
listDocuments({ page: 1, limit: 10, quadrant: 'STRATEGIC' })
|
||
.then(data => {
|
||
console.log(`Found ${data.pagination.total} documents`);
|
||
data.documents.forEach(doc => {
|
||
console.log(`- ${doc.title} (${doc.quadrant})`);
|
||
});
|
||
});
|
||
```
|
||
|
||
### Get Single Document
|
||
|
||
```javascript
|
||
async function getDocument(identifier) {
|
||
const response = await fetch(
|
||
`https://agenticgovernance.digital/api/documents/${identifier}`
|
||
);
|
||
|
||
if (response.status === 404) {
|
||
throw new Error('Document not found');
|
||
}
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Failed to fetch document');
|
||
}
|
||
|
||
return response.json();
|
||
}
|
||
|
||
// Usage (by slug)
|
||
getDocument('introduction-to-tractatus')
|
||
.then(data => {
|
||
console.log('Title:', data.document.title);
|
||
console.log('Quadrant:', data.document.quadrant);
|
||
console.log('Content:', data.document.content_html.substring(0, 100) + '...');
|
||
});
|
||
|
||
// Usage (by ID)
|
||
getDocument('672f821b6e820c0c7a0e0d55')
|
||
.then(data => console.log(data.document));
|
||
```
|
||
|
||
### Search Documents
|
||
|
||
```javascript
|
||
async function searchDocuments(query) {
|
||
const params = new URLSearchParams({ q: query });
|
||
|
||
const response = await fetch(
|
||
`https://agenticgovernance.digital/api/documents/search?${params}`
|
||
);
|
||
|
||
if (!response.ok) {
|
||
throw new Error('Search failed');
|
||
}
|
||
|
||
return response.json();
|
||
}
|
||
|
||
// Usage
|
||
searchDocuments('boundary enforcement')
|
||
.then(data => {
|
||
console.log(`Found ${data.count} results`);
|
||
data.results.forEach(result => {
|
||
console.log(`- ${result.title} (score: ${result.score})`);
|
||
});
|
||
});
|
||
```
|
||
|
||
### Create Document (Admin Only)
|
||
|
||
```javascript
|
||
async function createDocument(token, documentData) {
|
||
const client = createAuthClient(token);
|
||
|
||
try {
|
||
const response = await client.post('/documents', {
|
||
title: documentData.title,
|
||
slug: documentData.slug,
|
||
quadrant: documentData.quadrant,
|
||
content_markdown: documentData.content,
|
||
status: documentData.status || 'published'
|
||
});
|
||
|
||
console.log('Document created:', response.data.document._id);
|
||
return response.data.document;
|
||
} catch (error) {
|
||
if (error.response?.status === 403) {
|
||
console.error('Admin role required');
|
||
} else if (error.response?.status === 409) {
|
||
console.error('Slug already exists');
|
||
}
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// Usage
|
||
const newDocument = {
|
||
title: 'Advanced Boundary Enforcement Patterns',
|
||
slug: 'advanced-boundary-enforcement',
|
||
quadrant: 'OPERATIONAL',
|
||
content: '# Advanced Patterns\n\nThis document explores...',
|
||
status: 'published'
|
||
};
|
||
|
||
createDocument(process.env.TRACTATUS_TOKEN, newDocument);
|
||
```
|
||
|
||
---
|
||
|
||
## Governance Services
|
||
|
||
### InstructionPersistenceClassifier
|
||
|
||
```javascript
|
||
async function classifyInstruction(token, text, context = {}) {
|
||
const client = createAuthClient(token);
|
||
|
||
const response = await client.post('/governance/classify', {
|
||
text,
|
||
context: {
|
||
source: context.source || 'user',
|
||
session_id: context.session_id || 'default',
|
||
...context
|
||
}
|
||
});
|
||
|
||
return response.data.classification;
|
||
}
|
||
|
||
// Usage
|
||
classifyInstruction(
|
||
process.env.TRACTATUS_TOKEN,
|
||
'Always use MongoDB on port 27027',
|
||
{ source: 'user', session_id: 'sess_123' }
|
||
).then(classification => {
|
||
console.log('Quadrant:', classification.quadrant);
|
||
console.log('Persistence:', classification.persistence);
|
||
console.log('Temporal Scope:', classification.temporal_scope);
|
||
console.log('Confidence:', classification.confidence);
|
||
console.log('Reasoning:', classification.reasoning);
|
||
});
|
||
```
|
||
|
||
### CrossReferenceValidator
|
||
|
||
```javascript
|
||
async function validateAction(token, action, context = {}) {
|
||
const client = createAuthClient(token);
|
||
|
||
const response = await client.post('/governance/validate', {
|
||
action,
|
||
context: {
|
||
messages: context.messages || [],
|
||
session_id: context.session_id || 'default',
|
||
...context
|
||
}
|
||
});
|
||
|
||
return response.data.validation;
|
||
}
|
||
|
||
// Usage
|
||
const action = {
|
||
type: 'database_config',
|
||
target: 'MongoDB',
|
||
parameters: { port: 27017 }
|
||
};
|
||
|
||
validateAction(process.env.TRACTATUS_TOKEN, action)
|
||
.then(validation => {
|
||
if (validation.status === 'REJECTED') {
|
||
console.error('❌ Action rejected');
|
||
console.error('Reason:', validation.reason);
|
||
validation.conflicts.forEach(conflict => {
|
||
console.error(` Conflicts with: ${conflict.text} (${conflict.instruction_id})`);
|
||
});
|
||
console.log('Recommendation:', validation.recommendation);
|
||
} else if (validation.status === 'APPROVED') {
|
||
console.log('✅ Action approved');
|
||
}
|
||
});
|
||
```
|
||
|
||
### BoundaryEnforcer
|
||
|
||
```javascript
|
||
async function enforceBounda ry(token, action, context = {}) {
|
||
const client = createAuthClient(token);
|
||
|
||
const response = await client.post('/governance/enforce', {
|
||
action,
|
||
context
|
||
});
|
||
|
||
return response.data.enforcement;
|
||
}
|
||
|
||
// Usage
|
||
const action = {
|
||
type: 'policy_change',
|
||
description: 'Update privacy policy to enable more tracking',
|
||
impact: 'user_privacy'
|
||
};
|
||
|
||
enforceBoundary(process.env.TRACTATUS_TOKEN, action)
|
||
.then(enforcement => {
|
||
if (enforcement.decision === 'BLOCK') {
|
||
console.error('🚫 Action blocked - crosses values boundary');
|
||
console.error('Boundary:', enforcement.boundary_crossed);
|
||
console.error('Reason:', enforcement.reason);
|
||
console.log('\nAlternatives:');
|
||
enforcement.alternatives.forEach((alt, i) => {
|
||
console.log(`${i + 1}. ${alt}`);
|
||
});
|
||
} else {
|
||
console.log('✅ Action allowed');
|
||
}
|
||
});
|
||
```
|
||
|
||
### ContextPressureMonitor
|
||
|
||
```javascript
|
||
async function analyzePressure(token, context) {
|
||
const client = createAuthClient(token);
|
||
|
||
const response = await client.post('/governance/pressure', {
|
||
context: {
|
||
tokenUsage: context.tokenUsage || 50000,
|
||
tokenBudget: context.tokenBudget || 200000,
|
||
messageCount: context.messageCount || 20,
|
||
errorCount: context.errorCount || 0,
|
||
complexOperations: context.complexOperations || 0,
|
||
sessionDuration: context.sessionDuration || 1800
|
||
}
|
||
});
|
||
|
||
return response.data.pressure;
|
||
}
|
||
|
||
// Usage
|
||
analyzePressure(process.env.TRACTATUS_TOKEN, {
|
||
tokenUsage: 120000,
|
||
tokenBudget: 200000,
|
||
messageCount: 45,
|
||
errorCount: 3,
|
||
complexOperations: 8,
|
||
sessionDuration: 3600
|
||
}).then(pressure => {
|
||
console.log('Pressure Level:', pressure.level);
|
||
console.log('Score:', pressure.score + '%');
|
||
console.log('\nFactors:');
|
||
Object.entries(pressure.factors).forEach(([factor, data]) => {
|
||
console.log(` ${factor}: ${data.value} (${data.status})`);
|
||
});
|
||
console.log('\nRecommendation:', pressure.recommendation);
|
||
|
||
if (pressure.triggerHandoff) {
|
||
console.warn('⚠️ Session handoff recommended');
|
||
}
|
||
});
|
||
```
|
||
|
||
### MetacognitiveVerifier
|
||
|
||
```javascript
|
||
async function verifyAction(token, action, reasoning, context = {}) {
|
||
const client = createAuthClient(token);
|
||
|
||
const response = await client.post('/governance/verify', {
|
||
action,
|
||
reasoning,
|
||
context
|
||
});
|
||
|
||
return response.data.verification;
|
||
}
|
||
|
||
// Usage
|
||
const action = {
|
||
type: 'refactor',
|
||
scope: 'Refactor 47 files across 5 system areas',
|
||
complexity: 'high'
|
||
};
|
||
|
||
const reasoning = {
|
||
intent: 'Improve code organization',
|
||
approach: 'Extract shared utilities, consolidate duplicates',
|
||
risks: 'Potential breaking changes'
|
||
};
|
||
|
||
const context = {
|
||
requested: 'Refactor authentication module',
|
||
original_scope: 'single module'
|
||
};
|
||
|
||
verifyAction(process.env.TRACTATUS_TOKEN, action, reasoning, context)
|
||
.then(verification => {
|
||
console.log('Decision:', verification.decision);
|
||
console.log('Confidence:', verification.confidence);
|
||
|
||
if (verification.concerns.length > 0) {
|
||
console.log('\n⚠️ Concerns:');
|
||
verification.concerns.forEach(concern => {
|
||
console.log(` [${concern.severity}] ${concern.type}: ${concern.detail}`);
|
||
});
|
||
}
|
||
|
||
if (verification.scopeCreep) {
|
||
console.warn('\n🔴 Scope creep detected');
|
||
}
|
||
|
||
console.log('\nCriteria Scores:');
|
||
Object.entries(verification.criteria).forEach(([criterion, score]) => {
|
||
console.log(` ${criterion}: ${(score * 100).toFixed(0)}%`);
|
||
});
|
||
|
||
if (verification.alternatives.length > 0) {
|
||
console.log('\nAlternatives:');
|
||
verification.alternatives.forEach((alt, i) => {
|
||
console.log(`${i + 1}. ${alt}`);
|
||
});
|
||
}
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## Audit Logs
|
||
|
||
### Get Audit Logs with Filtering
|
||
|
||
```javascript
|
||
async function getAuditLogs(token, options = {}) {
|
||
const client = createAuthClient(token);
|
||
|
||
const params = {
|
||
page: options.page || 1,
|
||
limit: options.limit || 50
|
||
};
|
||
|
||
if (options.action) params.action = options.action;
|
||
if (options.userId) params.userId = options.userId;
|
||
if (options.startDate) params.startDate = options.startDate;
|
||
if (options.endDate) params.endDate = options.endDate;
|
||
|
||
const response = await client.get('/audit/audit-logs', { params });
|
||
return response.data;
|
||
}
|
||
|
||
// Usage
|
||
getAuditLogs(process.env.TRACTATUS_TOKEN, {
|
||
page: 1,
|
||
limit: 20,
|
||
action: 'validate_action',
|
||
startDate: '2025-10-01T00:00:00Z'
|
||
}).then(data => {
|
||
console.log(`Total logs: ${data.total}`);
|
||
data.logs.forEach(log => {
|
||
console.log(`[${log.timestamp}] ${log.service}: ${log.action} - ${log.status}`);
|
||
if (log.details) {
|
||
console.log(' Details:', JSON.stringify(log.details, null, 2));
|
||
}
|
||
});
|
||
});
|
||
```
|
||
|
||
### Get Audit Analytics
|
||
|
||
```javascript
|
||
async function getAuditAnalytics(token, startDate, endDate) {
|
||
const client = createAuthClient(token);
|
||
|
||
const params = {};
|
||
if (startDate) params.startDate = startDate;
|
||
if (endDate) params.endDate = endDate;
|
||
|
||
const response = await client.get('/audit/audit-analytics', { params });
|
||
return response.data.analytics;
|
||
}
|
||
|
||
// Usage
|
||
getAuditAnalytics(
|
||
process.env.TRACTATUS_TOKEN,
|
||
'2025-10-01T00:00:00Z',
|
||
'2025-10-12T23:59:59Z'
|
||
).then(analytics => {
|
||
console.log('Total Events:', analytics.total_events);
|
||
console.log('\nBreakdown by Service:');
|
||
Object.entries(analytics.by_service).forEach(([service, count]) => {
|
||
console.log(` ${service}: ${count}`);
|
||
});
|
||
console.log('\nBreakdown by Status:');
|
||
Object.entries(analytics.by_status).forEach(([status, count]) => {
|
||
console.log(` ${status}: ${count}`);
|
||
});
|
||
console.log('\nRejection Rate:', analytics.rejection_rate + '%');
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## Error Handling
|
||
|
||
### Comprehensive Error Handler
|
||
|
||
```javascript
|
||
async function handleApiRequest(requestFn) {
|
||
try {
|
||
return await requestFn();
|
||
} catch (error) {
|
||
// Axios error structure
|
||
if (error.response) {
|
||
const { status, data } = error.response;
|
||
|
||
switch (status) {
|
||
case 400:
|
||
console.error('Bad Request:', data.message);
|
||
console.error('Details:', data.details);
|
||
break;
|
||
case 401:
|
||
console.error('Unauthorized: Please login');
|
||
// Clear stored token
|
||
localStorage.removeItem('tractatus_token');
|
||
break;
|
||
case 403:
|
||
console.error('Forbidden: Insufficient permissions');
|
||
console.error('Required role:', data.required_role || 'admin');
|
||
break;
|
||
case 404:
|
||
console.error('Not Found:', data.message);
|
||
break;
|
||
case 409:
|
||
console.error('Conflict:', data.message);
|
||
console.error('Conflicting resource:', data.conflict);
|
||
break;
|
||
case 429:
|
||
console.error('Rate Limit Exceeded:', data.message);
|
||
console.error('Retry after:', error.response.headers['retry-after']);
|
||
break;
|
||
case 500:
|
||
console.error('Internal Server Error');
|
||
console.error('Error ID:', data.errorId);
|
||
break;
|
||
default:
|
||
console.error('API Error:', status, data.message);
|
||
}
|
||
} else if (error.request) {
|
||
console.error('Network Error: No response received');
|
||
console.error('Check your internet connection');
|
||
} else {
|
||
console.error('Error:', error.message);
|
||
}
|
||
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// Usage
|
||
handleApiRequest(async () => {
|
||
return await classifyInstruction(token, 'Test instruction');
|
||
})
|
||
.then(result => console.log('Success:', result))
|
||
.catch(error => console.log('Handled error'));
|
||
```
|
||
|
||
### Retry Logic with Exponential Backoff
|
||
|
||
```javascript
|
||
async function retryWithBackoff(fn, maxRetries = 3, baseDelay = 1000) {
|
||
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
||
try {
|
||
return await fn();
|
||
} catch (error) {
|
||
if (attempt === maxRetries) {
|
||
throw error;
|
||
}
|
||
|
||
// Don't retry on client errors (4xx except 429)
|
||
if (error.response?.status >= 400 &&
|
||
error.response?.status < 500 &&
|
||
error.response?.status !== 429) {
|
||
throw error;
|
||
}
|
||
|
||
const delay = baseDelay * Math.pow(2, attempt - 1);
|
||
console.log(`Attempt ${attempt} failed. Retrying in ${delay}ms...`);
|
||
await new Promise(resolve => setTimeout(resolve, delay));
|
||
}
|
||
}
|
||
}
|
||
|
||
// Usage
|
||
retryWithBackoff(async () => {
|
||
return await getDocument('some-slug');
|
||
}, 3, 1000)
|
||
.then(doc => console.log('Document:', doc))
|
||
.catch(error => console.error('All retries failed:', error));
|
||
```
|
||
|
||
---
|
||
|
||
## Complete Example: Full Integration
|
||
|
||
```javascript
|
||
const axios = require('axios');
|
||
|
||
class TractatusClient {
|
||
constructor(baseURL = 'https://agenticgovernance.digital/api') {
|
||
this.baseURL = baseURL;
|
||
this.token = null;
|
||
this.client = axios.create({ baseURL });
|
||
}
|
||
|
||
async login(email, password) {
|
||
const response = await this.client.post('/auth/login', { email, password });
|
||
this.token = response.data.token;
|
||
this.client.defaults.headers.common['Authorization'] = `Bearer ${this.token}`;
|
||
return response.data;
|
||
}
|
||
|
||
async classifyInstruction(text, context = {}) {
|
||
const response = await this.client.post('/governance/classify', { text, context });
|
||
return response.data.classification;
|
||
}
|
||
|
||
async validateAction(action, context = {}) {
|
||
const response = await this.client.post('/governance/validate', { action, context });
|
||
return response.data.validation;
|
||
}
|
||
|
||
async getDocuments(options = {}) {
|
||
const response = await this.client.get('/documents', { params: options });
|
||
return response.data;
|
||
}
|
||
}
|
||
|
||
// Usage
|
||
const tractatus = new TractatusClient();
|
||
|
||
async function main() {
|
||
await tractatus.login('admin@tractatus.local', 'password');
|
||
|
||
const classification = await tractatus.classifyInstruction(
|
||
'Always use MongoDB on port 27027'
|
||
);
|
||
console.log('Classification:', classification);
|
||
|
||
const docs = await tractatus.getDocuments({ limit: 5 });
|
||
console.log(`Found ${docs.total} documents`);
|
||
}
|
||
|
||
main().catch(console.error);
|
||
```
|
||
|
||
---
|
||
|
||
## Rate Limiting
|
||
|
||
The Tractatus API implements rate limiting:
|
||
|
||
- **Login endpoint**: 5 attempts per 15 minutes per IP
|
||
- **General API**: 100 requests per 15 minutes per IP
|
||
|
||
Handle rate limiting:
|
||
|
||
```javascript
|
||
async function apiCallWithRateLimit(fn) {
|
||
try {
|
||
return await fn();
|
||
} catch (error) {
|
||
if (error.response?.status === 429) {
|
||
const retryAfter = error.response.headers['retry-after'];
|
||
console.warn(`Rate limited. Retry after ${retryAfter} seconds`);
|
||
|
||
// Wait and retry
|
||
await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
|
||
return await fn();
|
||
}
|
||
throw error;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
For more information, see the [API Reference](https://agenticgovernance.digital/api-reference.html) and [OpenAPI Specification](https://agenticgovernance.digital/docs/api/openapi.yaml).
|