Documentation
Python SDK
Official SDK for Python 3.8+
Installation
pip
pip install qodryxPoetry
poetry add qodryxWith async support
pip install qodryx[async]Requirements
- Python 3.8 or higher
- httpx (for HTTP requests)
- pydantic (for data validation)
Quick Start
from qodryx import Qodryx
import os
# Initialize the client
client = Qodryx(api_key=os.environ['QODRYX_API_KEY'])
# List projects
projects = client.projects.list()
for project in projects.data:
print(f"{project.name}: {project.id}")
# Trigger a security scan
scan = client.security.scan(
project_id='proj_abc123',
types=['sast', 'secrets', 'dependencies']
)
print(f"Scan started: {scan.id}")Async Usage
import asyncio
from qodryx import AsyncQodryx
import os
async def main():
async with AsyncQodryx(api_key=os.environ['QODRYX_API_KEY']) as client:
# All methods are async
projects = await client.projects.list()
# Run multiple operations concurrently
scans = await asyncio.gather(*[
client.security.scan(project_id=p.id)
for p in projects.data
])
for scan in scans:
print(f"Started scan: {scan.id}")
asyncio.run(main())Configuration
from qodryx import Qodryx
client = Qodryx(
# Required: Your API key
api_key=os.environ['QODRYX_API_KEY'],
# Optional: Base URL (default: https://api.qodryx.com)
base_url='https://api.qodryx.com',
# Optional: Request timeout in seconds (default: 30)
timeout=60,
# Optional: Retry configuration
max_retries=3,
retry_on_rate_limit=True,
# Optional: Custom headers
headers={'X-Custom-Header': 'value'},
)Type Hints
The SDK provides full type hints for better IDE support:
from qodryx import Qodryx
from qodryx.types import Project, SecurityScan, Workflow
client = Qodryx(api_key=os.environ['QODRYX_API_KEY'])
# IDE autocomplete and type checking work out of the box
project: Project = client.projects.get('proj_abc123')
scan: SecurityScan = client.security.scan(project_id=project.id)
workflow: Workflow = client.workflows.create(
project_id=project.id,
prompt='Add user authentication'
)API Reference
Projects
# List all projects
projects = client.projects.list(page=1, per_page=20)
# Get a specific project
project = client.projects.get('proj_abc123')
# Create a new project
new_project = client.projects.create(
name='my-app',
repository='github.com/user/my-app',
description='My awesome app'
)
# Update a project
updated = client.projects.update('proj_abc123', name='new-name')
# Delete a project
client.projects.delete('proj_abc123')Workflows
# Create and trigger a workflow
workflow = client.workflows.create(
project_id='proj_abc123',
prompt='Add OAuth authentication with Google',
config={
'ai_provider': 'anthropic',
'auto_deploy': False
}
)
# List workflows
workflows = client.workflows.list(
project_id='proj_abc123',
status='running'
)
# Get workflow status
status = client.workflows.get('wf_xyz789')
# Get workflow logs
logs = client.workflows.logs('wf_xyz789', stage='build')
# Cancel a workflow
client.workflows.cancel('wf_xyz789')
# Wait for workflow completion
result = client.workflows.wait_for_completion(
'wf_xyz789',
timeout=300, # 5 minutes
poll_interval=5 # 5 seconds
)Security
# Trigger a security scan
scan = client.security.scan(
project_id='proj_abc123',
types=['sast', 'secrets', 'dependencies'],
branch='main'
)
# Get scan results
results = client.security.results('scan_abc123')
# List all findings
findings = client.security.findings(
project_id='proj_abc123',
severity=['critical', 'high'],
status='open'
)
# Get specific finding
finding = client.security.finding('finding_abc123')
# Auto-remediate a finding
remediation = client.security.remediate('finding_abc123')
# Ignore a finding
client.security.ignore(
'finding_abc123',
reason='False positive - test file'
)Deployments
# Trigger a deployment
deployment = client.deployments.create(
project_id='proj_abc123',
environment='production',
provider='vercel',
branch='main'
)
# Get deployment status
status = client.deployments.get('deploy_abc123')
# List deployments
deployments = client.deployments.list(
project_id='proj_abc123',
environment='production'
)
# Rollback a deployment
client.deployments.rollback('deploy_abc123')
# Get deployment logs
logs = client.deployments.logs('deploy_abc123')Tests
# Generate tests for a file
tests = client.tests.generate(
project_id='proj_abc123',
file_path='src/auth/login.py',
framework='pytest'
)
# Run tests
results = client.tests.run(
project_id='proj_abc123',
coverage=True
)
# Get test coverage
coverage = client.tests.coverage('proj_abc123')Error Handling
from qodryx import Qodryx
from qodryx.exceptions import (
QodrxyError,
AuthenticationError,
RateLimitError,
NotFoundError,
ValidationError
)
client = Qodryx(api_key=os.environ['QODRYX_API_KEY'])
try:
scan = client.security.scan(project_id='proj_abc123')
except AuthenticationError:
print('Invalid API key')
except RateLimitError as e:
print(f'Rate limit exceeded, retry after {e.retry_after} seconds')
except NotFoundError:
print('Project not found')
except ValidationError as e:
print(f'Validation error: {e.errors}')
except QodrxyError as e:
print(f'API error: {e.code} - {e.message}')Context Manager
Use the context manager for automatic resource cleanup:
from qodryx import Qodryx
with Qodryx(api_key=os.environ['QODRYX_API_KEY']) as client:
projects = client.projects.list()
# Connection is automatically closed after the block
# Async context manager
async with AsyncQodryx(api_key=os.environ['QODRYX_API_KEY']) as client:
projects = await client.projects.list()Pagination
# Manual pagination
page = 1
has_more = True
while has_more:
response = client.projects.list(page=page, per_page=50)
for project in response.data:
print(project.name)
has_more = page < response.meta.total_pages
page += 1
# Auto-pagination iterator
for project in client.projects.list_all():
print(project.name)
# Async auto-pagination
async for project in client.projects.list_all():
print(project.name)Webhooks
Verify and parse webhook payloads:
from qodryx.webhooks import verify_webhook, parse_event
from flask import Flask, request
app = Flask(__name__)
@app.route('/webhooks/qodryx', methods=['POST'])
def handle_webhook():
signature = request.headers.get('X-Qodryx-Signature')
payload = request.data
# Verify signature
if not verify_webhook(payload, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
# Parse event
event = parse_event(request.json)
if event.type == 'workflow.completed':
print(f'Workflow completed: {event.data.workflow_id}')
elif event.type == 'security.finding.created':
print(f'New vulnerability: {event.data.severity}')
return 'OK', 200Django Integration
# settings.py
QODRYX_API_KEY = os.environ['QODRYX_API_KEY']
# views.py
from django.conf import settings
from qodryx import Qodryx
def security_check(request, project_id):
client = Qodryx(api_key=settings.QODRYX_API_KEY)
# Trigger scan
scan = client.security.scan(
project_id=project_id,
types=['sast', 'secrets']
)
return JsonResponse({'scan_id': scan.id})FastAPI Integration
from fastapi import FastAPI, Depends
from qodryx import AsyncQodryx
import os
app = FastAPI()
async def get_qodryx_client():
async with AsyncQodryx(api_key=os.environ['QODRYX_API_KEY']) as client:
yield client
@app.get('/projects')
async def list_projects(client: AsyncQodryx = Depends(get_qodryx_client)):
projects = await client.projects.list()
return {'projects': [p.dict() for p in projects.data]}
@app.post('/scan/{project_id}')
async def trigger_scan(
project_id: str,
client: AsyncQodryx = Depends(get_qodryx_client)
):
scan = await client.security.scan(project_id=project_id)
return {'scan_id': scan.id}Examples
Complete Example: Automated Security Check
#!/usr/bin/env python3
"""
Automated security check script for CI/CD pipelines
"""
import os
import sys
import time
from qodryx import Qodryx
from qodryx.exceptions import QodrxyError
def run_security_check(project_id: str) -> bool:
"""Run security scan and return True if no critical issues."""
client = Qodryx(api_key=os.environ['QODRYX_API_KEY'])
print('Starting security scan...')
# Trigger scan
scan = client.security.scan(
project_id=project_id,
types=['sast', 'secrets', 'dependencies']
)
print(f'Scan started: {scan.id}')
# Wait for completion
while True:
results = client.security.results(scan.id)
if results.status == 'completed':
print('Scan completed!')
# Check for critical findings
critical = [f for f in results.findings if f.severity == 'critical']
if critical:
print(f'❌ Found {len(critical)} critical vulnerabilities:')
for finding in critical:
print(f' - {finding.title} ({finding.file}:{finding.line})')
return False
print('✅ No critical vulnerabilities found')
return True
if results.status == 'failed':
print('❌ Scan failed')
return False
print(f'Scan in progress... ({results.status})')
time.sleep(5)
if __name__ == '__main__':
project_id = sys.argv[1] if len(sys.argv) > 1 else os.environ.get('QODRYX_PROJECT')
if not project_id:
print('Usage: python security_check.py <project_id>')
sys.exit(1)
try:
success = run_security_check(project_id)
sys.exit(0 if success else 1)
except QodrxyError as e:
print(f'Error: {e.message}')
sys.exit(1)