Skip to main content

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 TypeRate Limit
Test Results100 requests/min per repo
Repository Operations60 requests/min
Analytics100 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 name
  • commit (required): Commit SHA
  • framework (required): Test framework (jest, pytest, nunit, etc.)
  • pr_number (optional): Pull request number
  • workflow_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 completed
  • workflow_job - GitHub Actions workflow status
  • installation - App installed/uninstalled
  • installation_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?