# Lightroom API Batch

> Batch image processing with Adobe Lightroom API — preset application, auto-tone, auto-straighten, exposure/highlight/shadow/color adjustments, color grading, and batch normalization across an image set. Use whenever the user wants "Lightroom API", "batch photo processing", "apply preset", "auto-tone images", "color grade", "normalize a photo set", "image.adobe.io", "Camera Raw filter", or needs to apply consistent processing to many images at scale. Encodes the pattern for delivering visually cohesive image sets from mixed-source raw input — the workhorse pipeline for any photo-heavy creative workflow.

- Skill: `focus-gts/lightroom-api-batch` (Agent Skill)
- Install (CLI): `npx skillmds@latest add focus-gts/lightroom-api-batch`
- Raw SKILL.md: https://api.skillmd.com/api/skills/focus-gts/lightroom-api-batch/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: Apache-2.0
- Author: Focus-GTS (https://skillmd.com/u/focus-gts)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/focus-gts/lightroom-api-batch

---


# Lightroom API — Batch Image Processing

The production pattern for applying Lightroom-grade adjustments to images at scale. Apply presets, normalize exposure, auto-tone, color grade — across hundreds or thousands of images consistently. Pair with Photoshop API for compositing or with Firefly for generation; Lightroom is the right tool for tonality and color.

## When to Use This Skill

Use this skill when:
- A campaign delivers a set of images that need to look like they were processed together
- Mixed-source images (different cameras, lighting conditions) need normalization
- A specific look or preset must be applied consistently
- Raw files (DNG, CR2, NEF, ARW) need processing before downstream use
- The user mentions Lightroom API, presets, auto-tone, batch color grading, or `image.adobe.io`

Do **NOT** use this skill when:
- The transformation is structural (layers, smart objects, compositing) — use Photoshop API
- The need is to *generate* new image content — use Firefly
- The pipeline only needs to resize/crop — that's also Lightroom-territory but minimal; this skill is overkill

## Mental Model

Lightroom API operations split into three families:

| Family | Operations |
|---|---|
| **Auto** | `auto_tone`, `auto_straighten` — Lightroom decides values |
| **Adjustments** | Set explicit `EditOptions` values for exposure, highlights, shadows, contrast, vibrance, saturation, and a `WhiteBalance` preset |
| **Preset** | Apply a `.xmp` Lightroom preset file |

A typical batch pipeline combines all three: auto-tone for baseline normalization, then preset for stylistic look, then fine-tune adjustments for specific shots.

## Step 1 — Apply a Preset

Lightroom presets are `.xmp` files containing the develop settings. They are produced in desktop Lightroom and exported.

```bash
curl --silent -X POST 'https://image.adobe.io/lrService/presets' \
  -H "Authorization: Bearer $FIREFLY_SERVICES_ACCESS_TOKEN" \
  -H "X-API-Key: $FIREFLY_SERVICES_CLIENT_ID" \
  -H 'Content-Type: application/json' \
  -d '{
    "inputs": {
      "source": {"href": "'"$SOURCE_IMAGE_URL"'", "storage": "external"},
      "presets": [{
        "href": "'"$PRESET_XMP_URL"'",
        "storage": "external"
      }]
    },
    "outputs": [{
      "href": "'"$OUTPUT_URL"'",
      "storage": "external",
      "type": "image/jpeg",
      "quality": 9
    }]
  }'
```

Response includes a status URL to poll. Same pattern as Photoshop API. (The `overwrite` output flag applies only to Adobe-storage outputs — omit it for external pre-signed URLs.)

## Step 2 — Apply Auto Adjustments

For "make these look consistent" without a specific preset, auto-tone is the right starting point:

```bash
curl --silent -X POST 'https://image.adobe.io/lrService/autoTone' \
  -H "Authorization: Bearer $FIREFLY_SERVICES_ACCESS_TOKEN" \
  -H "X-API-Key: $FIREFLY_SERVICES_CLIENT_ID" \
  -H 'Content-Type: application/json' \
  -d '{
    "inputs": {"href": "'"$SOURCE_IMAGE_URL"'", "storage": "external"},
    "outputs": [{"href": "'"$OUTPUT_URL"'", "storage": "external", "type": "image/jpeg"}]
  }'
```

Auto-tone analyzes the image and applies exposure, highlights, shadows, whites, and blacks. It doesn't touch color/style — preset on top of auto-tone is the standard combination.

## Step 3 — Manual Adjustments

For shot-specific tuning:

```bash
curl --silent -X POST 'https://image.adobe.io/lrService/edit' \
  -H "Authorization: Bearer $FIREFLY_SERVICES_ACCESS_TOKEN" \
  -H "X-API-Key: $FIREFLY_SERVICES_CLIENT_ID" \
  -H 'Content-Type: application/json' \
  -d '{
    "inputs": {
      "source": {"href": "'"$SOURCE_IMAGE_URL"'", "storage": "external"}
    },
    "options": {
      "Exposure": 0.5,
      "Highlights": -30,
      "Shadows": 25,
      "Whites": 10,
      "Blacks": -10,
      "Contrast": 10,
      "Vibrance": 15,
      "Saturation": 5,
      "WhiteBalance": "As Shot"
    },
    "outputs": [{"href": "'"$OUTPUT_URL"'", "storage": "external", "type": "image/jpeg"}]
  }'
```

Edit settings go **directly** under `options` (the `EditOptions` object) — there is no `adjustments` wrapper. Field names are **PascalCase**. White balance is set with the `WhiteBalance` **enum**, not a Kelvin temperature/tint pair.

### `EditOptions` fields

| Field | Range | Effect |
|---|---|---|
| `Exposure` | -5.0 to +5.0 | Overall brightness in EV; ±1 stop is typical |
| `Contrast` | -100 to +100 | Global contrast |
| `Highlights` | -100 to +100 | Brighter areas; negative recovers blown highlights |
| `Shadows` | -100 to +100 | Darker areas; positive opens shadows |
| `Whites` | -100 to +100 | White point |
| `Blacks` | -100 to +100 | Black point |
| `Vibrance` | -100 to +100 | Saturation of less-saturated colors only |
| `Saturation` | -100 to +100 | Saturation of all colors (use sparingly) |
| `Clarity` | -100 to +100 | Midtone contrast |
| `Dehaze` | -100 to +100 | Atmospheric haze removal/addition |
| `Texture` | -100 to +100 | Fine detail emphasis |
| `VignetteAmount` | -100 to +100 | Post-crop vignette |
| `Sharpness` | 0 to 150 | Sharpening amount |
| `SharpenRadius` | 0.5 to 3.0 | Sharpening radius |
| `SharpenDetail` | 0 to 100 | Sharpening detail |
| `SharpenEdgeMasking` | 0 to 10 | Sharpening edge masking |
| `NoiseReduction` | 0 to 100 | Luminance noise reduction |
| `ColorNoiseReduction` | 0 to 100 | Color noise reduction |
| `WhiteBalance` | enum (below) | White balance preset |

`WhiteBalance` is an enum, **not** a Kelvin temperature/tint pair. Valid values:

`As Shot` · `Auto` · `Cloudy` · `Custom` · `Daylight` · `Flash` · `Fluorescent` · `Shade` · `Tungsten`

## Step 4 — Composite Pipeline: Auto-Tone + Preset

The standard pattern for normalizing a mixed-source set:

```js
async function normalizeAndStyle({ sourceUrl, presetUrl, outputUrl }) {
  // Stage 1: auto-tone — write to an intermediate URL
  const intermediateUrl = await getSignedPutUrl(
    `intermediates/${randomUUID()}.jpg`,
    7200,
  );
  await submitAutoTone({ sourceUrl, outputUrl: intermediateUrl });
  await pollUntilSucceeded();

  // Stage 2: apply preset to the auto-toned intermediate
  const intermediateGetUrl = await getSignedGetUrl(
    `intermediates/${...}`,
    7200,
  );
  await submitPreset({
    sourceUrl: intermediateGetUrl,
    presetUrl,
    outputUrl,
  });
  await pollUntilSucceeded();
}
```

Two stages, two jobs. The intermediate file is a chunk of work; persist briefly, delete after final output is confirmed.

For a fully composed batch:

```
For each input image:
  1. auto_straighten
  2. auto_tone (sets exposure, highlights, shadows, whites, blacks)
  3. apply_preset (sets color/style)
  4. fine_tune_adjustments (per-image overrides if needed)
  5. Write to final destination
```

This produces a set of images that read as consistently processed. The exact recipe is brand-defined; the orchestration shape is universal.

## Step 5 — Raw File Processing

Lightroom API ingests raw formats directly:

```bash
curl --silent -X POST 'https://image.adobe.io/lrService/autoTone' \
  -H "Authorization: Bearer $FIREFLY_SERVICES_ACCESS_TOKEN" \
  -H "X-API-Key: $FIREFLY_SERVICES_CLIENT_ID" \
  -H 'Content-Type: application/json' \
  -d '{
    "inputs": {"href": "'"$RAW_FILE_URL"'", "storage": "external"},
    "outputs": [
      {"href": "'"$OUTPUT_JPEG_URL"'", "storage": "external", "type": "image/jpeg", "quality": 10}
    ]
  }'
```

`autoTone` accepts multiple outputs in one job — live-verified 2026-08-10: a single submission with both `"type": "image/jpeg"` and `"type": "image/x-adobe-dng"` outputs completed with both renditions `succeeded`. One job, one set of adjustments, delivery JPEG and DNG master together. (Note the input-shape asymmetry across Lightroom endpoints, also live-verified: `autoTone` takes a flat `inputs: {href, storage}` object, while `presets` wraps it as `inputs: {source: {...}, presets: [...]}`.)

## Production Patterns

### Pattern: Daily-shoot normalization

A real-estate photography pipeline: hundreds of raw shots per day from various sites, must look brand-consistent.

```
SQS queue (one message per shot)
  ↓
Worker:
  1. Get pre-signed read for raw
  2. Lightroom: auto_straighten → auto_tone → brand_preset
  3. Output as JPEG to delivery bucket
  4. Output as DNG to master bucket
  5. Write metadata to DynamoDB
```

Throughput is bounded by Lightroom API quota (typically 10 RPM default). Provisioned higher for production at-scale.

### Pattern: Campaign-set normalization

A campaign uses 50 hero shots from a single shoot, but lighting varied throughout the day. To make them read as one campaign:

```
1. Photographer flags one "reference" shot
2. Reference shot is auto-toned, then human-tuned (or pre-defined preset)
3. Export adjustments from reference as a .xmp preset
4. Apply preset to all 49 other shots via Lightroom API
5. Per-shot fine-tune only on outliers
```

This produces visual cohesion across a campaign set in minutes rather than hours.

### Pattern: Pre-processing for Firefly input

Firefly's reference-image generation works best on clean, well-exposed inputs. Lightroom pre-processing improves Firefly output quality:

```
Raw shot
  ↓ Lightroom: auto_tone + auto_straighten
Clean source
  ↓ Firefly: generate_similar / fill / expand
```

Skipping the Lightroom step often means Firefly produces output that inherits weird color shifts or exposure issues from the source.

## Validate

A Lightroom batch pipeline is production-ready when:

1. Preset files are versioned in object storage with content-hashes
2. Auto-tone + preset is the default; manual adjustments only on outliers
3. Intermediate files have a defined lifecycle (auto-delete after final output succeeds)
4. Pipeline is queue-fronted with rate-limit awareness
5. Output quality settings match the delivery use case (`quality: 8-10` for distribution, `quality: 12` for master)
6. Raw → JPEG conversion is end-to-end without losing color space information

## Troubleshooting & Edge Cases

- **Preset not applied:** The `.xmp` file is broken or has wrong format. Open in desktop Lightroom to validate, re-export.
- **Output looks blown out / clipped:** Auto-tone results vary by source quality. For better consistency, use a brand preset instead of auto-tone alone.
- **Raw file rejected:** Some camera-specific raw formats are unsupported. Check the [Lightroom API supported formats](https://developer.adobe.com/firefly-services/docs/lightroom/) list. For unsupported raws, convert to DNG first using Adobe DNG Converter.
- **Color shifts between batches:** White balance varies by source. Set a fixed `WhiteBalance` preset (e.g. `Daylight`) per-batch instead of relying on `As Shot`/auto.
- **Adjustments take effect but appear weaker than in desktop Lightroom:** API uses Camera Raw values; ranges may not map 1:1 to desktop sliders. Test and calibrate against a known reference.
- **Rate-limited at 10 RPM despite single workload:** Provisioned default is low. Request an increase from the customer's Adobe account team.

## Chaining with Other Skills

- `firefly-services-storage-refs` — Input/output URL management
- `firefly-services-auth` — Token retrieval
- `photoshop-api-composition` — Compose Lightroom-processed images into PSD templates
- `firefly-generate-similar` — Generate variations from Lightroom-cleaned sources
- `firefly-services-rate-limits` — Lightroom has separate quota from Firefly

## References

- [Lightroom API Documentation](https://developer.adobe.com/firefly-services/docs/lightroom/)
- [Adobe DNG Converter (for unsupported raws)](https://helpx.adobe.com/camera-raw/using/adobe-dng-converter.html)
- `firefly-services-bootstrap` — Lightroom API subscription

