Upload CV from URL
Fetch and process a CV from a public URL
The simplest upload method for automation - just provide a URL to a CV file, and the system will download, upload, and process it automatically.
⚠️ IMPORTANT: Must be a Direct File Link
This endpoint requires a direct download URL that returns the actual file content, not an HTML sharing page.
How It Works
Automated Fetch & Process:
- Provide a public URL to CV file
- Server fetches file from URL
- File uploaded to temporary storage
- Document record created automatically
- CV analysis starts immediately
- Poll status endpoint for results
Perfect for integrations - no file handling needed on your end.
Common URL Mistakes
1. Google Drive Sharing Links
Sharing links don't work - convert to direct download URLs:
❌ WRONG: https://drive.google.com/file/d/FILE_ID/view?usp=sharing
✅ CORRECT: https://drive.google.com/uc?export=download&id=FILE_ID
How to fix:
- Extract the FILE_ID from between
/d/and/viewin your sharing URL - Use this format:
https://drive.google.com/uc?export=download&id=FILE_ID - Make sure file is set to "Anyone with the link can view"
Example:
Sharing URL: https://drive.google.com/file/d/1abc123xyz/view?usp=sharing
Direct URL: https://drive.google.com/uc?export=download&id=1abc123xyz2. Dropbox Sharing Links
❌ WRONG: https://www.dropbox.com/s/abc123/file.pdf?dl=0
✅ CORRECT: https://www.dropbox.com/s/abc123/file.pdf?dl=1
How to fix: Change ?dl=0 to ?dl=1 at the end of the URL
3. HTML Pages Instead of Files
❌ WRONG: Download pages, login pages, landing pages ✅ CORRECT: Direct link to the file itself
How to test: Run curl -I "YOUR_URL" and check the Content-Type header should be application/pdf or similar, not text/html
Request Format
Body (JSON)
{
"url": "https://drive.google.com/uc?export=download&id=1niWNOc_FeSjt03vTpwl3TwFvW2KzU0jc",
"documentName": "John Doe - Software Engineer",
"documentType": "cv",
"documentDate": "11-2025",
"fileName": "john-doe-resume.pdf"
}Required Fields
| Field | Type | Required | Description | Example |
|---|---|---|---|---|
| url | string | Yes | Public URL to CV file | https://example.com/cv.pdf |
| documentName | string | Yes | Display name for CV | John Doe - Software Engineer |
| documentType | string | No | Document category (default: cv) | cv |
| documentDate | string | Yes | CV date in MM-YYYY format | 11-2025 |
| fileName | string | No | Custom filename (optional) | john-doe-cv.pdf |
URL Requirements
✅ Publicly Accessible - No authentication required ✅ Direct File Link - Must point directly to file, not HTML page ✅ HTTPS Recommended - HTTP also supported ✅ Response Time - Must respond within 30 seconds
Supported File Types
✅ PDF - application/pdf (recommended)
✅ DOCX - application/vnd.openxmlformats-officedocument.wordprocessingml.document
✅ DOC - application/msword
✅ TXT - text/plain
Maximum file size: 10 MB
Security Features
SSRF Protection
The endpoint blocks requests to internal/private URLs:
❌ Blocked:
localhost,127.0.0.1,0.0.0.0- AWS metadata endpoint:
169.254.169.254 - Private IP ranges:
10.x.x.x,192.168.x.x,172.16 to 31.x.x - IPv6 localhost:
::1
✅ Allowed:
- Public internet URLs
- CDN URLs
- Cloud storage URLs (S3, GCS, Azure Blob)
Response
Success (201 Created)
{
"documentId": "789e4567-e89b-12d3-a456-426614174000",
"status": "uploading",
"message": "Document fetched from URL and processing started",
"sourceUrl": "https://example.com/resumes/john-doe.pdf",
"documentUrl": "s3://bucket/companies/.../documents/.../resume.pdf",
"file": {
"name": "john_doe_resume.pdf",
"size": 245760,
"sizeFormatted": "240 KB",
"contentType": "application/pdf"
},
"estimatedProcessingTime": "30 to 60 seconds",
"statusEndpoint": "/v1/public/documents/789e4567-e89b-12d3-a456-426614174000",
"nextSteps": [
"Poll GET /v1/public/documents/789e4567-... to check processing status",
"Document will be in 'completed' state when analysis is done"
]
}Processing Pipeline
After fetching, your CV goes through this automated workflow:
Total time: 30 to 120 seconds for most CVs (up to 90s for complex documents or slow URLs)
Processing States
| State | Duration | Description | Next Action |
|---|---|---|---|
| uploading | 0 to 15s | Fetching and uploading file | Wait 10s |
| pending | 15 to 50s | AI analyzing CV content | Poll every 10s |
| completed | Final | Analysis finished, searchable | Retrieve data |
| failed | Final | Processing error occurred | Check error details |
| invalid | Final | Not recognized as CV | Upload different file |
Complete Examples
cURL
curl -X POST https://api.floreal.ai/v1/public/documents/from-url \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://drive.google.com/uc?export=download&id=1niWNOc_FeSjt03vTpwl3TwFvW2KzU0jc",
"documentName": "John Doe - Software Engineer",
"documentType": "cv",
"documentDate": "11-2025"
}'JavaScript (Node.js)
// Upload from URL
const response = await fetch(
'https://api.floreal.ai/v1/public/documents/from-url',
{
method: 'POST',
headers: {
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
url: 'https://drive.google.com/uc?export=download&id=1niWNOc_FeSjt03vTpwl3TwFvW2KzU0jc',
documentName: 'John Doe - Software Engineer',
documentType: 'cv',
documentDate: '11-2025'
})
}
);
const result = await response.json();
console.log('✅ Upload started! Document ID:', result.documentId);
// Poll for completion
const pollStatus = async (documentId) => {
const status = await fetch(
'https://api.floreal.ai/v1/public/documents/' + documentId,
{ headers: { 'Authorization': 'X-API-Key YOUR_API_KEY' } }
).then(r => r.json());
console.log('Status:', status.status);
if (status.status === 'completed') {
console.log('✅ Processing complete!');
console.log('Candidate:', status.contact);
return status;
}
if (status.status === 'failed' || status.status === 'invalid') {
console.error('❌ Processing failed:', status.error);
return status;
}
// Still processing
await new Promise(resolve => setTimeout(resolve, 5000));
return pollStatus(documentId);
};
await pollStatus(result.documentId);Python
# Upload from URL
response = requests.post(
'https://api.floreal.ai/v1/public/documents/from-url',
headers={
'X-API-Key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
json={
'url': 'https://drive.google.com/uc?export=download&id=1niWNOc_FeSjt03vTpwl3TwFvW2KzU0jc',
'documentName': 'John Doe - Software Engineer',
'documentType': 'cv',
'documentDate': '11-2025'
}
)
data = response.json()
document_id = data['documentId']
print(f"✅ Upload started! Document ID: {document_id}")
# Poll for completion
def poll_status(document_id):
while True:
status_response = requests.get(
f'https://api.floreal.ai/v1/public/documents/{document_id}',
headers={'Authorization': 'X-API-Key YOUR_API_KEY'}
)
status = status_response.json()
print(f"Status: {status['status']}")
if status['status'] == 'completed':
print("✅ Processing complete!")
print(f"Candidate: {status.get('contact', {})}")
return status
if status['status'] in ['failed', 'invalid']:
print(f"❌ Processing failed: {status.get('error', {})}")
return status
time.sleep(5)
poll_status(document_id)Error Responses
| Status | Error | Cause | Solution |
|---|---|---|---|
| 400 | Invalid URL | Malformed URL | Check URL format |
| 400 | Cannot fetch from internal URLs | Private/internal IP or localhost | Use public URL |
| 400 | Failed to fetch file | URL not accessible or timeout | Ensure URL is public and responds quickly |
| 400 | Invalid file type | Unsupported content type | Use PDF, DOC, DOCX, or TXT |
| 400 | File too large | File exceeds 10MB | Compress document or use smaller file |
| 400 | Invalid date format | Wrong date format | Use MM-YYYY (e.g., "11-2025") |
| 401 | Unauthorized | Invalid API key | Check Authorization header |
| 500 | Upload failed | Server error | Retry or contact support |
Validation & Limits
URL Requirements
✅ Format: Valid HTTP or HTTPS URL ✅ Access: Publicly accessible (no auth) ✅ Timeout: Must respond within 30 seconds ✅ Content: Direct file link, not HTML page
File Requirements
✅ Size: Maximum 10 MB ✅ Types: PDF, DOC, DOCX, TXT only ✅ Content: Must be a valid CV/resume ✅ Auto-conversion: DOC/DOCX files converted to PDF automatically
Metadata Requirements
✅ Document Name: 1 to 255 characters ✅ Document Date: MM-YYYY format (e.g., "03-2024") ✅ Document Type: "cv" or "other" (default: "cv") ✅ File Name: Optional, extracted from URL if not provided
Use Cases
Integration with Cloud Storage
// Upload from AWS S3 public URL
await uploadFromUrl({
url: 'https://my-bucket.s3.amazonaws.com/resumes/candidate-123.pdf',
documentName: 'Jane Smith - Data Scientist',
documentDate: '11-2025'
});Webhook Processing
// Process CV when webhook received
app.post('/webhook/new-application', async (req, res) => {
const { candidateName, cvUrl, applicationDate } = req.body;
const result = await uploadFromUrl({
url: cvUrl,
documentName: candidateName + ' - Application',
documentDate: applicationDate
});
res.json({ documentId: result.documentId });
});Batch Import
// Import multiple CVs from URL list
const cvUrls = [
{ url: 'https://...', name: 'John Doe', date: '11-2025' },
{ url: 'https://...', name: 'Jane Smith', date: '10-2025' },
// ... more CVs
];
const results = await Promise.all(
cvUrls.map(cv =>
uploadFromUrl({
url: cv.url,
documentName: cv.name,
documentDate: cv.date
})
)
);
console.log('Imported ' + results.length + ' CVs');ATS Integration
// Sync CVs from external ATS
async function syncFromATS() {
const applications = await ats.getNewApplications();
for (const app of applications) {
await uploadFromUrl({
url: app.resumeUrl,
documentName: app.firstName + ' ' + app.lastName + ' - ' + app.position,
documentDate: app.submittedDate,
fileName: app.applicationId + '.pdf'
});
}
}Comparison: Upload Methods
| Feature | Direct Upload | Presigned URL | URL Fetch |
|---|---|---|---|
| Requests | 1 request | 3 requests | 1 request |
| Complexity | ⭐ Simple | ⭐⭐⭐ Complex | ⭐ Simple |
| File Source | Local file | Local file | Remote URL |
| Client Handling | Send file bytes | Upload to S3 | JSON only |
| Best For | Quick integration | Large files | Automation |
| Webhook Friendly | ❌ No | ❌ No | ✅ Yes |
Recommendation: Use URL Fetch for:
- Webhook integrations
- Batch imports
- ATS integrations
- When files are already hosted online
Security
✅ API Key Required - All requests authenticated ✅ SSRF Protection - Blocks internal/private URLs ✅ Organization Scoped - Files isolated per company ✅ Temporary Storage - Files in temp folder until validated ✅ Automatic Cleanup - Temp files deleted after processing ✅ 30s Timeout - Prevents hanging on slow URLs ✅ Content-Type Validation - Only allowed file types ✅ Size Limits - Maximum 10MB enforced ✅ HTTPS Only - API encrypted in transit ✅ Audit Trail - All uploads tracked with source URL
Architecture
Upload Flow
Client
↓ (sends URL + metadata)
API Server
↓ (fetches from URL with 30s timeout)
External Server
↓ (returns file)
API Server validates & uploads
↓
S3: uploads/\{uploadId\}/file.pdf ← Temporary
↓ (processes)
Step Functions Workflow:
1. Copy to permanent location
2. Convert (if DOC/DOCX)
3. Extract text
4. Analyze with AI
5. Specialized CV parsing
6. Chunk for search
7. Generate embeddings
8. Store in Pinecone
↓ (cleanup)
Delete temp file
↓
✅ Ready for searchMonitoring & Debugging
Check Upload Status
curl -X GET https://api.floreal.ai/v1/public/documents/DOCUMENT_ID \
-H "X-API-Key: YOUR_API_KEY"List Recent Uploads
curl -X GET "https://api.floreal.ai/v1/public/documents?limit=10&sortOrder=desc" \
-H "X-API-Key: YOUR_API_KEY"Common Issues
Issue: "Failed to fetch file - timeout" Solution: URL took greater than 30s to respond - use faster hosting or direct upload
Issue: "Failed to fetch file - HTTP 403/404" Solution: URL not publicly accessible - check permissions and URL
Issue: "Cannot fetch from internal URLs" Solution: Provided private IP or localhost - use public URL
Issue: "Invalid file type" Solution: URL returns HTML page instead of file - ensure direct file link
Issue: "File too large" Solution: File at URL exceeds 10MB - compress or use smaller file
Troubleshooting
Testing URLs
Before uploading, test your URL:
# Check if URL is accessible
curl -I "https://example.com/resume.pdf"
# Should return:
# HTTP/1.1 200 OK
# Content-Type: application/pdf
# Content-Length: 245760URL Validation Checklist
✅ URL returns 200 OK status ✅ Content-Type header matches file type ✅ Content-Length is under 10MB ✅ No authentication required ✅ Not behind CAPTCHA or rate limiting ✅ Responds within 30 seconds ✅ Direct file link (not download page)
Next Steps
- ✅ Provide URL using this endpoint
- ✅ Save documentId from response
- ✅ Poll status every 5 to 10 seconds
- ✅ Wait for completion (30 to 70 seconds typical)
- ✅ Retrieve data once status is
completed - ✅ Start searching using Search API
Related Endpoints
- Direct Upload -
POST /v1/public/documents/uploads/direct- Upload file directly - Get Document -
GET /v1/public/documents/\{documentId\}- Check status and retrieve data - List Documents -
GET /v1/public/documents- Browse all uploads - Delete Document -
DELETE /v1/public/documents/\{documentId\}- Remove CV - Search CVs -
POST /v1/public/search- Find candidates
API key for public API access. Get yours at https://app.floreal.ai?tab=api
In: header
uri1 <= length <= 255"cv""cv" | "other"^\d{2}-\d{4}$Response Body
curl -X POST "https://api.floreal.ai/v1/public/documents/upload-from-url" \
-H "Content-Type: application/json" \
-d '{
"url": "http://example.com",
"documentName": "string",
"documentDate": "string"
}'{
"documentId": "789e4567-e89b-12d3-a456-426614174000",
"status": "uploading",
"message": "Document fetched from URL and processing started",
"sourceUrl": "https://example.com/resumes/john-doe.pdf",
"documentUrl": "string",
"file": {
"name": "john_doe_resume.pdf",
"size": 245760,
"sizeFormatted": "240 KB",
"contentType": "application/pdf"
},
"estimatedProcessingTime": "30 to 60 seconds",
"statusEndpoint": "/v1/public/documents/789e4567-e89b-12d3-a456-426614174000",
"nextSteps": [
"string"
]
}{
"error": "string",
"message": "string"
}{
"error": "string"
}{
"error": "string",
"message": "string"
}