The Reshape CLI includes an embedded REST API server that powers the Web UI. This API can also be used for custom integrations.
http://localhost:5000/api
No authentication required for local usage. The API is designed for local development and personal use.
Scan a folder and retrieve all files with their metadata.
Request Body:
{
"folderPath": "C:\\Photos\\Vacation",
"extensions": [".jpg", ".png", ".heic"]
}| Field | Type | Required | Description |
|---|---|---|---|
folderPath |
string | Yes | Absolute path to the folder to scan |
extensions |
string[] | No | Filter by file extensions |
Response:
{
"folderPath": "C:\\Photos\\Vacation",
"files": [
{
"name": "IMG_0001.jpg",
"fullPath": "C:\\Photos\\Vacation\\IMG_0001.jpg",
"relativePath": "",
"extension": ".jpg",
"size": 4194304,
"createdAt": "2024-01-15T14:30:00",
"modifiedAt": "2024-01-15T14:30:00",
"isSelected": true,
"metadata": {
"filename": "IMG_0001",
"ext": "jpg",
"year": "2024",
"month": "01",
"day": "15",
"date_taken": "2024-01-15",
"time_taken": "14-30-00",
"camera_make": "Apple",
"camera_model": "iPhone_15_Pro"
},
"gpsCoordinates": {
"latitude": 48.858844,
"longitude": 2.294351
},
"dateTakenUtc": "2024-01-15T13:30:00Z"
}
],
"totalCount": 1
}Error Response:
{
"error": "Folder not found: C:\\NonExistent"
}Get available rename pattern templates.
Response:
[
{
"pattern": "{year}-{month}-{day}_{filename}",
"description": "Date prefix: 2024-01-15_photo"
},
{
"pattern": "{date_taken}_{time_taken}_{filename}",
"description": "EXIF date/time: 2024-01-15_14-30-00_photo"
},
{
"pattern": "{year}/{month}/{filename}",
"description": "Year/Month folders (use with caution)"
},
{
"pattern": "{camera_model}_{date_taken}_{counter:4}",
"description": "Camera + date + counter: iPhone_2024-01-15_0001"
},
{
"pattern": "{filename}_{counter:3}",
"description": "Original name + counter: photo_001"
},
{
"pattern": "IMG_{year}{month}{day}_{counter:4}",
"description": "Standard format: IMG_20240115_0001"
}
]Add a custom pattern template.
Request Body:
{
"pattern": "{camera_model}_{year}_{counter:3}",
"description": "Custom camera pattern with year and counter"
}| Field | Type | Required | Description |
|---|---|---|---|
pattern |
string | Yes | The pattern template to add |
description |
string | Yes | Description of what the pattern does |
Response:
{
"success": true,
"message": "Pattern added successfully"
}Error Response:
{
"success": false,
"message": "Pattern already exists"
}Remove a custom pattern template.
Request Body:
{
"pattern": "{camera_model}_{year}_{counter:3}"
}| Field | Type | Required | Description |
|---|---|---|---|
pattern |
string | Yes | The pattern template to remove |
Response:
{
"success": true,
"message": "Pattern removed successfully"
}Error Response:
{
"success": false,
"message": "Pattern not found"
}Generate a preview of rename operations.
Request Body:
{
"folderPath": "C:\\Photos\\Vacation",
"pattern": "{year}-{month}-{day}_{filename}",
"extensions": [".jpg"],
"vacationMode": {
"enabled": false,
"startDate": null,
"dayFolderPattern": "Tag {day_number}",
"subfolderPattern": null
}
}| Field | Type | Required | Description |
|---|---|---|---|
folderPath |
string | Yes | Folder path |
pattern |
string | Yes | Rename pattern |
extensions |
string[] | No | Filter by extensions |
vacationMode |
object | No | Vacation mode configuration |
Vacation Mode Options:
| Field | Type | Description |
|---|---|---|
enabled |
boolean | Enable vacation mode |
startDate |
string | ISO date string (optional, auto-detected if null) |
dayFolderPattern |
string | Folder naming pattern (e.g., "Day {day_number}") |
subfolderPattern |
string | Optional subfolder pattern |
Response:
{
"items": [
{
"originalName": "IMG_0001.jpg",
"newName": "2024-01-15_IMG_0001.jpg",
"fullPath": "C:\\Photos\\Vacation\\IMG_0001.jpg",
"relativePath": "",
"hasConflict": false,
"isSelected": true,
"dayNumber": null
},
{
"originalName": "IMG_0002.jpg",
"newName": "2024-01-15_IMG_0002.jpg",
"fullPath": "C:\\Photos\\Vacation\\IMG_0002.jpg",
"relativePath": "",
"hasConflict": false,
"isSelected": true,
"dayNumber": null
}
],
"conflictCount": 0
}Vacation Mode Response Example:
{
"items": [
{
"originalName": "IMG_0001.jpg",
"newName": "Tag 1\\2024-01-15_001.jpg",
"fullPath": "C:\\Photos\\Vacation\\IMG_0001.jpg",
"relativePath": "",
"hasConflict": false,
"isSelected": true,
"dayNumber": 1
},
{
"originalName": "IMG_0005.jpg",
"newName": "Tag 2\\2024-01-16_001.jpg",
"fullPath": "C:\\Photos\\Vacation\\IMG_0005.jpg",
"relativePath": "",
"hasConflict": false,
"isSelected": true,
"dayNumber": 2
}
],
"conflictCount": 0
}Execute rename operations.
Request Body:
{
"items": [
{
"originalName": "IMG_0001.jpg",
"newName": "2024-01-15_IMG_0001.jpg",
"fullPath": "C:\\Photos\\Vacation\\IMG_0001.jpg",
"relativePath": "",
"hasConflict": false,
"isSelected": true,
"dayNumber": null
}
],
"baseFolderPath": "C:\\Photos\\Vacation",
"dryRun": false
}| Field | Type | Required | Description |
|---|---|---|---|
items |
array | Yes | Array of preview items to rename |
baseFolderPath |
string | No | Base folder for vacation mode |
dryRun |
boolean | No | Preview without executing (default: false) |
Response:
{
"results": [
{
"originalPath": "C:\\Photos\\Vacation\\IMG_0001.jpg",
"newPath": "C:\\Photos\\Vacation\\2024-01-15_IMG_0001.jpg",
"success": true,
"error": null
}
],
"successCount": 1,
"errorCount": 0
}Error Response:
{
"results": [
{
"originalPath": "C:\\Photos\\Vacation\\IMG_0001.jpg",
"newPath": "C:\\Photos\\Vacation\\2024-01-15_IMG_0001.jpg",
"success": false,
"error": "Access denied"
}
],
"successCount": 0,
"errorCount": 1
}Get metadata for a specific file.
URL Parameters:
| Parameter | Description |
|---|---|
filePath |
URL-encoded absolute file path |
Example:
GET /api/metadata/C%3A%5CPhotos%5CIMG_0001.jpg
Response:
{
"filename": "IMG_0001",
"ext": "jpg",
"size": "4194304",
"created": "2024-01-15",
"created_time": "14-30-00",
"modified": "2024-01-15",
"modified_time": "14-30-00",
"year": "2024",
"month": "01",
"day": "15",
"date_taken": "2024-01-15",
"time_taken": "14-30-00",
"camera_make": "Apple",
"camera_model": "iPhone_15_Pro",
"width": "4000",
"height": "3000",
"gps_lat": "48.858844",
"gps_lon": "2.294351"
}interface FileInfo {
name: string;
fullPath: string;
relativePath: string;
extension: string;
size: number;
createdAt: string; // ISO datetime
modifiedAt: string; // ISO datetime
isSelected: boolean;
metadata: Record<string, string>;
gpsCoordinates?: GpsCoordinates;
dateTakenUtc?: string; // ISO datetime
}interface GpsCoordinates {
latitude: number;
longitude: number;
}interface RenamePreviewItem {
originalName: string;
newName: string;
fullPath: string;
relativePath: string;
hasConflict: boolean;
isSelected: boolean;
dayNumber?: number;
}interface RenameResult {
originalPath: string;
newPath: string;
success: boolean;
error?: string;
}interface VacationModeOptions {
enabled: boolean;
startDate?: string; // ISO date
dayFolderPattern: string; // e.g., "Tag {day_number}"
subfolderPattern?: string; // e.g., "{camera_model}"
}interface PatternResponse {
success: boolean;
message: string;
}All endpoints return appropriate HTTP status codes:
| Status | Description |
|---|---|
200 |
Success |
400 |
Bad request (invalid parameters) |
404 |
Not found (file/folder doesn't exist) |
500 |
Internal server error |
Error responses follow this format:
{
"error": "Error message describing what went wrong"
}// Scan folder
const scanResponse = await fetch('/api/scan', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
folderPath: 'C:\\Photos',
extensions: ['.jpg', '.png']
})
});
const { files } = await scanResponse.json();
// Preview rename
const previewResponse = await fetch('/api/preview', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
folderPath: 'C:\\Photos',
pattern: '{year}-{month}-{day}_{filename}'
})
});
const { items, conflictCount } = await previewResponse.json();
// Execute rename
const renameResponse = await fetch('/api/rename', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ items })
});
const { successCount, errorCount } = await renameResponse.json();# Scan folder
curl -X POST http://localhost:5000/api/scan \
-H "Content-Type: application/json" \
-d '{"folderPath": "C:\\Photos", "extensions": [".jpg"]}'
# Get patterns
curl http://localhost:5000/api/patterns
# Preview rename
curl -X POST http://localhost:5000/api/preview \
-H "Content-Type: application/json" \
-d '{"folderPath": "C:\\Photos", "pattern": "{year}_{filename}"}'# Scan folder
$body = @{
folderPath = "C:\Photos"
extensions = @(".jpg", ".png")
} | ConvertTo-Json
$response = Invoke-RestMethod -Uri "http://localhost:5000/api/scan" `
-Method Post -ContentType "application/json" -Body $body
$response.files | Format-Table name, size, extension