API Reference
Base URL: /api/v1
All endpoints except authentication require a valid JWT token in the Authorization header: Bearer <token>
Authentication
Login
POST /api/v1/auth/login
OAuth2 compatible token login, get an access token for future requests.
Request Body (Form Data):
* username (string, required): The user's email or username.
* password (string, required): The user's password.
Response (200 OK):
Response (401 Unauthorized): * Invalid credentials.
Test Token
POST /api/v1/auth/login/test-token
Test access token. Returns the current user.
Response (200 OK): * Returns a User object.
Register
POST /api/v1/auth/register
Register a new user account.
Request Body:
Response (201 Created): * Returns the created User object.
Response (400 Bad Request): * Username or email already exists.
Change Password
POST /api/v1/auth/change-password
Change the current user's password.
Request Body:
Response (204 No Content): * Password changed successfully.
Response (400 Bad Request): * Weak password.
Response (401 Unauthorized): * Wrong current password.
Refresh Token
POST /api/v1/auth/refresh
Refresh the access token without requiring the user to log in again.
When to Use: - When your access token is about to expire or has expired - To maintain a seamless user experience without forcing re-authentication - For long-running applications or sessions that exceed the token lifetime
Access tokens have a limited lifetime for security reasons. In this backend, access tokens expire after 30 minutes. When a token expires, API requests will return a 401 Unauthorized error. Instead of requiring users to log in again with their credentials, you can use the refresh token to obtain a new access token. This keeps your application secure while maintaining a smooth user experience.
Best Practice: Implement token refresh logic in your client application to automatically refresh tokens before they expire (e.g., refresh at 25 minutes to provide a buffer).
Request Body:
Response (200 OK):
Response (401 Unauthorized): * Refresh token is invalid or expired. User must log in again.
Get Current User
GET /api/v1/auth/me
Get the current authenticated user's profile.
Response (200 OK): * Returns a User object.
Update Profile
PATCH /api/v1/auth/me
Update the current user's profile.
Request Body:
Response (200 OK): * Returns the updated User object.
Logout
POST /api/v1/auth/logout
Logout the current user.
Response (204 No Content): * Successfully logged out.
Users
Get Users
GET /api/v1/users/
Retrieve users. Requires superuser privileges.
Parameters:
* skip (integer, optional, default=0): Number of records to skip.
* limit (integer, optional, default=100): Maximum number of records to return.
Response (200 OK): * List of User objects.
Response (403 Forbidden): * User does not have superuser privileges.
Create User
POST /api/v1/users/
Create new user. Requires superuser privileges.
Request Body:
{
"username": "string",
"email": "string",
"password": "string",
"is_active": true,
"is_superuser": false
}
Response (200 OK): * Returns the created User object.
Get Current User
GET /api/v1/users/me
Get current user.
Response (200 OK): * Returns the current User object.
Update Current User
PUT /api/v1/users/me
Update own user.
Request Body:
Response (200 OK): * Returns the updated User object.
Get User by ID
GET /api/v1/users/{user_id}
Get a specific user by id.
Parameters:
* user_id (string, required): The UUID of the user.
Response (200 OK): * Returns a User object.
Response (404 Not Found): * User not found.
Update User
PUT /api/v1/users/{user_id}
Update a user. Requires superuser privileges.
Parameters:
* user_id (string, required): The UUID of the user.
Request Body:
Response (200 OK): * Returns the updated User object.
Delete User
DELETE /api/v1/users/{user_id}
Delete a user. Requires superuser privileges.
Parameters:
* user_id (string, required): The UUID of the user.
* transfer_to (string, query, optional): UUID of user to transfer ownership to.
Response (204 No Content): * User deleted successfully.
Response (404 Not Found): * User not found.
Assign Project to User
POST /api/v1/users/{user_id}/assignments
Assign a project to a user.
Parameters:
* user_id (string, required): The UUID of the user.
Request Body:
Response (200 OK): * Project assigned successfully.
Response (404 Not Found): * User or Project not found.
Update User Role
PATCH /api/v1/users/{user_id}/role
Update a user's role. Requires superuser privileges.
Parameters:
* user_id (string, required): The UUID of the user.
Request Body:
Response (200 OK): * Returns the updated User object.
Response (403 Forbidden): * Insufficient permissions.
Get User Projects
GET /api/v1/users/{user_id}/projects
List all projects for a specific user.
Parameters:
* user_id (string, required): The UUID of the user.
Response (200 OK): * List of Project objects.
Response (403 Forbidden): * Insufficient permissions.
Projects
Get Projects
GET /api/v1/projects/
Retrieve projects for the current user.
Parameters:
* skip (integer, optional, default=0)
* limit (integer, optional, default=100)
* include_stats (boolean, optional): When true, includes statistics for each project (feature_count, comparison_count, progress for each dimension).
Response (200 OK): * List of Project objects.
When include_stats=true, each project includes additional fields:
{
"id": "uuid",
"name": "string",
"description": "string",
"created_at": "datetime",
"feature_count": 10,
"comparison_count": {
"complexity": 15,
"value": 12
},
"progress": {
"complexity": {
"progress_percent": 75.5,
"transitive_coverage": 0.8
},
"value": {
"progress_percent": 60.0,
"transitive_coverage": 0.65
}
}
}
Create Project
POST /api/v1/projects/
Create new project.
Request Body:
{
"name": "string",
"description": "string",
"comparison_mode": "binary" | "graded" // optional, default "binary"
}
Comparison Modes:
| Mode | Description | Use Case |
|------|-------------|----------|
| binary | Simple A vs B choices (feature_a, feature_b, tie) | Quick decisions, low cognitive load |
| graded | 5-point scale (a_much_better, a_better, equal, b_better, b_much_better) | More expressive, 30-40% fewer comparisons needed |
Response (201 Created):
* Returns the created Project object with comparison_mode field.
Example: Creating a Graded Project
curl -X POST "https://api.oneselect.example.com/v1/projects/" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name": "My Graded Project", "description": "Using 5-point scale", "comparison_mode": "graded"}'
Get Project
GET /api/v1/projects/{id}
Get project by ID.
Parameters:
* id (string, required): The UUID of the project.
Response (200 OK): * Returns a Project object.
Response (404 Not Found): * Project not found.
Update Project
PUT /api/v1/projects/{id}
Update a project.
Parameters:
* id (string, required): The UUID of the project.
Request Body:
Response (200 OK): * Returns the updated Project object.
Delete Project
DELETE /api/v1/projects/{id}
Delete a project.
Parameters:
* id (string, required): The UUID of the project.
Response (204 No Content): * Project deleted successfully.
Get Project Summary
GET /api/v1/projects/{id}/summary
Get a summary of project statistics and progress.
Parameters:
* id (string, required): The UUID of the project.
Response (200 OK):
{
"project": { ...Project Object... },
"feature_count": 0,
"comparison_count": 0,
"progress": {
"complexity": 0.0,
"value": 0.0
}
}
Get Project Collaborators
GET /api/v1/projects/{id}/collaborators
List all collaborators for a project.
Parameters:
* id (string, required): The UUID of the project.
Response (200 OK): * List of collaborator objects with user information and roles.
Get Project Activity
GET /api/v1/projects/{id}/activity
Get activity log for a project.
Parameters:
* id (string, required): The UUID of the project.
* page (integer, query, optional): Page number.
* per_page (integer, query, optional): Items per page.
* action_type (string, query, optional): Filter by action type.
Response (200 OK):
{
"items": [
{
"action": "string",
"timestamp": "datetime",
"user_id": "uuid"
}
],
"total": 0,
"page": 1,
"per_page": 20
}
Get Last Modified
GET /api/v1/projects/{id}/last-modified
Get the last modification timestamp for a project.
Parameters:
* id (string, required): The UUID of the project.
Response (200 OK):
Get Comparison History
GET /api/v1/projects/{id}/history
Get complete audit trail of all comparisons made in a project, including both active and deleted comparisons. This provides a full history for auditing purposes.
Parameters:
* id (string, required): The UUID of the project.
Response (200 OK):
{
"project": {
"id": "uuid",
"name": "string",
"description": "string"
},
"comparisons": [
{
"id": "uuid",
"feature_a": {
"id": "uuid",
"name": "string"
},
"feature_b": {
"id": "uuid",
"name": "string"
},
"choice": "feature_a" | "feature_b" | "tie",
"dimension": "complexity" | "value",
"user": {
"id": "uuid",
"username": "string"
},
"created_at": "datetime"
}
],
"deleted_comparisons": [
{
"id": "uuid",
"feature_a": {
"id": "uuid",
"name": "string"
},
"feature_b": {
"id": "uuid",
"name": "string"
},
"choice": "string",
"dimension": "string",
"user": {
"id": "uuid",
"username": "string"
},
"created_at": "datetime",
"deleted_at": "datetime",
"deleted_by": {
"id": "uuid",
"username": "string"
}
}
]
}
Response (404 Not Found): * Project not found.
Features
All feature endpoints are nested under projects: /api/v1/projects/{project_id}/features
Get Features
GET /api/v1/projects/{project_id}/features
Retrieve features for a project.
Parameters:
* project_id (string, required): The UUID of the project.
* page (integer, query, optional, default=1)
* per_page (integer, query, optional, default=50)
* search (string, query, optional): Search term to filter features.
* include_scores (boolean, query, optional): When true, includes Bayesian scores (mu, sigma) for each feature in both dimensions.
Response (200 OK):
When include_scores=true, each feature includes additional score fields:
{
"id": "uuid",
"name": "string",
"description": "string",
"tags": ["string"],
"scores": {
"complexity": {
"mu": 0.5,
"sigma": 1.2
},
"value": {
"mu": 0.8,
"sigma": 0.9
}
}
}
Create Feature
POST /api/v1/projects/{project_id}/features
Create new feature in a project.
Parameters:
* project_id (string, required): The UUID of the project.
Request Body:
Response (201 Created): * Returns the created Feature object.
Bulk Create Features
POST /api/v1/projects/{project_id}/features/bulk
Create multiple features at once.
Parameters:
* project_id (string, required): The UUID of the project.
Request Body:
Response (201 Created):
Bulk Delete Features
POST /api/v1/projects/{project_id}/features/bulk-delete
Delete multiple features at once.
Parameters:
* project_id (string, required): The UUID of the project.
Request Body:
Response (200 OK):
Get Feature
GET /api/v1/projects/{project_id}/features/{feature_id}
Get feature by ID.
Parameters:
* project_id (string, required): The UUID of the project.
* feature_id (string, required): The UUID of the feature.
Response (200 OK): * Returns a Feature object.
Response (404 Not Found): * Feature not found.
Update Feature
PUT /api/v1/projects/{project_id}/features/{feature_id}
Update a feature.
Parameters:
* project_id (string, required): The UUID of the project.
* feature_id (string, required): The UUID of the feature.
Request Body:
Response (200 OK): * Returns the updated Feature object.
Delete Feature
DELETE /api/v1/projects/{project_id}/features/{feature_id}
Delete a feature.
Parameters:
* project_id (string, required): The UUID of the project.
* feature_id (string, required): The UUID of the feature.
Response (204 No Content): * Feature deleted successfully.
Comparisons
All comparison endpoints are nested under projects: /api/v1/projects/{project_id}/comparisons
Get Next Comparison Pair
GET /api/v1/projects/{project_id}/comparisons/next
Get the next pair of features to compare. This is the primary endpoint for the comparison workflow.
The endpoint uses a chain-building algorithm that leverages transitive closure for O(N log N) efficiency. When target_certainty is specified, the endpoint returns 204 once the transitive coverage reaches that threshold, enabling early stopping without exhaustive comparisons.
Parameters:
* project_id (string, required): The UUID of the project.
* dimension (string, query, required): One of "complexity", "value".
* target_certainty (number, query, optional, default=0.0): Target transitive coverage level (0.0-1.0). When set to a value > 0, the endpoint returns 204 No Content once transitive coverage reaches this threshold. Common values: 0.7 (70%), 0.8 (80%), 0.9 (90%). When set to 0.0 (default), comparisons continue until all orderings are known via transitivity.
* include_progress (boolean, query, optional): When true, includes progress metrics in the response to avoid a separate round-trip to the progress endpoint.
Response (200 OK):
{
"comparison_id": "uuid",
"feature_a": { ...Feature Object... },
"feature_b": { ...Feature Object... },
"dimension": "complexity" | "value",
"progress": { // Only included when include_progress=true
"dimension": "complexity",
"target_certainty": 0.9,
"transitive_coverage": 0.75,
"effective_confidence": 0.72,
"progress_percent": 80.0,
"comparisons_done": 15,
"comparisons_remaining": 5
}
}
Response (204 No Content):
* Target certainty reached (when target_certainty > 0 and transitive coverage ≥ target), or
* All orderings are known via transitive inference, or
* No useful comparisons left to make.
Usage Example:
GET /api/v1/projects/{id}/comparisons/next?dimension=complexity&target_certainty=0.9&include_progress=true
Efficiency: With transitive closure optimization, reaching 90% certainty for N features typically requires approximately N × log₂(N) comparisons per dimension, rather than the theoretical maximum of N×(N-1)/2 pairwise comparisons.
Get Comparisons
GET /api/v1/projects/{project_id}/comparisons
Retrieve comparison history for a project.
Parameters:
* project_id (string, required): The UUID of the project.
* dimension (string, query, optional): Filter by dimension.
* page (integer, query, optional)
* per_page (integer, query, optional)
* ids (string, query, optional): Comma-separated list of comparison UUIDs to fetch specific comparisons. When provided, pagination parameters are ignored.
Response (200 OK):
Batch Fetch Example:
Returns only the specified comparisons for efficient bulk retrieval.Create Comparison
POST /api/v1/projects/{project_id}/comparisons
Submit a comparison result. This endpoint performs a Bayesian update to the feature rankings and returns the comparison along with current inconsistency statistics for immediate UI feedback.
Parameters:
* project_id (string, required): The UUID of the project.
Request Body:
{
"feature_a_id": "uuid",
"feature_b_id": "uuid",
"choice": "feature_a" | "feature_b" | "tie",
"dimension": "complexity" | "value"
}
Response (201 Created):
{
"id": "uuid",
"project_id": "uuid",
"feature_a": {
"id": "uuid",
"name": "string"
},
"feature_b": {
"id": "uuid",
"name": "string"
},
"choice": "feature_a" | "feature_b" | "tie",
"dimension": "complexity" | "value",
"created_at": "datetime",
"inconsistency_stats": {
"cycle_count": 0,
"total_comparisons": 0,
"inconsistency_percentage": 0.0,
"dimension": "complexity" | "value"
}
}
The inconsistency_stats object provides immediate feedback about the health of the comparison graph, allowing UIs to display warnings when inconsistencies are detected.
Create Binary Comparison
POST /api/v1/projects/{project_id}/comparisons/binary
Submit a binary comparison (A beats B, B beats A, or tie). This endpoint is for projects in binary comparison mode. For graded comparisons, use the /comparisons/graded endpoint.
Parameters:
* project_id (string, required): The UUID of the project (must be in binary mode).
Request Body:
{
"feature_a_id": "uuid",
"feature_b_id": "uuid",
"choice": "feature_a" | "feature_b" | "tie",
"dimension": "complexity" | "value"
}
Response (201 Created):
{
"id": "uuid",
"project_id": "uuid",
"feature_a": {
"id": "uuid",
"name": "string"
},
"feature_b": {
"id": "uuid",
"name": "string"
},
"choice": "feature_a" | "feature_b" | "tie",
"dimension": "complexity" | "value",
"created_at": "datetime",
"inconsistency_stats": {
"cycle_count": 0,
"total_comparisons": 0,
"inconsistency_percentage": 0.0,
"dimension": "complexity" | "value"
}
}
Response (400 Bad Request):
* If the project is in graded mode: {"detail": "Project is in 'graded' mode. Use the graded comparison endpoint."}
Create Graded Comparison
POST /api/v1/projects/{project_id}/comparisons/graded
Submit a graded comparison using a 5-point scale. This endpoint is for projects in graded comparison mode. Graded comparisons provide more information per comparison, allowing 30-40% faster convergence with fewer total comparisons needed.
Strength Values:
| Strength | Meaning | Derived Choice | Update Multiplier |
|----------|---------|----------------|-------------------|
| a_much_better | Feature A is significantly better than B | feature_a | 2.0x (configurable) |
| a_better | Feature A is better than B | feature_a | 1.0x |
| equal | Features are roughly equal | tie | 1.0x (configurable) |
| b_better | Feature B is better than A | feature_b | 1.0x |
| b_much_better | Feature B is significantly better than A | feature_b | 2.0x (configurable) |
The "a_much_better" and "b_much_better" options apply a 2.0x multiplier to the Bayesian update, causing faster convergence of scores and reduced variance. The multipliers are configurable in app/core/config.py:
- GRADED_MUCH_BETTER_MULTIPLIER (default: 2.0) - for strong preferences
- GRADED_EQUAL_MULTIPLIER (default: 1.0) - for ties; reduce if users select "equal" when uncertain
Parameters:
* project_id (string, required): The UUID of the project (must be in graded mode).
Request Body:
{
"feature_a_id": "uuid",
"feature_b_id": "uuid",
"dimension": "complexity" | "value",
"strength": "a_much_better" | "a_better" | "equal" | "b_better" | "b_much_better"
}
Response (201 Created):
{
"id": "uuid",
"project_id": "uuid",
"feature_a": {
"id": "uuid",
"name": "string"
},
"feature_b": {
"id": "uuid",
"name": "string"
},
"dimension": "complexity" | "value",
"strength": "a_much_better" | "a_better" | "equal" | "b_better" | "b_much_better",
"choice": "feature_a" | "feature_b" | "tie",
"created_at": "datetime",
"inconsistency_stats": {
"cycle_count": 0,
"total_comparisons": 0,
"inconsistency_percentage": 0.0,
"dimension": "complexity" | "value"
}
}
Response (400 Bad Request):
* If the project is in binary mode: {"detail": "Project is in 'binary' mode. Use the binary comparison endpoint."}
Example: Graded vs Binary Comparison Count
For a project with 20 features: - Binary mode: ~60 comparisons to reach 90% certainty - Graded mode: ~40 comparisons to reach 90% certainty (33% reduction)
The stronger signal from "a_much_better"/"b_much_better" judgments (2x multiplier) accelerates learning.
Get Comparison Estimates
GET /api/v1/projects/{project_id}/comparisons/estimates
Get current estimates for all features based on comparisons made.
Parameters:
* project_id (string, required): The UUID of the project.
* dimension (string, query, required): One of "complexity", "value".
Response (200 OK):
{
"dimension": "complexity" | "value",
"estimates": [
{
"feature_id": "uuid",
"estimate": 0.0,
"variance": 0.0
}
]
}
Get Inconsistency Statistics
GET /api/v1/projects/{project_id}/comparisons/inconsistency-stats
Get a summary of inconsistency statistics without detailed cycle information. This lightweight endpoint is ideal for dashboard widgets, health checks, and polling for updates.
Parameters:
* project_id (string, required): The UUID of the project.
* dimension (string, query, optional): Filter by dimension ("complexity" or "value"). If omitted, returns stats for both dimensions.
Response (200 OK):
{
"cycle_count": 0,
"total_comparisons": 0,
"inconsistency_percentage": 0.0,
"dimension": "complexity" | "value"
}
Use Cases: * Dashboard widgets showing project health * Real-time polling for inconsistency alerts * Quick health checks without fetching full cycle details
Get Inconsistencies
GET /api/v1/projects/{project_id}/comparisons/inconsistencies
Detect and return detailed information about inconsistent comparison cycles (e.g., A > B > C > A). This endpoint performs a depth-first search on the comparison graph to identify all cycles, which represent logical inconsistencies where the transitive property of comparisons is violated.
Parameters:
* project_id (string, required): The UUID of the project.
* dimension (string, query, optional): Filter by dimension ("complexity" or "value"). If omitted, detects cycles in both dimensions.
Response (200 OK):
{
"cycles": [
{
"feature_ids": ["uuid", "uuid", "uuid"],
"feature_names": ["Feature A", "Feature B", "Feature C"],
"length": 3,
"dimension": "complexity" | "value"
}
],
"count": 0,
"message": "Found N inconsistency cycles"
}
Algorithm: Uses depth-first search (DFS) with cycle detection. Time complexity: O(V + E) where V is the number of features and E is the number of comparisons. Typical performance: <1ms for 70 features.
Cycle Interpretation: A cycle like [A, B, C] means: A > B > C > A, which is logically inconsistent. The cycle represents a sequence of comparisons where following the "winner" edges leads back to the starting feature.
Get Resolution Pair
GET /api/v1/projects/{project_id}/comparisons/resolve-inconsistency
Get a specific pair of features to compare to resolve detected inconsistencies. Uses the "weakest link" strategy: identifies all cycles, examines pairs in those cycles, and suggests re-comparing the pair with the highest combined uncertainty (σ_i + σ_j). This approach targets the comparison most likely to be incorrect.
Parameters:
* project_id (string, required): The UUID of the project.
* dimension (string, query, required): The dimension to check ("complexity" or "value").
Response (200 OK):
{
"comparison_id": null,
"feature_a": {
"id": "uuid",
"name": "string",
"description": "string"
},
"feature_b": {
"id": "uuid",
"name": "string",
"description": "string"
},
"dimension": "complexity" | "value",
"reason": "Weakest link in cycle: highest combined uncertainty",
"combined_uncertainty": 2.5,
"cycle_context": {
"cycle_count": 2,
"features_in_cycles": ["Feature A", "Feature B", "Feature C", "Feature D"]
}
}
The cycle_context field provides additional information about the inconsistency state:
- cycle_count: Total number of cycles detected in the comparison graph
- features_in_cycles: List of unique feature names involved in any cycle, helping users understand which features have conflicting rankings
Response (204 No Content): * No inconsistencies detected in the specified dimension.
Strategy: The weakest link approach assumes that pairs with high uncertainty are more likely to have incorrect comparison results. By re-comparing these pairs, users can potentially break the cycle and restore consistency to the comparison graph.
Get Comparison Progress
GET /api/v1/projects/{project_id}/comparisons/progress
Get progress metrics for comparisons in a project using a hybrid confidence model that combines transitive coverage, Bayesian confidence, and consistency scoring.
Parameters:
* project_id (string, required): The UUID of the project.
* dimension (string, query, required): One of "complexity", "value".
* target_certainty (number, query, optional, default=0.90): Target certainty level (0.0-1.0).
Response (200 OK):
{
"dimension": "complexity",
"target_certainty": 0.90,
"transitive_coverage": 0.85,
"transitive_known_pairs": 34,
"uncertain_pairs": 6,
"direct_coverage": 0.45,
"unique_pairs_compared": 18,
"total_possible_pairs": 40,
"coverage_confidence": 0.45,
"bayesian_confidence": 0.72,
"consistency_score": 1.0,
"effective_confidence": 0.88,
"progress_percent": 88.0,
"total_comparisons_done": 22,
"comparisons_remaining": 3,
"theoretical_minimum": 22,
"practical_estimate": 27,
"current_avg_variance": 0.28,
"comparisons_done": 22,
"cycle_count": 0
}
Key Fields:
* transitive_coverage: Fraction of pairs with known ordering (0.0-1.0), including those inferred via transitivity. Primary progress metric.
* effective_confidence: Combined confidence score considering transitivity, Bayesian updates, and consistency.
* uncertain_pairs: Number of pairs whose ordering is still unknown.
* theoretical_minimum: Information-theoretic lower bound: ⌈log₂(N!)⌉
* practical_estimate: Expected comparisons needed for target: ~0.77 × N × log₂(N) for 90% target.
* cycle_count: Number of detected logical inconsistencies (A>B>C>A cycles).
Reset Comparisons
POST /api/v1/projects/{project_id}/comparisons/reset
Reset all comparisons for a project or dimension.
Parameters:
* project_id (string, required): The UUID of the project.
Request Body:
Response (200 OK):
Undo Last Comparison
POST /api/v1/projects/{project_id}/comparisons/undo
Undo the last comparison made. Returns updated progress metrics for immediate UI feedback.
Parameters:
* project_id (string, required): The UUID of the project.
Request Body:
Response (200 OK):
{
"undone_comparison_id": "uuid",
"message": "Comparison undone",
"updated_progress": {
"comparisons_done": 14,
"progress_percent": 75.5
}
}
The updated_progress field provides post-undo progress metrics so the UI can update without an additional API call:
- comparisons_done: Number of comparisons remaining after undo
- progress_percent: Current progress percentage toward target certainty
Response (404 Not Found): * No comparisons to undo.
Skip Comparison
POST /api/v1/projects/{project_id}/comparisons/skip
Skip a comparison pair without recording a result.
Parameters:
* project_id (string, required): The UUID of the project.
Request Body:
Response (200 OK):
Get Comparison
GET /api/v1/projects/{project_id}/comparisons/{comparison_id}
Get comparison by ID.
Parameters:
* project_id (string, required): The UUID of the project.
* comparison_id (string, required): The UUID of the comparison.
Response (200 OK): * Returns a Comparison object.
Update Comparison
PUT /api/v1/projects/{project_id}/comparisons/{comparison_id}
Update a comparison result.
Parameters:
* project_id (string, required): The UUID of the project.
* comparison_id (string, required): The UUID of the comparison.
Request Body:
Response (200 OK): * Returns the updated Comparison object.
Delete Comparison
DELETE /api/v1/projects/{project_id}/comparisons/{comparison_id}
Delete a comparison.
Parameters:
* project_id (string, required): The UUID of the project.
* comparison_id (string, required): The UUID of the comparison.
Response (204 No Content): * Comparison deleted successfully.
Statistics
All statistics endpoints are nested under projects: /api/v1/projects/{project_id}/statistics
Get Project Statistics
GET /api/v1/projects/{project_id}/statistics
Get comprehensive statistics for a project.
Parameters:
* project_id (string, required): The UUID of the project.
Response (200 OK):
{
"feature_count": 0,
"comparison_count": 0,
"dimensions": {
"complexity": {
"comparisons": 0,
"progress": 0.0
},
"value": {
"comparisons": 0,
"progress": 0.0
}
}
}
Get Feature Scores
GET /api/v1/projects/{project_id}/statistics/scores
Get computed scores for all features in a project.
Parameters:
* project_id (string, required): The UUID of the project.
Response (200 OK):
[
{
"feature_id": "uuid",
"feature_name": "string",
"complexity_score": 0.0,
"value_score": 0.0,
"ratio": 0.0
}
]
Results
All results endpoints are nested under projects: /api/v1/projects/{project_id}/results
Get Ranked Results
GET /api/v1/projects/{project_id}/results
Get ranked and scored results for all features.
Parameters:
* project_id (string, required): The UUID of the project.
* sort_by (string, query, optional): One of "complexity", "value", "ratio".
* include_quadrants (boolean, query, optional): When true, includes quadrant analysis in the response.
Response (200 OK):
[
{
"feature": { ...Feature Object... },
"complexity": 0.0,
"value": 0.0,
"ratio": 0.0,
"rank": 1
}
]
When include_quadrants=true, the response format changes to include both results and quadrant analysis:
{
"results": [
{
"feature": { ...Feature Object... },
"complexity": 0.0,
"value": 0.0,
"ratio": 0.0,
"rank": 1
}
],
"quadrants": {
"quick_wins": [ ...Feature Objects... ],
"major_projects": [ ...Feature Objects... ],
"fill_ins": [ ...Feature Objects... ],
"thankless_tasks": [ ...Feature Objects... ]
}
}
This allows fetching both ranked results and quadrant categorization in a single API call.
Get Quadrant Analysis
GET /api/v1/projects/{project_id}/results/quadrants
Get features organized into quadrants based on complexity and value.
Parameters:
* project_id (string, required): The UUID of the project.
Response (200 OK):
{
"quick_wins": [ ...Feature Objects... ],
"major_projects": [ ...Feature Objects... ],
"fill_ins": [ ...Feature Objects... ],
"thankless_tasks": [ ...Feature Objects... ]
}
Export Results
GET /api/v1/projects/{project_id}/results/export
Export project results in various formats.
Parameters:
* project_id (string, required): The UUID of the project.
* format (string, query, optional): One of "json", "csv". Default: "json".
* sort_by (string, query, optional): One of "complexity", "value", "ratio".
Response (200 OK): * Content-Type: application/json or text/csv depending on format parameter.
Model Configuration
All model configuration endpoints are nested under projects: /api/v1/projects/{project_id}/model-config
Get Model Configuration
GET /api/v1/projects/{project_id}/model-config
Get the current model configuration for a project.
Parameters:
* project_id (string, required): The UUID of the project.
Response (200 OK):
{
"model_type": "string",
"num_features": 0,
"selection_strategy": "string",
"dimensions": {
"complexity": {
"prior_mean": 0.0,
"prior_variance": 1.0,
"target_variance": 0.1
},
"value": {
"prior_mean": 0.0,
"prior_variance": 1.0,
"target_variance": 0.1
}
}
}
Update Model Configuration
PUT /api/v1/projects/{project_id}/model-config
Update the model configuration for a project.
Parameters:
* project_id (string, required): The UUID of the project.
Request Body:
{
"model_type": "string",
"selection_strategy": "string",
"dimensions": {
"complexity": {
"prior_mean": 0.0,
"prior_variance": 1.0,
"target_variance": 0.1
}
}
}
Response (200 OK):
{
"model_type": "string",
"num_features": 0,
"selection_strategy": "string",
"updated_at": "datetime"
}
Response (400 Bad Request): * Invalid configuration parameters.
Preview Configuration Impact
POST /api/v1/projects/{project_id}/model-config/preview
Preview the impact of configuration changes without applying them.
Parameters:
* project_id (string, required): The UUID of the project.
Request Body:
Response (200 OK):
Reset Model Configuration
POST /api/v1/projects/{project_id}/model-config/reset
Reset model configuration to default values.
Parameters:
* project_id (string, required): The UUID of the project.
Response (200 OK):
Admin
All admin endpoints require superuser privileges and are prefixed with /api/v1/admin
Create Database Backup
POST /api/v1/admin/database/backup
Create a backup of the database.
Response (200 OK):
List Database Backups
GET /api/v1/admin/database/backups
List all available database backups.
Response (200 OK):
Download Backup
GET /api/v1/admin/database/backups/{backup_id}
Download a specific backup file.
Parameters:
* backup_id (string, required): The ID of the backup.
Response (200 OK): * Content-Type: application/octet-stream * Binary backup file.
Restore Database
POST /api/v1/admin/database/restore
Restore database from a backup.
Request Body:
Response (200 OK):
Response (503 Service Unavailable): * Service temporarily unavailable during restore.
Get Database Statistics
GET /api/v1/admin/database/stats
Get database statistics and health metrics.
Response (200 OK):
{
"size_bytes": 0,
"table_counts": {
"users": 0,
"projects": 0,
"features": 0,
"comparisons": 0
},
"last_vacuum": "datetime",
"integrity_ok": true
}
Run Database Maintenance
POST /api/v1/admin/database/maintenance
Run database maintenance operations.
Request Body:
Response (200 OK):
Export Database
GET /api/v1/admin/database/export
Bulk export data from the database.
Parameters:
* project_id (string, query, optional): Export specific project only.
* format (string, query, optional): One of "json", "sql". Default: "json".
Response (200 OK): * Content-Type: application/json or application/sql depending on format.
Import Database
POST /api/v1/admin/database/import
Bulk import data into the database.
Request Body (multipart/form-data):
* file (file, required): The import file.
Response (200 OK):
Data Objects
User Object
{
"id": "uuid",
"username": "string",
"email": "string",
"is_active": true,
"is_superuser": false,
"role": "user" | "root",
"display_name": "string",
"avatar_url": "string"
}
Project Object
{
"id": "uuid",
"name": "string",
"description": "string",
"comparison_mode": "binary" | "graded",
"created_at": "datetime",
"owner_id": "uuid"
}
Feature Object
{
"id": "uuid",
"name": "string",
"description": "string",
"tags": ["string"],
"project_id": "uuid",
"created_at": "datetime",
"updated_at": "datetime"
}
Comparison Object
{
"id": "uuid",
"feature_a_id": "uuid",
"feature_b_id": "uuid",
"choice": "feature_a" | "feature_b" | "tie",
"strength": "a_much_better" | "a_better" | "equal" | "b_better" | "b_much_better" | null,
"dimension": "complexity" | "value",
"project_id": "uuid",
"user_id": "uuid",
"created_at": "datetime"
}
Note: The strength field is only populated for graded comparisons. Binary comparisons have strength: null.
ComparisonWithStats Object
Returned by the Create Comparison endpoint to provide immediate feedback about inconsistencies:
{
"id": "uuid",
"project_id": "uuid",
"feature_a": {
"id": "uuid",
"name": "string"
},
"feature_b": {
"id": "uuid",
"name": "string"
},
"choice": "feature_a" | "feature_b" | "tie",
"strength": "much_better" | "better" | "equal" | "worse" | "much_worse" | null,
"dimension": "complexity" | "value",
"created_at": "datetime",
"inconsistency_stats": {
"cycle_count": 0,
"total_comparisons": 0,
"inconsistency_percentage": 0.0,
"dimension": "complexity" | "value"
}
}