Comprehensive API reference for the Submittal Review Assistant backend
The Submittal Review Assistant API is a RESTful API built with FastAPI that provides endpoints for managing construction projects, specifications, submittals, and AI-powered cross-reference analysis.
Key Features:
- Multi-tenant architecture with company isolation
- Role-based access control (Admin, Project Manager, Contractor, Viewer)
- JWT-based authentication via Supabase
- AI-powered document analysis using OpenAI GPT-4o
- Async document processing with Redis job queue
- Real-time cost tracking and budgeting
All API endpoints (except health checks) require authentication via Supabase JWT tokens.
- Sign in via Supabase:
const { data, error } = await supabase.auth.signInWithPassword({
email: 'user@example.com',
password: 'password'
});
const token = data.session.access_token;- Include token in requests:
Authorization: Bearer <token>After creating a user in Supabase, register them in the system:
POST /api/v1/users/register
Content-Type: application/json
Authorization: Bearer <token>
{
"company_id": "uuid-of-company",
"full_name": "John Doe",
"role": "project_manager"
}Development: http://localhost:8000
Production: https://your-domain.com
API Version: All endpoints are prefixed with /api/v1 except health checks.
{
"id": "uuid",
"name": "Project Name",
"created_at": "2026-01-31T12:00:00Z",
...
}{
"items": [...],
"total": 100,
"page": 1,
"page_size": 20
}{
"detail": "Error message describing what went wrong"
}The API uses standard HTTP status codes:
| Code | Meaning | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 400 | Bad Request | Invalid request data |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource not found |
| 409 | Conflict | Resource already exists |
| 422 | Unprocessable Entity | Validation error |
| 500 | Internal Server Error | Server error |
Check if the API is running.
GET /healthResponse:
{
"status": "healthy",
"timestamp": "2026-01-31T12:00:00Z"
}Check if the API is ready to serve requests (database connected).
GET /health/readyResponse:
{
"status": "ready",
"database": "connected",
"timestamp": "2026-01-31T12:00:00Z"
}Check if the API is alive (for Kubernetes probes).
GET /health/liveResponse:
{
"status": "alive"
}Get the authenticated user's profile.
GET /api/v1/users/me
Authorization: Bearer <token>Response:
{
"id": "uuid",
"supabase_user_id": "uuid",
"company_id": "uuid",
"full_name": "John Doe",
"email": "john@example.com",
"role": "project_manager",
"is_active": true,
"created_at": "2026-01-31T12:00:00Z",
"updated_at": "2026-01-31T12:00:00Z"
}List all users in the company.
GET /api/v1/users?skip=0&limit=20
Authorization: Bearer <token>Query Parameters:
skip(optional): Number of records to skip (default: 0)limit(optional): Number of records to return (default: 20, max: 100)
Response:
[
{
"id": "uuid",
"full_name": "John Doe",
"email": "john@example.com",
"role": "project_manager",
"is_active": true
},
...
]Create a new user in the system.
POST /api/v1/users
Authorization: Bearer <token>
Content-Type: application/json
{
"supabase_user_id": "uuid-from-supabase",
"company_id": "uuid",
"full_name": "Jane Smith",
"email": "jane@example.com",
"role": "contractor"
}Roles: admin, project_manager, contractor, viewer
Response: 201 Created
{
"id": "uuid",
"supabase_user_id": "uuid",
"company_id": "uuid",
"full_name": "Jane Smith",
"email": "jane@example.com",
"role": "contractor",
"is_active": true,
"created_at": "2026-01-31T12:00:00Z"
}Get all projects the user has access to.
GET /api/v1/projects?skip=0&limit=20
Authorization: Bearer <token>Response:
[
{
"id": "uuid",
"company_id": "uuid",
"name": "Downtown Office Building",
"number": "PROJ-2026-001",
"location": "123 Main St, City, State",
"description": "New construction office building",
"is_active": true,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-31T12:00:00Z"
},
...
]Create a new project.
POST /api/v1/projects
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Downtown Office Building",
"number": "PROJ-2026-001",
"location": "123 Main St, City, State",
"description": "New construction office building"
}Response: 201 Created
Get a specific project by ID.
GET /api/v1/projects/{project_id}
Authorization: Bearer <token>Update project details.
PATCH /api/v1/projects/{project_id}
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Updated Project Name",
"is_active": false
}Delete a project (soft delete).
DELETE /api/v1/projects/{project_id}
Authorization: Bearer <token>Response: 204 No Content
Get all spec sections for a project.
GET /api/v1/spec-sections?project_id=<uuid>&skip=0&limit=20
Authorization: Bearer <token>Query Parameters:
project_id(required): Filter by projectskip,limit: Pagination
Response:
[
{
"id": "uuid",
"project_id": "uuid",
"section_number": "23 05 00",
"title": "Common Work Results for HVAC",
"description": "General HVAC requirements",
"created_at": "2026-01-20T10:00:00Z"
},
...
]Create a new specification section.
POST /api/v1/spec-sections
Authorization: Bearer <token>
Content-Type: application/json
{
"project_id": "uuid",
"section_number": "23 05 00",
"title": "Common Work Results for HVAC",
"description": "General HVAC requirements"
}Upload a specification document (PDF, Word, etc.).
POST /api/v1/documents/spec/{spec_section_id}
Authorization: Bearer <token>
Content-Type: multipart/form-data
file=@specification.pdfResponse: 201 Created
{
"id": "uuid",
"filename": "specification.pdf",
"file_size": 1048576,
"mime_type": "application/pdf",
"storage_path": "specs/uuid/specification.pdf"
}Download a specification document.
GET /api/v1/documents/spec/{spec_document_id}
Authorization: Bearer <token>Response: Binary file with appropriate Content-Type header
Get all submittals for a project.
GET /api/v1/submittals?project_id=<uuid>&skip=0&limit=20
Authorization: Bearer <token>Response:
[
{
"id": "uuid",
"project_id": "uuid",
"spec_section_id": "uuid",
"submittal_number": "SUB-001",
"title": "HVAC Equipment Schedule",
"description": "Equipment data sheets",
"status": "pending_review",
"submitted_by": "uuid",
"submitted_date": "2026-01-25T10:00:00Z",
"created_at": "2026-01-25T10:00:00Z"
},
...
]Submittal Statuses:
draftsubmittedpending_reviewunder_reviewreviewedapprovedapproved_as_notedrejectedrevise_and_resubmit
Create a new submittal.
POST /api/v1/submittals
Authorization: Bearer <token>
Content-Type: application/json
{
"project_id": "uuid",
"spec_section_id": "uuid",
"submittal_number": "SUB-001",
"title": "HVAC Equipment Schedule",
"description": "Equipment data sheets",
"status": "submitted"
}Upload a submittal document.
POST /api/v1/documents/submittal/{submittal_revision_id}
Authorization: Bearer <token>
Content-Type: multipart/form-data
file=@submittal.pdfProcess a document synchronously and return extracted text.
POST /api/v1/processing/upload
Authorization: Bearer <token>
Content-Type: multipart/form-data
file=@document.pdfResponse:
{
"text": "Extracted text content...",
"page_count": 10,
"processing_time": 2.5,
"method": "pymupdf"
}Queue a document for background processing.
POST /api/v1/processing/upload/async
Authorization: Bearer <token>
Content-Type: multipart/form-data
file=@document.pdfResponse:
{
"job_id": "uuid",
"status": "queued",
"message": "Document processing job queued"
}Check the status of a processing job.
GET /api/v1/processing/status/{job_id}
Authorization: Bearer <token>Response:
{
"job_id": "uuid",
"status": "completed",
"progress": 100,
"message": "Processing completed successfully",
"created_at": "2026-01-31T12:00:00Z",
"completed_at": "2026-01-31T12:01:30Z"
}Job Statuses: queued, processing, completed, failed
Get the result of a completed processing job.
GET /api/v1/processing/result/{job_id}
Authorization: Bearer <token>Response:
{
"text": "Extracted text...",
"page_count": 10,
"processing_time": 90.5
}Start an AI-powered cross-reference review.
POST /api/v1/reviews
Authorization: Bearer <token>
Content-Type: application/json
{
"submittal_id": "uuid",
"spec_section_ids": ["uuid1", "uuid2"],
"review_mode": "standard"
}Review Modes:
quick: Fast review with GPT-4o-ministandard: Balanced review with mixed modelsthorough: Detailed review with GPT-4o
Response: 201 Created
{
"id": "uuid",
"submittal_id": "uuid",
"status": "completed",
"total_findings": 15,
"compliant": 12,
"non_compliant": 2,
"missing": 1,
"ambiguous": 0,
"total_cost_usd": 0.45,
"created_at": "2026-01-31T12:00:00Z",
"completed_at": "2026-01-31T12:02:30Z"
}Get a review with all findings.
GET /api/v1/reviews/{review_id}
Authorization: Bearer <token>Response:
{
"id": "uuid",
"submittal_id": "uuid",
"status": "completed",
"findings": [
{
"id": "uuid",
"requirement_text": "Equipment shall meet ASHRAE 90.1 efficiency standards",
"submittal_data": "EER: 12.5, meets ASHRAE 90.1-2019",
"compliance_status": "compliant",
"confidence_score": 0.95,
"ai_explanation": "The submittal clearly states compliance...",
"severity": "medium"
},
...
],
"summary": {
"total_findings": 15,
"compliant": 12,
"non_compliant": 2,
"missing": 1
}
}List findings for a review with filtering.
GET /api/v1/reviews/{review_id}/findings?status=non_compliant&severity=high
Authorization: Bearer <token>Query Parameters:
status: Filter by compliance status (compliant,non_compliant,missing,ambiguous)severity: Filter by severity (low,medium,high,critical)skip,limit: Pagination
Update a finding (human review/override).
PATCH /api/v1/reviews/findings/{finding_id}
Authorization: Bearer <token>
Content-Type: application/json
{
"human_review_status": "confirmed",
"human_review_notes": "Verified with engineer",
"compliance_status": "compliant"
}Get real-time cost tracking dashboard.
GET /api/v1/reviews/costs/dashboard
Authorization: Bearer <token>Response:
{
"today_cost_usd": 12.50,
"month_cost_usd": 450.75,
"daily_budget_usd": 50.00,
"monthly_budget_usd": 1000.00,
"daily_budget_remaining": 37.50,
"monthly_budget_remaining": 549.25,
"total_reviews_today": 25,
"total_reviews_month": 450
}Check if email notifications are configured.
GET /api/v1/notifications/status
Authorization: Bearer <token>Response:
{
"configured": true,
"email_provider": "resend",
"from_address": "noreply@example.com"
}Send a test email to verify configuration.
POST /api/v1/notifications/test
Authorization: Bearer <token>
Content-Type: application/json
{
"to": "test@example.com"
}Response:
{
"success": true,
"message": "Test email sent successfully",
"email_id": "re_abc123xyz"
}Send an email notification when a review is complete.
POST /api/v1/notifications/review-complete
Authorization: Bearer <token>
Content-Type: application/json
{
"to": "pm@example.com",
"project_name": "Downtown Office Building",
"submittal_number": "SUB-001",
"submittal_title": "HVAC Equipment",
"compliance_status": "compliant",
"compliant_count": 12,
"noncompliant_count": 2,
"missing_count": 1,
"ambiguous_count": 0,
"review_url": "https://app.example.com/reviews/uuid"
}To ensure fair usage and system stability, the following rate limits apply:
| Endpoint Category | Limit | Window |
|---|---|---|
| Document Processing | 100 requests | 1 hour |
| AI Reviews | 50 reviews | 24 hours |
| General API | 1000 requests | 1 hour |
Rate limit headers are included in responses:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1706716800
Swagger UI: Visit /docs for interactive API documentation where you can test endpoints directly.
ReDoc: Visit /redoc for alternative API documentation with a cleaner reading experience.
import requests
BASE_URL = "http://localhost:8000"
TOKEN = "your-jwt-token"
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json"
}
# Create a project
response = requests.post(
f"{BASE_URL}/api/v1/projects",
headers=headers,
json={
"name": "Test Project",
"number": "PROJ-001",
"location": "New York, NY"
}
)
project = response.json()
print(f"Created project: {project['id']}")
# Start an AI review
response = requests.post(
f"{BASE_URL}/api/v1/reviews",
headers=headers,
json={
"submittal_id": "uuid",
"spec_section_ids": ["uuid1"],
"review_mode": "standard"
}
)
review = response.json()
print(f"Review status: {review['status']}")const BASE_URL = 'http://localhost:8000';
const TOKEN = 'your-jwt-token';
const headers = {
'Authorization': `Bearer ${TOKEN}`,
'Content-Type': 'application/json'
};
// Get current user
const response = await fetch(`${BASE_URL}/api/v1/users/me`, { headers });
const user = await response.json();
console.log(`Logged in as: ${user.full_name}`);
// List projects
const projectsResponse = await fetch(`${BASE_URL}/api/v1/projects`, { headers });
const projects = await projectsResponse.json();
console.log(`Found ${projects.length} projects`);Webhook support is planned for future releases to enable real-time notifications for:
- Review completion
- Document processing completion
- Cost threshold alerts
- GitHub: https://github.com/jesusr04/Submittal_Review
- Issues: https://github.com/jesusr04/Submittal_Review/issues
- Discussions: https://github.com/jesusr04/Submittal_Review/discussions
Last updated: January 31, 2026