Quick Example
Here's a simple example of scanning a business card using an API key:
const response = await fetch('https://havilo.ai/api/entities/scan-business-card', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.HAVILO_API_KEY}`,
},
body: JSON.stringify({
imageBase64: base64ImageData,
mimeType: 'image/jpeg',
entityTypeId: 'your-entity-type-id',
}),
})
const result = await response.json()
console.log(result.extractedFields) // { Name: "John Smith", Email: "john@example.com", ... }Authentication
This endpoint requires API key authentication. See API Authentication for details on creating and managing API keys.
Authorization: Bearer sk_live_abcdefgh.ijklmnopqrstuvwxyz...Requirements:
- API key must have
ENTITY_CREATEpermission in its scopes - For workspace-scoped keys, the
workspaceIdin the request body must match the key's workspace (or can be omitted) - For system-level keys,
workspaceIdmust be provided in the request body
Authentication Errors
| Status Code | Error | Description |
|---|---|---|
| 401 | Unauthorized | Invalid or missing API key |
| 403 | Insufficient permissions | API key lacks ENTITY_CREATE permission |
| 403 | Workspace mismatch | Provided workspaceId doesn't match API key's scope |
Endpoint
/api/entities/scan-business-cardExtract structured contact information from a business card image.
Request
Content-Type: application/json
Request Body Parameters
All parameters are required:
| Parameter | Type | Required | Description |
|---|---|---|---|
imageBase64 | string | Yes | Base64 encoded image data without the data URL prefix (e.g., omit data:image/jpeg;base64,) |
mimeType | string | Yes | Image MIME type: image/jpeg, image/png, image/webp, or image/gif |
entityTypeId | string | Yes | ID of the entity type that defines which fields to extract |
workspaceId | string | Yes | Workspace ID for permission validation and entity type lookup |
Image Requirements
Example Request
{
"imageBase64": "/9j/4AAQSkZJRgABAQAAAQABAAD...",
"mimeType": "image/jpeg",
"entityTypeId": "4d15d6af-f5c8-4126-bef5-9f4321f1d598",
"workspaceId": "c33f6347-e9a4-4acc-a3df-d0c74c736dbd"
}Response
Success Response (200 OK)
When the business card is successfully scanned, you'll receive:
{
"success": true,
"extractedFields": {
"Name": "Jane Doe",
"Email": "jane.doe@example.com",
"Phone": "(555) 123-4567",
"Company": "Acme Corporation",
"Title": "Senior Product Manager"
},
"fieldsFound": ["Name", "Email", "Phone", "Company", "Title"],
"confidence": 0.92,
"validation": {
"valid": true,
"errors": []
}
}Response Fields
| Field | Type | Description |
|---|---|---|
success | boolean | Always true for successful requests |
extractedFields | object | Key-value pairs of extracted data. Only includes fields found on the card with non-empty values |
fieldsFound | string[] | Array of field names that were successfully extracted |
confidence | number | AI confidence score from 0 to 1 (default: 0.7 if not provided by AI) |
validation.valid | boolean | Whether all extracted fields passed validation rules |
validation.errors | string[] | Array of validation error messages (empty if all fields are valid) |
Error Responses
400 Bad Request - Missing Parameters
{
"error": "Missing required fields: imageBase64, mimeType, entityTypeId, workspaceId"
}One or more required parameters are missing from the request body.
400 Bad Request - Invalid File Type
{
"error": "Invalid file type. Only images are supported."
}The provided mimeType is not an image format.
401 Unauthorized
{
"error": "Unauthorized"
}Invalid or missing API key. Ensure you're passing a valid Authorization: Bearer header.
403 Forbidden
{
"error": "Insufficient permissions",
"required": ["ENTITY_CREATE"],
"mode": "all"
}API key lacks the required ENTITY_CREATE permission in its scopes.
404 Not Found
{
"error": "Entity type not found"
}The specified entityTypeId doesn't exist or isn't accessible in the workspace.
400 Bad Request - No Fields
{
"error": "Entity type has no fields to extract"
}The entity type has no field definitions configured.
500 Internal Server Error
{
"error": "Failed to scan business card",
"message": "AI service unavailable"
}An unexpected error occurred during processing (e.g., AI service timeout, parsing error).
Field Validation
The API automatically validates extracted data based on field types of the corresponding entity type schema:
| Field Type | Validation Rule | Auto-Fix |
|---|---|---|
EMAIL | Must contain @ symbol | None |
URL | Must be a valid URL | Prepends https:// if protocol missing |
NUMBER | Must be numeric | None |
BOOLEAN | Must be: true, false, 1, 0, yes, no (case-insensitive) | None |
| Other types | No validation | None |
Example Response with Validation Errors
{
"success": true,
"extractedFields": {
"Name": "Bob Johnson",
"Email": "not-a-valid-email"
},
"fieldsFound": ["Name", "Email"],
"confidence": 0.65,
"validation": {
"valid": false,
"errors": ["Field \"Email\" should be a valid email"]
}
}Code Examples
// Store your API key securely (e.g., environment variables)
const API_KEY = process.env.HAVILO_API_KEY
async function scanBusinessCard(
imageFile: File,
entityTypeId: string,
workspaceId?: string // Optional if using workspace-scoped API key
) {
// Convert image to base64
const base64Data = await new Promise<string>((resolve) => {
const reader = new FileReader()
reader.onloadend = () => {
const result = reader.result as string
// Remove data URL prefix
const base64 = result.split(',')[1]
resolve(base64)
}
reader.readAsDataURL(imageFile)
})
// Make API request with API key authentication
const response = await fetch('https://havilo.ai/api/entities/scan-business-card', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`,
},
body: JSON.stringify({
imageBase64: base64Data,
mimeType: imageFile.type,
entityTypeId,
...(workspaceId && { workspaceId }), // Only include if provided
}),
})
if (!response.ok) {
const error = await response.json()
throw new Error(error.error || 'Failed to scan business card')
}
return await response.json()
}
// Usage with API key (workspace-scoped key - workspaceId optional)
try {
const result = await scanBusinessCard(
file,
'clx1234567890'
)
console.log('Extracted fields:', result.extractedFields)
console.log('Confidence:', result.confidence)
if (!result.validation.valid) {
console.warn('Validation errors:', result.validation.errors)
}
} catch (error) {
console.error('Scan failed:', error.message)
}Best Practices
1. Always Check Confidence Scores
if (result.confidence < 0.7) {
// Low confidence - require user review
showReviewDialog(result.extractedFields)
} else {
// High confidence - safe to auto-populate
populateForm(result.extractedFields)
}2. Handle Validation Errors Gracefully
if (!result.validation.valid) {
result.validation.errors.forEach((error) => {
console.warn(error)
// Highlight the problematic field in your UI
})
}3. Optimize Image Quality
// Compress large images before sending
async function optimizeImage(file: File): Promise<File> {
if (file.size > 2 * 1024 * 1024) {
// 2MB
// Resize or compress the image
return await compressImage(file, { maxWidth: 2000, quality: 0.8 })
}
return file
}Data Privacy
Need Help?
If you encounter issues or have questions:
- Check Entities to understand entity types and field configuration
- Review your API key scopes to ensure
ENTITY_CREATEpermission is granted - Verify your entity type has field definitions configured
- See API Authentication for API key setup