Manage Documents
Manage Documents
Monitor CV processing, retrieve parsed data, and manage your document library. Track upload status, access extracted candidate information, and maintain your talent database.
Quick Start
Check processing status and retrieve parsed data:
curl "https://api.floreal.aiv1/public/documents/DOCUMENT_ID" \
-H "X-API-Key: YOUR_API_KEY"Browse all uploaded CVs:
curl "https://api.floreal.aiv1/public/documents?limit=20" \
-H "X-API-Key: YOUR_API_KEY"Permanently remove a CV:
curl -X DELETE "https://api.floreal.aiv1/public/documents/DOCUMENT_ID" \
-H "X-API-Key: YOUR_API_KEY"What You Can Do
📊 Monitor Processing
Track CV processing status in real-time
📋 Retrieve Data
Access complete parsed candidate information
📚 Browse Library
List and filter your CV database
🗑️ Clean Up
Delete outdated or failed documents
Processing States:
- uploading (0-15s) - File being copied to storage
- pending (15-90s) - AI analyzing CV content
- completed ✅ - Ready to use
- failed ❌ - Processing error
- invalid ⚠️ - Not recognized as a CV
1. Get Document Status
Poll this endpoint after uploading to check if processing is complete.
When to Use
✅ After uploading - Monitor processing progress
✅ Retrieve data - Access parsed CV information
✅ Check errors - Diagnose failed uploads
✅ Automation - Integrate status checks into workflows
Quick Example
// Poll until complete
async function getDocument(documentId) {
const response = await fetch(
`https://api.floreal.aiv1/public/documents/${documentId}`,
{ headers: { 'X-API-Key': 'YOUR_API_KEY' } }
);
const data = await response.json();
if (data.status === 'completed') {
console.log('✅ Ready!');
console.log('Contact:', data.contact);
console.log('Profile:', data.profile);
return data;
}
if (data.status === 'failed') {
console.error('❌ Failed:', data.error?.message);
return data;
}
// Still processing
console.log(`⏳ Status: ${data.status}`);
await new Promise(r => setTimeout(r, 5000));
return getDocument(documentId); // Poll again
}
const result = await getDocument('789e4567-...');import time
import requests
def get_document(document_id):
while True:
response = requests.get(
f'https://api.floreal.aiv1/public/documents/{document_id}',
headers={'X-API-Key': 'YOUR_API_KEY'}
)
data = response.json()
if data['status'] == 'completed':
print('✅ Ready!')
print('Contact:', data['contact'])
print('Profile:', data['profile'])
return data
if data['status'] == 'failed':
print('❌ Failed:', data.get('error', {}).get('message'))
return data
# Still processing
print(f"⏳ Status: {data['status']}")
time.sleep(5)
result = get_document('789e4567-...')curl "https://api.floreal.aiv1/public/documents/789e4567-..." \
-H "X-API-Key: YOUR_API_KEY"Response (when completed)
{
"documentId": "789e4567-e89b-12d3-a456-426614174000",
"status": "completed",
"summary": "Benjamin Gabay - Sales-marketing Professional with 4 years of experience...",
"extractedText": "Contact +33766771226\nLinkedIn: www.linkedin.com/in/benjamin-gabay...",
"contact": {
"firstName": "Benjamin",
"lastName": "Gabay",
"email": "benjamin.gabay@example.com",
"linkedin": "www.linkedin.com/in/benjamin-gabay",
"location": { "city": "Paris", "country": "FRA" }
},
"profile": {
"domain": "Sales Marketing",
"specialization": "Content Marketing",
"seniorityLevel": "Junior",
"experienceYears": 4,
"technicalStack": ["Copywriting", "Content Creation"],
"industries": ["Real-estate", "Social Media"],
"hasManagement": false
},
"attributes": {
"languages": { "french": 1, "languages_count": 1 },
"technical_skills": { "copywriting": 1, "content_creation": 1 },
"professional_experience": { "total_experience_years": 4 }
},
"processingTime": { "seconds": 62, "formatted": "62s" }
}What you get:
- ✅ summary - AI-generated professional overview
- ✅ extractedText - Full CV text (5-15KB)
- ✅ contact - Name, email, LinkedIn, location
- ✅ profile - Domain, experience, skills, industries
- ✅ attributes - 100+ structured fields for filtering
2. List Documents
Browse your organization's CV library with filtering and pagination.
When to Use
✅ Dashboard - Show recent uploads
✅ Monitoring - Track processing status
✅ Batch operations - Find documents to process
✅ Reports - Analyze upload patterns
Quick Example
// List recent completed CVs
const response = await fetch(
'https://api.floreal.aiv1/public/documents?status=completed&limit=20',
{ headers: { 'X-API-Key': 'YOUR_API_KEY' } }
);
const { data, pagination, summary } = await response.json();
console.log(`Found ${data.length} completed CVs`);
console.log(`Total in database: ${summary.total}`);
data.forEach(doc => {
console.log(`${doc.documentName} - ${doc.candidate?.fullName}`);
});# List recent completed CVs
response = requests.get(
'https://api.floreal.aiv1/public/documents',
params={'status': 'completed', 'limit': 20},
headers={'X-API-Key': 'YOUR_API_KEY'}
)
data = response.json()
print(f"Found {len(data['data'])} completed CVs")
print(f"Total in database: {data['summary']['total']}")
for doc in data['data']:
candidate = doc.get('candidate', {})
print(f"{doc['documentName']} - {candidate.get('fullName')}")# List recent uploads
curl "https://api.floreal.aiv1/public/documents?limit=20&sortOrder=desc" \
-H "X-API-Key: YOUR_API_KEY"
# Filter by status
curl "https://api.floreal.aiv1/public/documents?status=completed&limit=100" \
-H "X-API-Key: YOUR_API_KEY"Response
{
"data": [
{
"documentId": "789e4567-...",
"documentName": "Benjamin Gabay - CV",
"documentType": "cv",
"status": "completed",
"documentDate": "11-2025",
"candidate": {
"firstName": "Benjamin",
"lastName": "Gabay",
"fullName": "Benjamin Gabay",
"domain": "Sales Marketing"
},
"timestamps": {
"createdAt": "2025-11-05T14:30:00.000Z",
"updatedAt": "2025-11-05T14:30:45.000Z"
}
}
],
"pagination": {
"limit": 50,
"offset": 0,
"total": 127,
"hasMore": true,
"currentPage": 1,
"totalPages": 3
},
"summary": {
"uploading": 2,
"pending": 5,
"completed": 115,
"failed": 3,
"invalid": 2,
"total": 127
}
}Filtering Options
| Parameter | Type | Description | Example |
|---|---|---|---|
| limit | integer | Results per page (1-100) | 50 |
| offset | integer | Skip results | 0 |
| status | string | Filter by state | completed |
| documentType | string | Filter by type | cv |
| sortBy | string | Sort field | createdAt |
| sortOrder | string | Sort direction | desc |
| createdAfter | datetime | After date | 2025-11-01T00:00:00Z |
| createdBefore | datetime | Before date | 2025-11-30T23:59:59Z |
Common Use Cases
Find failed uploads
const failed = await fetch("/v1/public/documents?status=failed", {
headers: { "X-API-Key": "YOUR_API_KEY" },
}).then((r) => r.json());
console.log(`${failed.data.length} failed uploads`);Paginate through all documents
let allDocs = [];
let offset = 0;
while (true) {
const { data, pagination } = await fetch(
`/v1/public/documents?limit=100&offset=${offset}`,
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
).then((r) => r.json());
allDocs = allDocs.concat(data);
if (!pagination.hasMore) break;
offset += 100;
}Get documents from last week
const lastWeek = new Date();
lastWeek.setDate(lastWeek.getDate() - 7);
const recent = await fetch(
`/v1/public/documents?createdAfter=${lastWeek.toISOString()}`,
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
).then((r) => r.json());Note: This endpoint returns metadata only. For complete candidate data
(contact, profile, attributes), use GET /v1/public/documents/{documentId}.
3. Delete Document
Permanently remove a CV and all associated data.
When to Use
✅ Cleanup - Remove failed or invalid uploads
✅ GDPR compliance - Delete candidate data on request
✅ Storage management - Remove outdated CVs
✅ Error recovery - Clear corrupted documents
⚠️ Important
This action is irreversible!
Deletion removes data from 3 locations:
- Database - Document record and extracted data
- Cloud Storage (S3) - Original PDF/DOC/DOCX file
- Vector Database - Search embeddings (Pinecone)
Make sure you have backups if needed.
Quick Example
// Delete a document
const response = await fetch(
'https://api.floreal.aiv1/public/documents/789e4567-...',
{
method: 'DELETE',
headers: { 'X-API-Key': 'YOUR_API_KEY' }
}
);
if (response.ok) {
const result = await response.json();
console.log('✅ Deleted:', result.documentName);
console.log('Database:', result.deleted.database);
console.log('Storage:', result.deleted.storage);
console.log('Search index:', result.deleted.searchIndex);
} else {
console.error('❌ Failed:', await response.text());
}# Delete a document
response = requests.delete(
'https://api.floreal.aiv1/public/documents/789e4567-...',
headers={'X-API-Key': 'YOUR_API_KEY'}
)
if response.status_code == 200:
result = response.json()
print(f"✅ Deleted: {result['documentName']}")
print(f"Database: {result['deleted']['database']}")
print(f"Storage: {result['deleted']['storage']}")
print(f"Search index: {result['deleted']['searchIndex']}")
else:
print(f"❌ Failed: {response.text}")curl -X DELETE "https://api.floreal.aiv1/public/documents/789e4567-..." \
-H "X-API-Key: YOUR_API_KEY"Response
{
"message": "Document deleted successfully",
"documentId": "789e4567-e89b-12d3-a456-426614174000",
"documentName": "Benjamin Gabay - CV",
"deleted": {
"database": true,
"storage": true,
"searchIndex": true
}
}Common Use Cases
Delete all failed uploads
const failed = await fetch("/v1/public/documents?status=failed", {
headers: { "X-API-Key": "YOUR_API_KEY" },
}).then((r) => r.json());
for (const doc of failed.data) {
await fetch(`/v1/public/documents/${doc.documentId}`, {
method: "DELETE",
headers: { "X-API-Key": "YOUR_API_KEY" },
});
console.log(`Deleted: ${doc.documentName}`);
}Delete documents older than 6 months
const sixMonthsAgo = new Date();
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
const old = await fetch(
`/v1/public/documents?createdBefore=${sixMonthsAgo.toISOString()}`,
{ headers: { "X-API-Key": "YOUR_API_KEY" } }
).then((r) => r.json());
for (const doc of old.data) {
await fetch(`/v1/public/documents/${doc.documentId}`, {
method: "DELETE",
headers: { "X-API-Key": "YOUR_API_KEY" },
});
}Delete specific candidate's documents
// First, list all documents
const all = await fetch("/v1/public/documents?status=completed", {
headers: { "X-API-Key": "YOUR_API_KEY" },
}).then((r) => r.json());
// Filter by candidate email
const toDelete = all.data.filter((doc) => {
// Get full details
return fetch(`/v1/public/documents/${doc.documentId}`, {
headers: { "X-API-Key": "YOUR_API_KEY" },
})
.then((r) => r.json())
.then((details) => details.contact?.email === "candidate@example.com");
});
// Delete them
for (const doc of toDelete) {
await fetch(`/v1/public/documents/${doc.documentId}`, {
method: "DELETE",
headers: { "X-API-Key": "YOUR_API_KEY" },
});
}Best Practices
Polling Strategy
✅ Use exponential backoff
async function pollWithBackoff(documentId) {
let delay = 5000; // Start with 5s
const maxDelay = 30000; // Max 30s
while (true) {
const data = await getDocument(documentId);
if (data.status === "completed" || data.status === "failed") {
return data;
}
await new Promise((r) => setTimeout(r, delay));
delay = Math.min(delay * 1.5, maxDelay); // Increase delay
}
}✅ Set reasonable timeouts
const maxAttempts = 18; // 90 seconds max
const pollInterval = 5000; // 5 seconds✅ Handle all states
switch (data.status) {
case "completed":
return processData(data);
case "failed":
return handleError(data.error);
case "invalid":
return notifyUser("Not a valid CV");
case "uploading":
case "pending":
return pollAgain();
}Error Handling
✅ Always check response status
const response = await fetch(url);
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}✅ Implement retries for transient errors
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (response.ok) return response;
if (response.status >= 500) {
// Server error - retry
await new Promise((r) => setTimeout(r, 1000 * (i + 1)));
continue;
}
// Client error - don't retry
throw new Error(await response.text());
} catch (error) {
if (i === maxRetries - 1) throw error;
}
}
}Data Management
✅ Store document IDs in your database
// After upload
const { documentId } = await uploadCV(file);
await db.candidates.update(candidateId, { documentId });✅ Track upload metadata
await db.uploads.create({
documentId,
candidateId,
uploadedBy: userId,
uploadedAt: new Date(),
status: "processing",
});✅ Clean up regularly
// Delete failed uploads older than 7 days
const sevenDaysAgo = new Date();
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7);
const oldFailed = await fetch(
`/v1/public/documents?status=failed&createdBefore=${sevenDaysAgo.toISOString()}`,
{ headers: { "X-API-Key": API_KEY } }
).then((r) => r.json());
for (const doc of oldFailed.data) {
await deleteDocument(doc.documentId);
}API Reference
Get Document
Check processing status and retrieve parsed CV data
List Documents
Browse uploaded CVs with filtering and pagination
Delete Document
Permanently remove CV and all associated data
Troubleshooting
Document stuck in "pending"
Wait time: Up to 90 seconds
Solution: If longer, contact support with documentId
"Document not found" error
Causes:
- Wrong documentId
- Document belongs to different organization
- Document was deleted
Solution: Verify documentId and API key
List returns empty but summary shows documents
Cause: Filters too restrictive
Solution: Remove filters or adjust date range
Delete fails with 403
Cause: Document belongs to different organization
Solution: Verify you're using correct API key