API Endpoints¶
tuvl automatically generates REST API endpoints for workflows and models.
Workflow Endpoints¶
Workflows with HTTP triggers become API endpoints:
This creates:
Request Format¶
curl -X POST http://localhost:8000/api/onboard \
-H "Content-Type: application/json" \
-d '{
"email": "jane@example.com",
"name": "Jane Doe"
}'
Response Format¶
All workflow responses follow this structure:
{
"success": true,
"status_code": 200,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "jane@example.com",
"name": "Jane Doe",
"status": "completed"
},
"error": null
}
Error Response¶
{
"success": false,
"status_code": 400,
"data": {
"email": "jane@example.com"
},
"error": {
"message": "Workflow execution halted",
"details": "Validation failed: invalid email format"
}
}
CRUD Endpoints¶
Each model automatically gets CRUD endpoints:
| Method | Path | Description |
|---|---|---|
POST |
/api/{model} |
Create record |
GET |
/api/{model} |
List records |
GET |
/api/{model}/{id} |
Get record |
PATCH |
/api/{model}/{id} |
Update record |
DELETE |
/api/{model}/{id} |
Delete record |
Create¶
curl -X POST http://localhost:8000/api/contact \
-H "Content-Type: application/json" \
-d '{
"email": "jane@example.com",
"name": "Jane Doe",
"company": "Acme Inc"
}'
Response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "jane@example.com",
"name": "Jane Doe",
"company": "Acme Inc",
"created_at": "2024-01-15T10:30:00Z"
}
List¶
# List all (paginated)
curl http://localhost:8000/api/contact
# With filters
curl "http://localhost:8000/api/contact?company=Acme&limit=10&offset=0"
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit |
integer | 100 | Max records |
offset |
integer | 0 | Skip records |
{field} |
varies | - | Filter by field value |
Get Single¶
Update¶
curl -X PATCH http://localhost:8000/api/contact/550e8400-... \
-H "Content-Type: application/json" \
-d '{
"company": "New Company Name"
}'
Delete¶
API Documentation¶
FastAPI auto-generates interactive documentation:
| URL | Format |
|---|---|
/docs |
Swagger UI |
/redoc |
ReDoc |
/openapi.json |
OpenAPI spec |
Authentication¶
tuvl has a built-in IAM system based on Biscuit tokens. All /auth/* endpoints are
served under the prefix /auth. See IAM and
Tokens for full documentation.
Bootstrap¶
One-time setup to create the first superadmin user. Disabled once any admin exists.
Login¶
Standard OAuth2 password grant. Returns a Biscuit bearer token.
Use the token in subsequent requests:
Refresh Token¶
Exchange a valid token for a new one (old token is revoked):
Logout¶
Revoke the current token immediately:
Response: 204 No Content
Admin — Users¶
Requires scope iam:admin.
| Method | Path | Description |
|---|---|---|
GET |
/auth/admin/users |
List all users |
POST |
/auth/admin/users |
Create a user |
GET |
/auth/admin/users/{id} |
Get a user |
PATCH |
/auth/admin/users/{id} |
Update email / password / active |
DELETE |
/auth/admin/users/{id} |
Delete a user |
{
"email": "new@example.com",
"password": "newpassword",
"is_active": false
}
Admin — Roles¶
Requires scope iam:admin.
| Method | Path | Description |
|---|---|---|
GET |
/auth/admin/roles |
List all roles |
POST |
/auth/admin/roles |
Create a role |
DELETE |
/auth/admin/roles/{id} |
Delete a role |
PATCH |
/auth/admin/roles/{id}/scopes |
Add or remove scopes |
{
"add": ["data:read", "models:read"],
"remove": ["iam:admin"]
}
Admin — Role Assignments¶
Requires scope iam:admin.
| Method | Path | Description |
|---|---|---|
POST |
/auth/admin/users/{user_id}/roles/{role_id} |
Assign role to user |
DELETE |
/auth/admin/users/{user_id}/roles/{role_id} |
Remove role from user |
OAuth2 Federation¶
Social login via configured providers. See Federation.
| Method | Path | Description |
|---|---|---|
GET |
/auth/oauth/{provider}/start |
Redirect to provider |
GET |
/auth/oauth/{provider}/callback |
OAuth2 callback (browser) |
Admin — Federation Providers¶
Requires scope iam:admin.
| Method | Path | Description |
|---|---|---|
GET |
/auth/admin/federation |
List provider configs |
GET |
/auth/admin/federation/{name} |
Get a provider config |
PUT |
/auth/admin/federation/{name} |
Create / update a provider |
DELETE |
/auth/admin/federation/{name} |
Delete a provider |
Token in Dev Mode¶
In dev mode (tuvl dev) no Authorization header is required. The dev middleware
auto-injects a session key that grants all scopes. You can still pass a real token to
test IAM flows.
Version Management Admin API¶
The admin endpoints let you inspect, enable/disable, and fork versioned workflow and model definitions at runtime — no YAML edits or server restarts required.
Workflow version endpoints¶
| Method | Path | Description |
|---|---|---|
GET |
/admin/workflows |
List all workflow versions grouped by name |
PATCH |
/admin/workflows/{name}/{version}/toggle |
Toggle enabled flag |
POST |
/admin/workflows/{name}/{version}/fork |
Fork a version to a new YAML file |
GET /admin/workflows¶
Returns all versions from the workflow_versions table grouped by workflow name.
{
"onboard": [
{
"schema_version": "v1",
"enabled": true,
"trigger_path": "/api/onboard",
"trigger_method": "POST",
"description": "Initial onboarding flow"
},
{
"schema_version": "v2",
"enabled": false,
"trigger_path": "/api/onboard",
"trigger_method": "POST",
"description": null
}
]
}
PATCH /admin/workflows/{name}/{version}/toggle¶
Flips the enabled field for the given (name, schema_version) pair. Returns 404
if no such row exists.
POST /admin/workflows/{name}/{version}/fork¶
Deep-copies the config of an existing version, stamps it with new_version, writes it
to workflows/{name}_{new_version}.yaml, and returns the new file details.
{
"name": "onboard",
"source_version": "v1",
"new_version": "v2",
"file": "onboard_v2.yaml"
}
Returns 404 if the source (name, version) is not found in the in-memory registry or
in the database.
Model version endpoints¶
| Method | Path | Description |
|---|---|---|
GET |
/admin/models |
List all model versions grouped by name |
PATCH |
/admin/models/{name}/{version}/toggle |
Toggle enabled flag |
POST |
/admin/models/{name}/{version}/fork |
Fork a version to a new YAML file |
GET /admin/models¶
Returns all versions from the model_versions table grouped by model name.
{
"Candidate": [
{
"schema_version": "v1",
"enabled": true,
"tablename": "candidates",
"datasource": "main_postgres",
"fields": ["id", "name", "email"],
"description": null
},
{
"schema_version": "v2",
"enabled": false,
"tablename": "candidates",
"datasource": "main_postgres",
"fields": ["id", "name", "email", "tags"],
"description": null
}
]
}
PATCH /admin/models/{name}/{version}/toggle¶
Flips enabled for the given model version. Returns 404 if not found.
POST /admin/models/{name}/{version}/fork¶
Deep-copies the model config, stamps new_version, and writes it to
models/{name_lower}_{new_version}.yaml.
{
"name": "Candidate",
"source_version": "v2",
"new_version": "v3",
"file": "candidate_v3.yaml"
}
Error Handling¶
HTTP Status Codes¶
| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad request / validation error |
| 404 | Resource not found |
| 422 | Unprocessable entity |
| 500 | Server error |
Error Response Structure¶
{
"success": false,
"status_code": 400,
"data": null,
"error": {
"message": "Brief error description",
"details": "Detailed error information"
}
}