Complete API documentation for Resuvio-AI backend.
| Environment | URL |
|---|---|
| Development | http://localhost:3001/api |
| Staging | https://api-staging.resuvio.ai/api |
| Production | https://resuvio-ai.onrender.com/api |
All protected endpoints require a Firebase ID token in the Authorization header:
Authorization: Bearer <firebase-id-token>import { getAuth } from 'firebase/auth';
const auth = getAuth();
const user = auth.currentUser;
const token = await user.getIdToken();
// Use token in API callsTokens are verified using Firebase Admin SDK. The decoded token is attached to req.user:
req.user = {
uid: 'user-uid',
email: 'user@example.com',
email_verified: true,
auth_time: 1234567890,
// ... other claims
};{
"success": false,
"error": "Human-readable error message",
"code": "ERROR_CODE",
"details": {}
}| Code | Meaning |
|---|---|
| 200 | Success |
| 201 | Created |
| 400 | Bad Request (validation error) |
| 401 | Unauthorized (missing/invalid token) |
| 403 | Forbidden (insufficient permissions) |
| 404 | Not Found |
| 409 | Conflict (duplicate resource) |
| 422 | Unprocessable Entity (business logic error) |
| 429 | Too Many Requests (rate limited) |
| 500 | Internal Server Error |
| 503 | Service Unavailable |
| Code | Description |
|---|---|
UNAUTHORIZED |
Missing or invalid authentication token |
FORBIDDEN |
Insufficient permissions for resource |
NOT_FOUND |
Resource not found |
VALIDATION_ERROR |
Request body validation failed |
QUOTA_EXCEEDED |
Credit limit reached |
FILE_TOO_LARGE |
Uploaded file exceeds size limit |
INVALID_FILE_TYPE |
Unsupported file format |
AI_SERVICE_ERROR |
Gemini API error |
PAYMENT_FAILED |
Payment processing error |
| Endpoint Category | Limit |
|---|---|
| Authentication | 10 req/min |
| Resume Upload | 5 req/min |
| AI Analysis | 10 req/min |
| Job Matching | 10 req/min |
| Cover Letter | 10 req/min |
| Resume Builder | 30 req/min |
| Public (tips) | 30 req/min |
| General API | 100 req/min |
Rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1699999999Register a new user.
Request:
{
"email": "user@example.com",
"password": "securePassword123",
"displayName": "John Doe"
}Response (201):
{
"success": true,
"data": {
"uid": "firebase-uid",
"email": "user@example.com",
"displayName": "John Doe",
"emailVerified": false
},
"message": "User created. Please verify your email."
}Login is handled client-side via Firebase SDK. This endpoint returns 501.
Response (501):
{
"success": false,
"error": "Login is handled client-side via Firebase SDK",
"code": "NOT_IMPLEMENTED"
}Upload and parse a resume file (PDF or DOCX).
Headers:
Content-Type: multipart/form-data
Authorization: Bearer <token>
Body: Form data with resume file field
Response (201):
{
"success": true,
"data": {
"resumeId": "resume-abc123",
"fileName": "john-doe-resume.pdf",
"fileType": "pdf",
"fileSize": 245760,
"extractedText": "JOHN DOE\nSoftware Engineer\n...",
"createdAt": "2024-01-15T10:30:00Z"
}
}Errors:
400- No file provided413- File too large (>5MB)415- Invalid file type (not PDF/DOCX)422- Failed to parse file
List user's uploaded resumes.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| page | number | 1 | Page number |
| limit | number | 10 | Items per page (max 50) |
| sortBy | string | createdAt | Sort field |
| sortOrder | string | desc | asc/desc |
Response (200):
{
"success": true,
"data": [
{
"resumeId": "resume-abc123",
"fileName": "john-doe-resume.pdf",
"fileType": "pdf",
"fileSize": 245760,
"createdAt": "2024-01-15T10:30:00Z",
"hasAnalysis": true
}
],
"pagination": {
"page": 1,
"limit": 10,
"total": 1,
"totalPages": 1
}
}Get resume details including extracted text.
Response (200):
{
"success": true,
"data": {
"resumeId": "resume-abc123",
"fileName": "john-doe-resume.pdf",
"fileType": "pdf",
"fileSize": 245760,
"extractedText": "JOHN DOE\nSoftware Engineer\n...",
"createdAt": "2024-01-15T10:30:00Z",
"analysis": {
"analysisId": "analysis-xyz789",
"score": 85,
"feedback": "Strong technical background...",
"suggestions": ["Add more metrics", "Include keywords"],
"keywords": ["React", "Node.js", "TypeScript"],
"createdAt": "2024-01-15T11:00:00Z"
}
}
}Analyze resume with AI.
Response (200):
{
"success": true,
"data": {
"analysisId": "analysis-xyz789",
"score": 85,
"feedback": "Strong technical background with good project diversity...",
"suggestions": [
"Quantify achievements with metrics",
"Add more industry-specific keywords",
"Include leadership experience"
],
"keywords": ["React", "Node.js", "TypeScript", "AWS", "Docker"],
"sections": {
"experience": "Good depth, consider adding impact metrics",
"education": "Complete",
"skills": "Well-organized, consider grouping by category",
"projects": "Impressive variety"
},
"creditsUsed": 5,
"creditsRemaining": 95,
"createdAt": "2024-01-15T11:00:00Z"
}
}Errors:
402- Insufficient credits404- Resume not found503- AI service unavailable
Delete a resume and its analyses.
Response (200):
{
"success": true,
"message": "Resume deleted successfully"
}Create or update a resume workspace (draft).
Request:
{
"title": "Senior Software Engineer Resume",
"template": "modern",
"data": {
"personalInfo": {
"fullName": "John Doe",
"email": "john@example.com",
"phone": "+1-555-0123",
"location": "San Francisco, CA",
"linkedin": "linkedin.com/in/johndoe",
"github": "github.com/johndoe",
"website": "johndoe.dev"
},
"summary": "Experienced software engineer...",
"experience": [
{
"id": "exp-1",
"company": "Tech Corp",
"position": "Senior Software Engineer",
"location": "San Francisco, CA",
"startDate": "2022-01",
"endDate": "present",
"current": true,
"description": "Led team of 5 engineers...",
"achievements": [
"Improved API performance by 40%",
"Mentored 3 junior developers"
]
}
],
"education": [
{
"id": "edu-1",
"institution": "Stanford University",
"degree": "Master of Science",
"field": "Computer Science",
"location": "Stanford, CA",
"startDate": "2018-09",
"endDate": "2020-06"
}
],
"skills": [
{ "category": "Frontend", "items": ["React", "TypeScript", "Next.js"] },
{ "category": "Backend", "items": ["Node.js", "Python", "PostgreSQL"] }
],
"projects": [],
"certifications": [],
"languages": []
}
}Response (201):
{
"success": true,
"data": {
"workspaceId": "workspace-abc123",
"title": "Senior Software Engineer Resume",
"template": "modern",
"isDraft": true,
"createdAt": "2024-01-15T12:00:00Z",
"updatedAt": "2024-01-15T12:00:00Z"
}
}List user's resume workspaces.
Response (200):
{
"success": true,
"data": [
{
"workspaceId": "workspace-abc123",
"title": "Senior Software Engineer Resume",
"template": "modern",
"isDraft": true,
"updatedAt": "2024-01-15T12:00:00Z"
}
]
}Get a specific workspace.
Response (200):
{
"success": true,
"data": {
"workspaceId": "workspace-abc123",
"title": "Senior Software Engineer Resume",
"template": "modern",
"data": { ... },
"isDraft": true,
"createdAt": "2024-01-15T12:00:00Z",
"updatedAt": "2024-01-15T14:30:00Z"
}
}Delete a workspace.
Response (200):
{
"success": true,
"message": "Workspace deleted"
}Duplicate a workspace.
Response (201):
{
"success": true,
"data": {
"workspaceId": "workspace-def456",
"title": "Senior Software Engineer Resume (Copy)",
"template": "modern",
"isDraft": true,
"createdAt": "2024-01-15T15:00:00Z"
}
}Generate a complete resume from scratch using AI.
Request:
{
"targetRole": "Senior Frontend Engineer",
"experienceLevel": "senior",
"skills": ["React", "TypeScript", "Next.js", "GraphQL"],
"preferences": {
"template": "modern",
"tone": "professional"
}
}Response (200):
{
"success": true,
"data": {
"workspaceId": "workspace-new789",
"title": "Senior Frontend Engineer Resume",
"template": "modern",
"data": { ... },
"creditsUsed": 5,
"creditsRemaining": 90
}
}Get AI assistance for a specific section.
Request:
{
"section": "experience",
"context": {
"currentContent": "Worked on React projects",
"targetRole": "Senior Frontend Engineer"
},
"instruction": "Make this more impactful with metrics"
}Response (200):
{
"success": true,
"data": {
"suggestion": "Led development of 3 React applications serving 100K+ users, improving load times by 40% through code splitting and lazy loading",
"creditsUsed": 1,
"creditsRemaining": 89
}
}Parse an uploaded resume and populate builder fields.
Headers:
Content-Type: multipart/form-data
Authorization: Bearer <token>
Body: Form data with resume file field
Response (200):
{
"success": true,
"data": {
"workspaceId": "workspace-parsed123",
"title": "Parsed Resume - John Doe",
"template": "modern",
"data": { ... },
"creditsUsed": 2,
"creditsRemaining": 87
}
}Download resume as PDF.
Response: Binary PDF file with Content-Type: application/pdf
Headers:
Content-Disposition: attachment; filename="resume-john-doe.pdf"
List generated (non-draft) resumes.
Response (200):
{
"success": true,
"data": [
{
"workspaceId": "workspace-final123",
"title": "Senior Software Engineer Resume",
"template": "modern",
"pdfUrl": "https://storage.googleapis.com/.../resume.pdf",
"createdAt": "2024-01-15T12:00:00Z"
}
]
}Match a resume against a job description.
Request:
{
"resumeId": "resume-abc123",
"jobDescription": "We are looking for a Senior Frontend Engineer with 5+ years of React experience...",
"jobTitle": "Senior Frontend Engineer",
"company": "Tech Corp"
}Response (200):
{
"success": true,
"data": {
"matchId": "match-xyz789",
"matchScore": 78,
"summary": "Strong match for Senior Frontend Engineer role...",
"strengths": [
"5+ years React experience",
"TypeScript expertise",
"Performance optimization experience"
],
"gaps": [
"No GraphQL experience mentioned",
"Limited team leadership examples"
],
"recommendations": [
"Add GraphQL projects to resume",
"Highlight mentoring experience",
"Include specific performance metrics"
],
"keywordMatch": {
"matched": ["React", "TypeScript", "JavaScript", "CSS", "REST API"],
"missing": ["GraphQL", "Next.js", "Team Lead", "Mentoring"]
},
"atsScore": 82,
"creditsUsed": 3,
"creditsRemaining": 84,
"createdAt": "2024-01-15T16:00:00Z"
}
}Generate a cover letter for a job application.
Request:
{
"resumeId": "resume-abc123",
"jobDescription": "We are looking for a Senior Frontend Engineer...",
"jobTitle": "Senior Frontend Engineer",
"company": "Tech Corp",
"hiringManager": "Jane Smith",
"tone": "professional",
"length": "medium"
}Response (200):
{
"success": true,
"data": {
"letterId": "letter-abc123",
"content": "Dear Jane Smith,\n\nI am writing to express my interest in the Senior Frontend Engineer position at Tech Corp...",
"jobTitle": "Senior Frontend Engineer",
"company": "Tech Corp",
"creditsUsed": 2,
"creditsRemaining": 82,
"createdAt": "2024-01-15T16:30:00Z"
}
}List user's generated cover letters.
Response (200):
{
"success": true,
"data": [
{
"letterId": "letter-abc123",
"jobTitle": "Senior Frontend Engineer",
"company": "Tech Corp",
"createdAt": "2024-01-15T16:30:00Z"
}
]
}Get resume writing tips (public endpoint, no auth required).
Query Parameters:
| Param | Type | Description |
|---|---|---|
| category | string | Filter by category (general, formatting, content, ats) |
Response (200):
{
"success": true,
"data": [
{
"id": "tip-1",
"title": "Quantify Your Achievements",
"category": "content",
"content": "Use numbers and metrics to demonstrate impact...",
"tags": ["achievements", "metrics", "impact"]
},
{
"id": "tip-2",
"title": "Optimize for ATS",
"category": "ats",
"content": "Include relevant keywords from the job description...",
"tags": ["ats", "keywords", "optimization"]
}
]
}Get user's recent activity.
Query Parameters:
| Param | Type | Default | Description |
|---|---|---|---|
| limit | number | 20 | Number of activities |
| type | string | - | Filter by type |
Response (200):
{
"success": true,
"data": [
{
"activityId": "act-1",
"type": "resume_analysis",
"description": "Analyzed resume 'john-doe-resume.pdf'",
"metadata": {
"resumeId": "resume-abc123",
"score": 85
},
"createdAt": "2024-01-15T11:00:00Z"
},
{
"activityId": "act-2",
"type": "builder_save",
"description": "Saved resume 'Senior Software Engineer Resume'",
"metadata": {
"workspaceId": "workspace-abc123"
},
"createdAt": "2024-01-15T12:00:00Z"
}
]
}Get current user's credit usage.
Response (200):
{
"success": true,
"data": {
"plan": "pro",
"creditsUsed": 18,
"creditsLimit": 500,
"creditsRemaining": 482,
"resetDate": "2024-02-01T00:00:00Z",
"usageByFeature": {
"resume_analysis": 10,
"job_matching": 6,
"cover_letter": 4,
"builder_generate": 15,
"builder_assist": 3,
"builder_parse": 4
},
"history": [
{
"amount": -5,
"type": "usage",
"description": "Resume analysis",
"createdAt": "2024-01-15T11:00:00Z"
}
]
}
}Get credit limits for all plans (public).
Response (200):
{
"success": true,
"data": {
"free": {
"monthlyCredits": 10,
"features": ["resume_analysis", "job_matching", "cover_letter"],
"limits": {
"resume_uploads": 5,
"builder_workspaces": 2
}
},
"pro": {
"monthlyCredits": 500,
"features": ["all"],
"limits": {
"resume_uploads": -1,
"builder_workspaces": -1
}
},
"enterprise": {
"monthlyCredits": -1,
"features": ["all", "api_access", "priority_support"],
"limits": {}
}
}
}Create a Razorpay order for subscription.
Request:
{
"planId": "pro_monthly",
"currency": "INR"
}Response (200):
{
"success": true,
"data": {
"orderId": "order_abc123",
"amount": 99900,
"currency": "INR",
"keyId": "rzp_test_...",
"plan": "pro_monthly"
}
}Verify payment signature after Razorpay checkout.
Request:
{
"razorpayOrderId": "order_abc123",
"razorpayPaymentId": "pay_xyz789",
"razorpaySignature": "signature..."
}Response (200):
{
"success": true,
"data": {
"subscriptionId": "sub_abc123",
"plan": "pro_monthly",
"status": "active",
"currentPeriodEnd": "2024-02-15T10:30:00Z",
"creditsAdded": 500
}
}Get current subscription status.
Response (200):
{
"success": true,
"data": {
"plan": "pro",
"status": "active",
"currentPeriodEnd": "2024-02-15T10:30:00Z",
"cancelAtPeriodEnd": false,
"paymentMethod": "razorpay"
}
}Endpoint: POST /api/payments/webhook
Headers:
X-Razorpay-Signature: <signature>
Content-Type: application/json
Events Handled:
payment.captured- Activate subscriptionpayment.failed- Notify usersubscription.charged- Renew creditssubscription.cancelled- Downgrade plan
// lib/api.ts
import axios from 'axios';
const api = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL + '/api',
});
api.interceptors.request.use((config) => {
const token = localStorage.getItem('firebaseIdToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Resume upload
export const uploadResume = (file: File) => {
const formData = new FormData();
formData.append('resume', file);
return api.post('/resumes/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
});
};
// Resume analysis
export const analyzeResume = (resumeId: string) =>
api.post(`/resumes/${resumeId}/analyze`);
// Job matching
export const matchResumeJob = (data: {
resumeId: string;
jobDescription: string;
jobTitle: string;
company: string;
}) => api.post('/match/resume-job', data);
// Cover letter
export const generateCoverLetter = (data: {
resumeId: string;
jobDescription: string;
jobTitle: string;
company: string;
}) => api.post('/cover-letter/generate', data);import requests
import firebase_admin
from firebase_admin import auth
class ResuvioClient:
def __init__(self, base_url: str, firebase_token: str):
self.base_url = base_url.rstrip('/') + '/api'
self.headers = {
'Authorization': f'Bearer {firebase_token}',
'Content-Type': 'application/json'
}
def upload_resume(self, file_path: str):
with open(file_path, 'rb') as f:
files = {'resume': f}
headers = {'Authorization': self.headers['Authorization']}
return requests.post(
f'{self.base_url}/resumes/upload',
files=files,
headers=headers
).json()
def analyze_resume(self, resume_id: str):
return requests.post(
f'{self.base_url}/resumes/{resume_id}/analyze',
headers=self.headers
).json()
def match_job(self, resume_id: str, job_description: str, job_title: str, company: str):
return requests.post(
f'{self.base_url}/match/resume-job',
headers=self.headers,
json={
'resumeId': resume_id,
'jobDescription': job_description,
'jobTitle': job_title,
'company': company
}
).json()
# Usage
# Get Firebase ID token from your auth flow
firebase_token = "your-firebase-id-token"
client = ResuvioClient("https://resuvio-ai.onrender.com", firebase_token)
result = client.upload_resume("resume.pdf")
analysis = client.analyze_resume(result['data']['resumeId'])# Upload resume
curl -X POST https://resuvio-ai.onrender.com/api/resumes/upload \
-H "Authorization: Bearer YOUR_TOKEN" \
-F "resume=@resume.pdf"
# Analyze resume
curl -X POST https://resuvio-ai.onrender.com/api/resumes/RESUME_ID/analyze \
-H "Authorization: Bearer YOUR_TOKEN"
# Match job
curl -X POST https://resuvio-ai.onrender.com/api/match/resume-job \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"resumeId": "RESUME_ID",
"jobDescription": "Job description here...",
"jobTitle": "Senior Engineer",
"company": "Tech Corp"
}'
# Get credits
curl -X GET https://resuvio-ai.onrender.com/api/credits/usage \
-H "Authorization: Bearer YOUR_TOKEN"| Version | Date | Changes |
|---|---|---|
| 1.0 | 2024-01-15 | Initial API release |
| 1.1 | 2024-02-01 | Added builder assist endpoint |
| 1.2 | 2024-03-01 | Added resume parsing endpoint |
For questions about the API, see SUPPORT.md or open a GitHub Discussion.