Overview
The upload endpoint accepts odontogram images (PNG format) along with patient data in JSON format, uploads the image to Cloudinary, and stores both the image reference and formatted notes in Airtable.
Endpoint
POST /api/upload-odontogram
The endpoint accepts multipart form data with the following fields:
PNG image file of the odontogram (max size: 10MB)
The Airtable record ID of the patient
The Airtable field name where the image attachment will be stored
JSON string containing odontogram data with patient information and dental findings
jsonFieldName
string
default:"notas-odontograma-paciente"
The Airtable field name where formatted notes will be appended
Example JSON Data Structure
{
"nombre": "Juan Pérez",
"piezas": [
{
"pieza": "11",
"condiciones": ["Caries"],
"prestacion_preexistente": ["Corona"],
"prestacion_requerida": ["Endodoncia"],
"notas": "Requiere atención urgente"
}
]
}
Success Response (200)
{
"success": true,
"message": "Complete odontogram data uploaded! Image + formatted text appended.",
"recordId": "recABCDEF123456",
"attachmentUrl": "https://dl.airtable.com/.attachments/...",
"cloudinaryUrl": "https://res.cloudinary.com/your-cloud/image/upload/...",
"fileName": "odontogram.png",
"service": "Cloudinary FREE",
"totalAttachments": 3,
"jsonAppended": true,
"jsonFieldName": "notas-odontograma-paciente"
}
Error Responses
Error message describing what went wrong
Additional error details (only included in 500 errors)
400 Bad Request
{
"error": "Missing required fields"
}
405 Method Not Allowed
{
"error": "Method not allowed"
}
500 Internal Server Error
{
"error": "Upload failed",
"details": "Cloudinary error: Invalid credentials"
}
Or if credentials are not configured:
{
"error": "Airtable credentials not configured"
}
{
"error": "Cloudinary credentials not configured"
}
CORS Configuration
The endpoint is configured with the following CORS headers:
res.setHeader('Access-Control-Allow-Origin', '*')
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS')
res.setHeader('Access-Control-Allow-Headers', 'Content-Type')
This allows requests from any origin. For production, consider restricting the Access-Control-Allow-Origin to your specific domain.
Error Handling
The endpoint includes comprehensive error handling:
Credential Validation
Before processing, the endpoint validates that all required environment variables are configured:
if (!process.env.AIRTABLE_API_KEY || !process.env.AIRTABLE_BASE_ID) {
return res
.status(500)
.json({ error: 'Airtable credentials not configured' })
}
if (
!process.env.CLOUDINARY_CLOUD_NAME ||
!process.env.CLOUDINARY_API_KEY ||
!process.env.CLOUDINARY_API_SECRET
) {
return res
.status(500)
.json({ error: 'Cloudinary credentials not configured' })
}
File Upload Limits
The endpoint enforces a maximum file size of 10MB:
const form = new IncomingForm({
maxFileSize: 10 * 1024 * 1024,
keepExtensions: true,
})
Cleanup
Temporary files are automatically cleaned up after processing:
try {
fs.unlinkSync(file.filepath)
console.log('🧹 Temp file cleaned up')
} catch (cleanupError) {
console.warn('⚠️ Could not clean temp file:', cleanupError.message)
}
Code Example
Making a Request
const formData = new FormData()
formData.append('file', pngBlob, 'odontogram.png')
formData.append('recordId', 'recABCDEF123456')
formData.append('fieldName', 'odontograma-imagenes')
formData.append('jsonData', JSON.stringify({
nombre: 'Juan Pérez',
piezas: [
{
pieza: '11',
condiciones: ['Caries'],
prestacion_requerida: ['Endodoncia'],
notas: 'Requiere atención urgente'
}
]
}))
formData.append('jsonFieldName', 'notas-odontograma-paciente')
const response = await fetch('/api/upload-odontogram', {
method: 'POST',
body: formData
})
const result = await response.json()
console.log('Upload result:', result)
How It Works
- Parse Form Data - Extracts file, recordId, fieldName, and optional JSON data from multipart form
- Upload to Cloudinary - Uploads PNG image to Cloudinary with metadata
- Fetch Existing Data - Retrieves current attachments and notes from Airtable record
- Append Attachment - Adds new Cloudinary URL to existing attachments array
- Format Notes - Converts JSON data to formatted text and appends to existing notes
- Update Airtable - Updates record with new attachments and formatted notes
- Cleanup - Removes temporary file from server
Next.js Configuration
The endpoint disables default body parsing to handle multipart form data:
export const config = {
api: {
bodyParser: false,
},
}
This is required when using formidable to parse file uploads.