API Reference
Complete REST API reference for integrating with Unfold CI's cloud platform.
Base URL
https://api.unfoldci.com
Authentication
All API requests require authentication using Bearer tokens (API keys):
Authorization: Bearer unfold_sk_1234567890abcdef
Get your API key from the Dashboard Settings.
Rate Limits
| Endpoint Type | Rate Limit |
|---|---|
| Test Results | 100 requests/min per repo |
| Repository Operations | 60 requests/min |
| Analytics | 100 requests/min |
Rate limit headers included in all responses:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1699012800
Endpoints
Test Results
Submit Test Results
POST /api/test-results
Submit test execution results for flake detection and analysis.
Headers:
Authorization: Bearer {api_key}
Content-Type: multipart/form-data
Request (Multipart Form):
curl -X POST https://api.unfoldci.com/api/test-results \
-H "Authorization: Bearer unfold_sk_..." \
-F "file=@test-results.json" \
-F "repo=your-org/your-repo" \
-F "branch=main" \
-F "commit=abc123def456..." \
-F "framework=jest"
Parameters:
file(required): Test results file (JSON/XML)repo(required): Repository name (format:owner/repo)branch(required): Branch namecommit(required): Commit SHAframework(required): Test framework (jest,pytest,nunit, etc.)pr_number(optional): Pull request numberworkflow_run_id(optional): GitHub Actions run ID
Response: 201 Created
{
"success": true,
"tests_processed": 42,
"flaky_detected": 3,
"run_id": "uuid-123"
}
Repositories
List Monitored Repositories
GET /api/repos
Get all repositories monitored by your installation.
Response: 200 OK
{
"repos": [
{
"id": "uuid",
"name": "your-org/your-repo",
"github_url": "https://github.com/your-org/your-repo",
"health_score": 95,
"total_tests": 247,
"flaky_count": 3,
"installed_at": "2025-11-01T00:00:00Z",
"last_run": "2025-11-02T15:30:00Z"
}
]
}
Get Repository Details
GET /api/repos/{id}
Get detailed information and test list for a specific repository.
Response: 200 OK
{
"repo": {
"id": "uuid",
"name": "your-org/your-repo",
"health_score": 95
},
"tests": [
{
"id": "uuid",
"name": "test_user_login",
"file_path": "tests/auth/test_login.py",
"flake_score": 0.15,
"pass_rate": 0.85,
"total_runs": 100,
"framework": "pytest",
"root_cause_category": "timing_issue",
"root_cause_confidence": 0.87,
"last_run": "2025-11-02T15:30:00Z"
}
],
"summary": {
"total": 247,
"passing": 234,
"flaky": 10,
"failing": 3
}
}
Tests
Get Test Details
GET /api/tests/{id}
Get comprehensive details about a specific test.
Response: 200 OK
{
"id": "uuid",
"test_name": "test_user_login",
"file_path": "tests/auth/test_login.py",
"flake_score": 0.15,
"pass_rate": 0.85,
"total_runs": 100,
"failed_runs": 15,
"framework": "pytest",
"root_cause_category": "timing_issue",
"root_cause_explanation": "Test fails intermittently due to race condition in authentication flow. The login request occasionally times out when multiple requests are made simultaneously.",
"root_cause_confidence": 0.87,
"ai_model_used": "gpt-4o",
"last_analyzed_at": "2025-11-02T14:00:00Z",
"last_run_at": "2025-11-02T15:30:00Z",
"recent_runs": [
{
"timestamp": "2025-11-02T15:30:00Z",
"status": "passed",
"duration_ms": 450
},
{
"timestamp": "2025-11-02T14:00:00Z",
"status": "failed",
"duration_ms": 5000,
"error_message": "Timeout after 5000ms"
}
],
"repo": {
"id": "uuid",
"name": "your-org/your-repo"
}
}
Fix Attempts (AI-Generated PRs)
List Fix Attempts
GET /api/fix-attempts?limit=20&status=all
Get recent AI-generated fix attempts and PRs.
Query Parameters:
limit(optional): Number of results (default: 10, max: 100)status(optional): Filter by status (pending,merged,closed,all)repo_id(optional): Filter by repository ID
Response: 200 OK
{
"fix_attempts": [
{
"id": "uuid",
"test_id": "uuid",
"test_name": "test_user_login",
"test_file_path": "tests/auth/test_login.py",
"pr_url": "https://github.com/your-org/your-repo/pull/123",
"pr_number": 123,
"branch_name": "unfoldci/fix-test-user-login",
"root_cause_category": "timing_issue",
"ai_explanation": "Added retry logic with exponential backoff to handle race conditions in authentication flow.",
"status": "merged",
"merged": true,
"merged_at": "2025-11-02T16:00:00Z",
"confidence": 0.87,
"model_used": "gpt-4o",
"created_at": "2025-11-02T14:30:00Z",
"repo": {
"id": "uuid",
"name": "your-org/your-repo",
"github_url": "https://github.com/your-org/your-repo"
}
}
],
"summary": {
"total": 15,
"pending": 3,
"merged": 10,
"closed": 2
}
}
Get Fix Attempt Details
GET /api/fix-attempts/{id}
Get detailed information about a specific fix attempt.
Response: 200 OK
{
"id": "uuid",
"test_id": "uuid",
"pr_url": "https://github.com/your-org/your-repo/pull/123",
"pr_number": 123,
"status": "merged",
"confidence": 0.87,
"code_changes": {
"files_changed": 1,
"lines_added": 5,
"lines_removed": 2
},
"effectiveness": {
"runs_since_fix": 20,
"failures_before": 15,
"failures_after": 0,
"success_rate": 1.0
}
}
Analytics
Get Dashboard Analytics
GET /api/analytics/dashboard
Get aggregated analytics across all monitored repositories.
Response: 200 OK
{
"health_score": 95,
"total_tests": 1247,
"total_repos": 5,
"flaky_tests": 23,
"critical_flaky": 5,
"prs_created": 15,
"prs_merged": 10,
"time_saved_hours": 48,
"cost_savings_usd": 4800,
"trends": {
"health_score_change": 5,
"flaky_tests_change": -8
}
}
Get Repository Analytics
GET /api/analytics/repos/{id}?period=30d
Get detailed analytics for a specific repository.
Query Parameters:
period(optional): Time period (7d,30d,90d,all)
Response: 200 OK
{
"repo_id": "uuid",
"period": "30d",
"test_runs": 450,
"total_tests": 247,
"flaky_tests": 10,
"fixes_applied": 5,
"health_score_history": [
{"date": "2025-10-03", "score": 90},
{"date": "2025-10-10", "score": 92},
{"date": "2025-10-17", "score": 94},
{"date": "2025-10-24", "score": 95}
],
"root_causes": {
"timing_issue": 4,
"network_dependency": 3,
"race_condition": 2,
"environment_specific": 1
}
}
Error Responses
All endpoints may return these standard error responses:
400 Bad Request
Invalid request format or missing required parameters.
{
"error": "Bad Request",
"message": "Missing required field: repo",
"details": {
"field": "repo",
"expected": "string in format 'owner/repo'"
}
}
401 Unauthorized
Invalid or missing API key.
{
"error": "Unauthorized",
"message": "Invalid API key"
}
404 Not Found
Resource not found.
{
"error": "Not Found",
"message": "Test with ID 'uuid' not found"
}
429 Too Many Requests
Rate limit exceeded.
{
"error": "Too Many Requests",
"message": "Rate limit exceeded",
"retry_after": 60
}
500 Internal Server Error
Server-side error.
{
"error": "Internal Server Error",
"message": "An unexpected error occurred",
"request_id": "req_abc123"
}
Webhooks (Informational)
Unfold CI receives webhooks from GitHub automatically. You don't need to configure these manually.
Events We Handle
check_run- Test execution completedworkflow_job- GitHub Actions workflow statusinstallation- App installed/uninstalledinstallation_repositories- Repos added/removed
These webhooks are configured automatically when you install the GitHub App.
Code Examples
JavaScript/Node.js
const axios = require('axios');
const unfoldAPI = axios.create({
baseURL: 'https://api.unfoldci.com',
headers: {
'Authorization': `Bearer ${process.env.UNFOLD_API_KEY}`
}
});
// Get repositories
async function getRepositories() {
const { data } = await unfoldAPI.get('/api/repos');
return data.repos;
}
// Get fix attempts
async function getFixAttempts(limit = 20) {
const { data } = await unfoldAPI.get('/api/fix-attempts', {
params: { limit }
});
return data.fix_attempts;
}
// Submit test results
async function submitTestResults(file, metadata) {
const formData = new FormData();
formData.append('file', file);
formData.append('repo', metadata.repo);
formData.append('branch', metadata.branch);
formData.append('commit', metadata.commit);
formData.append('framework', metadata.framework);
const { data } = await unfoldAPI.post('/api/test-results', formData);
return data;
}
Python
import requests
class UnfoldClient:
def __init__(self, api_key):
self.base_url = 'https://api.unfoldci.com'
self.headers = {
'Authorization': f'Bearer {api_key}'
}
def get_repositories(self):
response = requests.get(
f'{self.base_url}/api/repos',
headers=self.headers
)
return response.json()['repos']
def get_test_details(self, test_id):
response = requests.get(
f'{self.base_url}/api/tests/{test_id}',
headers=self.headers
)
return response.json()
def submit_test_results(self, file_path, repo, branch, commit, framework):
with open(file_path, 'rb') as f:
files = {'file': f}
data = {
'repo': repo,
'branch': branch,
'commit': commit,
'framework': framework
}
response = requests.post(
f'{self.base_url}/api/test-results',
headers=self.headers,
files=files,
data=data
)
return response.json()
# Usage
client = UnfoldClient(api_key='unfold_sk_...')
repos = client.get_repositories()
cURL
# Get repositories
curl https://api.unfoldci.com/api/repos \
-H "Authorization: Bearer unfold_sk_..."
# Get test details
curl https://api.unfoldci.com/api/tests/uuid-123 \
-H "Authorization: Bearer unfold_sk_..."
# Submit test results
curl -X POST https://api.unfoldci.com/api/test-results \
-H "Authorization: Bearer unfold_sk_..." \
-F "file=@test-results.json" \
-F "repo=your-org/your-repo" \
-F "branch=main" \
-F "commit=abc123" \
-F "framework=jest"
# Get fix attempts
curl https://api.unfoldci.com/api/fix-attempts?limit=20 \
-H "Authorization: Bearer unfold_sk_..."
Support
Need help with the API?