Search K
Passport Photo
AI-powered passport and ID photo generator. Two-phase workflow: analyze (face detection + background removal) then generate (crop, resize, and tile for printing).
API Endpoints
This tool uses a two-phase flow with separate endpoints for analysis and generation.
Model bundles: background-removal and face-detection
Phase 1: Analyze
POST /api/v1/tools/image/passport-photo/analyze
Detects face landmarks and removes the background. Returns landmark data and a preview for the frontend to display a crop preview.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| file | file | Yes | - | Image file (multipart) |
| clientJobId | string | No | - | Optional job ID for progress tracking via SSE |
Example Request
bash
curl -X POST http://localhost:1349/api/v1/tools/image/passport-photo/analyze \
-F "file=@headshot.jpg"Response (200 OK)
json
{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"filename": "headshot.jpg",
"preview": "<base64-encoded PNG>",
"previewWidth": 800,
"previewHeight": 1067,
"landmarks": {
"leftEye": { "x": 0.42, "y": 0.35 },
"rightEye": { "x": 0.58, "y": 0.35 },
"eyeCenter": { "x": 0.50, "y": 0.35 },
"chin": { "x": 0.50, "y": 0.65 },
"forehead": { "x": 0.50, "y": 0.22 },
"crown": { "x": 0.50, "y": 0.18 },
"nose": { "x": 0.50, "y": 0.48 },
"faceCenterX": 0.50
},
"imageWidth": 2400,
"imageHeight": 3200
}Progress (SSE, optional)
If clientJobId is provided, progress is streamed (0-30% for face detection, 30-95% for background removal).
Error: No Face Detected (422)
json
{
"error": "No face detected",
"details": "Could not detect a face in the uploaded image. Please upload a clear, front-facing photo with good lighting."
}Phase 2: Generate
POST /api/v1/tools/image/passport-photo/generate
Crops, resizes, and optionally tiles the photo onto a print sheet. Uses cached images from Phase 1 (no AI re-run).
Parameters (JSON body)
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| jobId | string | Yes | - | Job ID from Phase 1 |
| filename | string | Yes | - | Original filename from Phase 1 |
| countryCode | string | Yes | - | Country code for passport spec (e.g., US, GB, IN) |
| documentType | string | No | "passport" | Document type (from country spec) |
| bgColor | string | No | "#FFFFFF" | Background color hex |
| printLayout | string | No | "none" | Print paper layout: none, 4x6, a4 |
| maxFileSizeKb | number | No | 0 | Max file size in KB, where 1 KB = 1000 bytes (0 = no limit) |
| dpi | number | No | 300 | Output DPI (72-1200) |
| customWidthMm | number | No | - | Custom photo width in mm (overrides country spec) |
| customHeightMm | number | No | - | Custom photo height in mm (overrides country spec) |
| zoom | number | No | 1 | Zoom factor (0.5-3). Values > 1 crop tighter |
| adjustX | number | No | 0 | Horizontal position adjustment |
| adjustY | number | No | 0 | Vertical position adjustment |
| landmarks | object | Yes | - | Landmarks object from Phase 1 response |
| imageWidth | number | Yes | - | Image width from Phase 1 response |
| imageHeight | number | Yes | - | Image height from Phase 1 response |
Example Request
bash
curl -X POST http://localhost:1349/api/v1/tools/image/passport-photo/generate \
-H "Content-Type: application/json" \
-d '{
"jobId": "a1b2c3d4-...",
"filename": "headshot.jpg",
"countryCode": "US",
"documentType": "passport",
"bgColor": "#FFFFFF",
"printLayout": "4x6",
"dpi": 300,
"zoom": 1,
"adjustX": 0,
"adjustY": 0,
"landmarks": { "leftEye": {"x":0.42,"y":0.35}, "rightEye": {"x":0.58,"y":0.35}, "eyeCenter": {"x":0.50,"y":0.35}, "chin": {"x":0.50,"y":0.65}, "forehead": {"x":0.50,"y":0.22}, "crown": {"x":0.50,"y":0.18}, "nose": {"x":0.50,"y":0.48}, "faceCenterX": 0.50 },
"imageWidth": 2400,
"imageHeight": 3200
}'Response (200 OK)
json
{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"downloadUrl": "/api/v1/download/{jobId}/headshot_passport.jpg",
"dimensions": {
"widthMm": 51,
"heightMm": 51,
"widthPx": 602,
"heightPx": 602,
"dpi": 300
},
"spec": {
"country": "United States",
"countryCode": "US",
"documentType": "passport",
"documentLabel": "Passport"
},
"printDownloadUrl": "/api/v1/download/{jobId}/headshot_passport_print_4x6.jpg"
}Error: Analysis Expired (410)
Generate works from the background-removed image that analyze stored for jobId. Stored outputs are cleaned up after a while, so if too much time passes between the two calls, that image is gone. Call analyze again with the same photo and use the jobId it returns.
json
{
"error": "This photo's analysis has expired. Analyze it again, then generate.",
"code": "ANALYSIS_EXPIRED"
}A jobId or filename that can't name a stored file (a path separator, .., an empty string) answers 400.
Base Route
POST /api/v1/tools/image/passport-photo
Returns guidance to use the correct sub-endpoint.
json
{
"error": "Use /api/v1/tools/image/passport-photo/analyze or /generate"
}Notes
- Requires the
background-removalandface-detectionmodel bundles to be installed. - Phase 1 runs AI (face landmarks + background removal) and caches results. Phase 2 is pure Sharp image manipulation (fast, no AI needed).
- Landmarks are returned as normalized coordinates (0-1 range relative to image dimensions).
- The
previewfield in the analyze response is a base64-encoded PNG (max 800px wide) for fast display. - Country specs include document dimensions, head height ratios, and eye-line positioning based on official passport photo requirements.
- The
printLayoutoption generates a tiled sheet on 4x6" or A4 paper with 2mm gutters between photos. - When
maxFileSizeKbis set, the output's JPEG quality is lowered step by step until it fits. The pixel size is fixed by the country spec, so if the photo still doesn't fit at the lowest quality,/generatereturns 422 with a message saying so, rather than a file over the limit.
