REST API Reference
The Horizon Epoch REST API provides HTTP endpoints for all version control operations. This reference documents all available endpoints, their parameters, and response formats.
Overview
Base URL
http://localhost:8000/api/v1
The API prefix is configurable via the EPOCH_API_PREFIX environment variable (default: /api/v1).
Content Type
All requests and responses use JSON format:
Content-Type: application/json
Running the API Server
# Start the server
uv run uvicorn horizon_epoch.api:app --reload --host 0.0.0.0 --port 8000
# With debug mode (enables Swagger UI)
EPOCH_DEBUG=true uv run uvicorn horizon_epoch.api:app --reload
Authentication
Current Status: The API does not currently implement authentication. All endpoints are open.
When authentication is implemented, the following methods will be supported:
Planned: API Key Authentication
curl -H "Authorization: Bearer <api-key>" \
http://localhost:8000/api/v1/repositories
Planned: Basic Auth
curl -u username:password \
http://localhost:8000/api/v1/repositories
For production deployments, we recommend placing the API behind a reverse proxy (nginx, Traefik) that handles authentication.
Health Endpoints
Health check endpoints are available at the root level (not under /api/v1).
Health Check
Check if the API server is running.
GET /health
Response:
{
"status": "healthy",
"version": "0.1.0",
"connected": true
}
curl Example:
curl http://localhost:8000/health
Readiness Check
Check if the API is ready to handle requests.
GET /ready
Response:
{
"ready": true,
"metadata_connected": true,
"repository": "my-repo"
}
curl Example:
curl http://localhost:8000/ready
API Root
Get basic API information.
GET /
Response:
{
"name": "Horizon Epoch API",
"version": "0.1.0",
"api_prefix": "/api/v1",
"docs": "/docs"
}
curl Example:
curl http://localhost:8000/
Repository Endpoints
Manage repositories and their configuration.
Create Repository
Initialize a new Horizon Epoch repository.
POST /api/v1/repositories
Request Body:
{
"name": "my-data-repo",
"storage_configs": {
"primary": {
"backend": "postgresql",
"url": "postgresql://user:pass@localhost/mydb"
}
}
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Repository name (min 1 character) |
storage_configs | object | No | Optional storage backend configurations |
Response: 201 Created
{
"name": "my-data-repo",
"default_branch": "main",
"metadata_url": "postgresql://localhost/horizon_epoch"
}
curl Example:
curl -X POST http://localhost:8000/api/v1/repositories \
-H "Content-Type: application/json" \
-d '{"name": "my-data-repo"}'
Open Repository
Open an existing repository.
POST /api/v1/repositories/{name}/open
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
name | string | Repository name |
Response: 200 OK
{
"name": "my-data-repo",
"default_branch": "main",
"metadata_url": "postgresql://localhost/horizon_epoch"
}
curl Example:
curl -X POST http://localhost:8000/api/v1/repositories/my-data-repo/open
Get Repository Status
Get the current status of the repository.
GET /api/v1/repositories/status
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
repository | string | No | Repository name (uses current if not specified) |
Response:
{
"branch": "main",
"commit_id": "abc123def456789",
"is_detached": false,
"is_clean": true,
"staged_count": 0,
"unstaged_count": 0,
"merge_in_progress": false
}
curl Example:
# Current repository
curl http://localhost:8000/api/v1/repositories/status
# Specific repository
curl "http://localhost:8000/api/v1/repositories/status?repository=my-data-repo"
List Tracked Tables
List all tables being tracked for version control.
GET /api/v1/repositories/tables
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
repository | string | No | Repository name |
Response:
["users", "orders", "products"]
curl Example:
curl http://localhost:8000/api/v1/repositories/tables
Add Storage Backend
Add a new storage backend to the repository.
POST /api/v1/repositories/storage
Request Body:
{
"name": "warehouse",
"backend": "postgresql",
"config": {
"url": "postgresql://user:pass@warehouse-host/analytics"
}
}
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Storage configuration name |
backend | string | Yes | Backend type (see below) |
config | object | Yes | Backend-specific configuration |
Supported Backend Types:
| Backend | Value | Required Config Fields |
|---|---|---|
| PostgreSQL | postgresql | url or (host, database) |
| MySQL | mysql | url or (host, database) |
| SQL Server | mssql | url or (host, database) |
| SQLite | sqlite | path |
| S3 | s3 | bucket |
| Azure Blob | azure | account, container |
| GCS | gcs | bucket |
| Local Filesystem | local | path |
Backend Configuration Examples:
// PostgreSQL
{
"name": "prod-pg",
"backend": "postgresql",
"config": {
"host": "db.example.com",
"port": 5432,
"database": "production",
"schema": "public"
}
}
// MySQL
{
"name": "analytics-mysql",
"backend": "mysql",
"config": {
"host": "mysql.example.com",
"port": 3306,
"database": "analytics"
}
}
// SQL Server
{
"name": "warehouse-mssql",
"backend": "mssql",
"config": {
"host": "sqlserver.example.com",
"port": 1433,
"database": "warehouse"
}
}
// SQLite
{
"name": "local-sqlite",
"backend": "sqlite",
"config": {
"path": "/data/local.db"
}
}
// S3
{
"name": "datalake",
"backend": "s3",
"config": {
"bucket": "company-datalake",
"region": "us-west-2",
"prefix": "epoch/"
}
}
// Azure Blob
{
"name": "azure-store",
"backend": "azure",
"config": {
"account": "myaccount",
"container": "data",
"prefix": "epoch/"
}
}
// GCS
{
"name": "gcs-store",
"backend": "gcs",
"config": {
"bucket": "my-bucket",
"project": "my-project",
"prefix": "epoch/"
}
}
// Local Filesystem
{
"name": "local-fs",
"backend": "local",
"config": {
"path": "/data/epoch"
}
}
Response: 201 Created
{
"status": "created",
"name": "warehouse"
}
curl Examples:
# PostgreSQL
curl -X POST http://localhost:8000/api/v1/repositories/storage \
-H "Content-Type: application/json" \
-d '{
"name": "warehouse",
"backend": "postgresql",
"config": {"host": "db.example.com", "database": "warehouse"}
}'
# S3
curl -X POST http://localhost:8000/api/v1/repositories/storage \
-H "Content-Type: application/json" \
-d '{
"name": "datalake",
"backend": "s3",
"config": {"bucket": "company-datalake", "region": "us-west-2"}
}'
# Azure Blob
curl -X POST http://localhost:8000/api/v1/repositories/storage \
-H "Content-Type: application/json" \
-d '{
"name": "azure-store",
"backend": "azure",
"config": {"account": "myaccount", "container": "data"}
}'
Track Table
Start tracking a table for version control.
POST /api/v1/repositories/tables/track
Request Body:
{
"table_name": "users",
"storage_name": "primary",
"primary_key": ["id"]
}
| Field | Type | Required | Description |
|---|---|---|---|
table_name | string | Yes | Table name to track |
storage_name | string | Yes | Storage backend containing the table |
primary_key | array | Yes | Primary key column(s) |
Response: 201 Created
{
"status": "tracking",
"table": "users"
}
curl Example:
curl -X POST http://localhost:8000/api/v1/repositories/tables/track \
-H "Content-Type: application/json" \
-d '{
"table_name": "users",
"storage_name": "primary",
"primary_key": ["id"]
}'
Branch Endpoints
Create, list, and manage branches.
Create Branch
Create a new branch.
POST /api/v1/branches
Request Body:
{
"name": "feature/add-preferences",
"start_point": "main",
"checkout": true
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
name | string | Yes | - | Branch name (min 1 character) |
start_point | string | No | HEAD | Commit ID or branch to start from |
checkout | boolean | No | true | Whether to checkout the new branch |
Response: 201 Created
{
"name": "feature/add-preferences",
"head_commit_id": "abc123def456789",
"is_protected": false,
"created_at": "2024-12-18T10:30:00Z"
}
curl Example:
# Create and checkout a new branch
curl -X POST http://localhost:8000/api/v1/branches \
-H "Content-Type: application/json" \
-d '{"name": "feature/add-preferences"}'
# Create from specific commit without checkout
curl -X POST http://localhost:8000/api/v1/branches \
-H "Content-Type: application/json" \
-d '{"name": "hotfix/urgent", "start_point": "abc123", "checkout": false}'
List Branches
List all branches in the repository.
GET /api/v1/branches
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
repository | string | No | Repository name |
Response:
{
"branches": [
{
"name": "main",
"head_commit_id": "abc123def456789",
"is_protected": true,
"created_at": "2024-12-01T00:00:00Z"
},
{
"name": "feature/add-preferences",
"head_commit_id": "def456abc789012",
"is_protected": false,
"created_at": "2024-12-18T10:30:00Z"
}
],
"current": "main"
}
curl Example:
curl http://localhost:8000/api/v1/branches
Checkout Branch
Switch to a different branch.
POST /api/v1/branches/{name}/checkout
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
name | string | Branch name to checkout |
Response:
{
"status": "checked_out",
"branch": "feature/add-preferences",
"commit_id": "def456abc789012"
}
curl Example:
curl -X POST http://localhost:8000/api/v1/branches/feature%2Fadd-preferences/checkout
Note: URL-encode branch names containing
/(e.g.,feature/xyzbecomesfeature%2Fxyz).
Delete Branch
Delete a branch.
DELETE /api/v1/branches/{name}
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
name | string | Branch name to delete |
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
force | boolean | No | false | Force delete even if unmerged |
repository | string | No | - | Repository name |
Response: 204 No Content
curl Example:
# Normal delete (fails if unmerged)
curl -X DELETE http://localhost:8000/api/v1/branches/feature%2Fadd-preferences
# Force delete
curl -X DELETE "http://localhost:8000/api/v1/branches/feature%2Fadd-preferences?force=true"
Compare Branches
Compare two branches to see how they’ve diverged.
GET /api/v1/branches/compare
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
source | string | Yes | Source branch |
target | string | Yes | Target branch |
repository | string | No | Repository name |
Response:
{
"source_branch": "feature/add-preferences",
"target_branch": "main",
"ahead_count": 3,
"behind_count": 1,
"can_fast_forward": false,
"merge_base_id": "abc123def456789"
}
curl Example:
curl "http://localhost:8000/api/v1/branches/compare?source=feature/add-preferences&target=main"
Merge Branch
Merge a branch into the current branch.
POST /api/v1/branches/merge
Request Body:
{
"source": "feature/add-preferences",
"strategy": "THREE_WAY",
"message": "Merge feature/add-preferences into main",
"no_commit": false
}
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
source | string | Yes | - | Source branch to merge from |
strategy | string | No | THREE_WAY | Merge strategy: THREE_WAY, FAST_FORWARD, OURS, THEIRS |
message | string | No | auto | Custom merge commit message |
no_commit | boolean | No | false | Stage but don’t commit merge |
Response:
{
"source_branch": "feature/add-preferences",
"target_branch": "main",
"is_fast_forward": false,
"has_conflicts": false,
"conflict_count": 0,
"result_commit_id": "ghi789jkl012345"
}
Response (with conflicts): 409 Conflict
{
"error": "MergeConflictError",
"message": "Merge conflict in 1 table(s)",
"details": {
"conflict_tables": ["users"],
"conflict_count": 3
}
}
curl Examples:
# Standard merge
curl -X POST http://localhost:8000/api/v1/branches/merge \
-H "Content-Type: application/json" \
-d '{"source": "feature/add-preferences"}'
# Fast-forward only
curl -X POST http://localhost:8000/api/v1/branches/merge \
-H "Content-Type: application/json" \
-d '{"source": "feature/add-preferences", "strategy": "FAST_FORWARD"}'
# Merge without committing
curl -X POST http://localhost:8000/api/v1/branches/merge \
-H "Content-Type: application/json" \
-d '{"source": "feature/add-preferences", "no_commit": true}'
Abort Merge
Abort an in-progress merge operation.
POST /api/v1/branches/merge/abort
Response:
{
"status": "aborted"
}
curl Example:
curl -X POST http://localhost:8000/api/v1/branches/merge/abort
Commit Endpoints
Create commits and view commit history.
Create Commit
Create a new commit with staged changes.
POST /api/v1/commits
Request Body:
{
"message": "Add user preferences feature",
"author_name": "Jane Developer",
"author_email": "jane@example.com"
}
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | Commit message (min 1 character) |
author_name | string | No | Author name (uses config default) |
author_email | string | No | Author email (uses config default) |
Response: 201 Created
{
"id": "abc123def456789abcdef0123456789abcdef01",
"short_id": "abc123d",
"message": "Add user preferences feature",
"author_name": "Jane Developer",
"author_email": "jane@example.com",
"created_at": "2024-12-18T10:45:00Z",
"parent_count": 1
}
curl Example:
curl -X POST http://localhost:8000/api/v1/commits \
-H "Content-Type: application/json" \
-d '{"message": "Add user preferences feature"}'
List Commits
Get commit history.
GET /api/v1/commits
Query Parameters:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
ref | string | No | HEAD | Branch or commit reference |
limit | integer | No | 100 | Max commits to return (1-1000) |
offset | integer | No | 0 | Offset for pagination |
repository | string | No | - | Repository name |
Response:
{
"commits": [
{
"id": "abc123def456789abcdef0123456789abcdef01",
"short_id": "abc123d",
"message": "Add user preferences feature",
"author_name": "Jane Developer",
"author_email": "jane@example.com",
"created_at": "2024-12-18T10:45:00Z",
"parent_count": 1
}
],
"total": 42,
"has_more": true
}
curl Examples:
# Get recent commits
curl http://localhost:8000/api/v1/commits
# Get commits from specific branch
curl "http://localhost:8000/api/v1/commits?ref=feature/add-preferences"
# Pagination
curl "http://localhost:8000/api/v1/commits?limit=10&offset=20"
Get Commit
Get details of a specific commit.
GET /api/v1/commits/{commit_id}
Path Parameters:
| Parameter | Type | Description |
|---|---|---|
commit_id | string | Full or short commit ID |
Response:
{
"id": "abc123def456789abcdef0123456789abcdef01",
"short_id": "abc123d",
"message": "Add user preferences feature",
"author_name": "Jane Developer",
"author_email": "jane@example.com",
"created_at": "2024-12-18T10:45:00Z",
"parent_count": 1
}
curl Example:
curl http://localhost:8000/api/v1/commits/abc123def456789
Get Diff
Show differences between commits or branches.
GET /api/v1/commits/diff
Query Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
base | string | No | Base commit/branch (defaults to parent) |
target | string | No | Target commit/branch (defaults to HEAD) |
repository | string | No | Repository name |
Response:
{
"base_commit_id": "abc123def456789",
"target_commit_id": "def456ghi789012",
"tables_changed": 2,
"total_inserts": 15,
"total_updates": 8,
"total_deletes": 3
}
curl Examples:
# Diff working state vs HEAD
curl http://localhost:8000/api/v1/commits/diff
# Diff between branches
curl "http://localhost:8000/api/v1/commits/diff?base=main&target=feature/add-preferences"
# Diff between commits
curl "http://localhost:8000/api/v1/commits/diff?base=abc123&target=def456"
Stage All Changes
Stage all pending changes for commit.
POST /api/v1/commits/stage
Response:
{
"status": "staged",
"count": 5
}
curl Example:
curl -X POST http://localhost:8000/api/v1/commits/stage
Unstage All Changes
Remove all changes from the staging area.
POST /api/v1/commits/unstage
Response:
{
"status": "unstaged",
"count": 5
}
curl Example:
curl -X POST http://localhost:8000/api/v1/commits/unstage
Error Responses
All errors follow a consistent format:
{
"error": "ErrorClassName",
"message": "Human-readable error description",
"details": {
"additional": "context information"
}
}
HTTP Status Codes
| Code | Meaning | When Used |
|---|---|---|
200 | OK | Successful GET/POST operations |
201 | Created | Resource successfully created |
204 | No Content | Successful DELETE operations |
400 | Bad Request | Invalid request format or parameters |
404 | Not Found | Resource not found |
409 | Conflict | Merge conflicts, duplicate resources |
422 | Unprocessable Entity | Validation errors |
503 | Service Unavailable | Database connection issues |
Error Types
| Error | HTTP Status | Description |
|---|---|---|
BranchNotFoundError | 404 | Specified branch doesn’t exist |
CommitNotFoundError | 404 | Specified commit doesn’t exist |
RepositoryNotInitializedError | 404 | Repository not initialized or opened |
BranchAlreadyExistsError | 409 | Branch name already in use |
MergeConflictError | 409 | Merge has unresolved conflicts |
ConnectionFailedError | 503 | Unable to connect to metadata database |
ValidationError | 422 | Request body validation failed |
Example Error Responses
Branch Not Found:
{
"error": "BranchNotFoundError",
"message": "Branch 'feature/nonexistent' not found",
"details": {
"branch": "feature/nonexistent"
}
}
Validation Error:
{
"detail": [
{
"type": "string_too_short",
"loc": ["body", "name"],
"msg": "String should have at least 1 character",
"input": "",
"ctx": {"min_length": 1}
}
]
}
Connection Error:
{
"error": "ConnectionFailedError",
"message": "Failed to connect to metadata database",
"details": {
"url": "postgresql://localhost/horizon_epoch"
}
}
Pagination
List endpoints support pagination via limit and offset query parameters.
GET /api/v1/commits?limit=10&offset=20
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
limit | integer | 100 | 1-1000 | Maximum items per page |
offset | integer | 0 | 0+ | Number of items to skip |
Paginated responses include metadata:
{
"items": [...],
"total": 150,
"has_more": true
}
OpenAPI Specification
The API provides auto-generated OpenAPI 3.0 documentation:
| Endpoint | Description |
|---|---|
GET /openapi.json | Raw OpenAPI specification |
GET /docs | Swagger UI (interactive, debug mode only) |
GET /redoc | ReDoc (alternative UI, debug mode only) |
Enable debug mode to access Swagger UI:
EPOCH_DEBUG=true uv run uvicorn horizon_epoch.api:app --reload
Then visit: http://localhost:8000/docs
Export OpenAPI Spec
You can export the OpenAPI specification using the provided script:
# Export as JSON
cd python
uv run python scripts/export_openapi.py --output ../docs/openapi.json
# Export as YAML
uv run python scripts/export_openapi.py --format yaml --output ../docs/openapi.yaml
Environment Configuration
The API can be configured via environment variables:
| Variable | Default | Description |
|---|---|---|
EPOCH_METADATA_URL | postgresql://localhost:5432/horizon_epoch | Metadata database URL |
EPOCH_DEFAULT_BRANCH | main | Default branch name |
EPOCH_API_PREFIX | /api/v1 | API route prefix |
EPOCH_DEBUG | false | Enable debug mode (exposes Swagger UI) |
EPOCH_LOG_LEVEL | INFO | Logging level |
EPOCH_AUTHOR_NAME | - | Default author name for commits |
EPOCH_AUTHOR_EMAIL | - | Default author email for commits |
Example:
export EPOCH_METADATA_URL="postgresql://user:pass@localhost:5432/horizon_epoch"
export EPOCH_DEBUG=true
export EPOCH_AUTHOR_NAME="API User"
export EPOCH_AUTHOR_EMAIL="api@example.com"
uv run uvicorn horizon_epoch.api:app --host 0.0.0.0 --port 8000
Rate Limiting
Current Status: Rate limiting is not currently implemented.
When rate limiting is implemented, limits will be returned in response headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1705312200
CORS
Cross-Origin Resource Sharing is enabled for all origins by default. For production, configure allowed origins appropriately.
Current configuration:
- Origins:
*(all) - Methods: All HTTP methods
- Headers: All headers
- Credentials: Allowed
See Also
- CLI Reference - Command-line interface documentation
- Python SDK - Python client library
- Configuration Reference - All configuration options