Execute Actions Workflow

Learn how to automate Photoshop operations using Actions and UXP Scripts with the /v2/execute-actions endpoint.

The execute-actions workflow allows you to execute Photoshop operations programmatically using Actions (JSON-formatted action descriptors and traditional binary .atn files) and UXP Scripts (modern JavaScript automation).

Key capabilities include:

Recommendation: Use Actions for image manipulation and effects. Use UXP Scripts when you need conditional logic, data extraction, or complex decision-making.

Prerequisites

Before you begin, ensure you have:

  1. Authentication token: Follow the Authentication guide to get your OAuth 2.0 Bearer token
  2. API key: Your Client ID from the Adobe Developer Console
  3. Image storage: A publicly accessible URL for your input image (S3, Azure, Dropbox, or any pre-signed URL)

Set up your environment variables:

export TOKEN="your_access_token"
export API_KEY="your_client_id"

Request structure

All execute-actions requests follow this pattern:

{
  "image": {
    "source": {
      "url": "<INPUT_IMAGE_URL>"
    }
  },
  "options": {
    "actions": [/* Action definitions */],
    "uxp": {/* UXP script */},
    "additionalContents": [/* Additional images */]
  },
  "outputs": [
    {
      "mediaType": "<OUTPUT_FORMAT>",
      "destination": {
        "validityPeriod": 3600
      },
      "scriptOutputPattern": "<FILENAME_OR_PATTERN>"
    }
  ]
}

Key fields:

Working with actions

Actions allow you to automate Photoshop operations using JSON-formatted descriptors or traditional .atn files. You can provide actions inline or reference external files.

Example: warm vintage film look

Apply a warm vintage effect with sepia tones and adjusted levels:

curl -X POST "https://photoshop-api.adobe.io/v2/execute-actions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "image": {
    "source": {
      "url": "https://your-storage.com/input-image.jpg"
    }
  },
  "options": {
    "actions": [
      {
        "source": {
          "content": "[{\"_obj\":\"make\",\"_target\":[{\"_ref\":\"adjustmentLayer\"}],\"using\":{\"_obj\":\"adjustmentLayer\",\"type\":{\"_obj\":\"hueSaturation\",\"colorize\":true,\"hue\":{\"_unit\":\"angleUnit\",\"_value\":30},\"saturation\":{\"_unit\":\"percentUnit\",\"_value\":25},\"lightness\":{\"_unit\":\"percentUnit\",\"_value\":0}}}},{\"_obj\":\"make\",\"_target\":[{\"_ref\":\"adjustmentLayer\"}],\"using\":{\"_obj\":\"adjustmentLayer\",\"type\":{\"_obj\":\"levels\",\"adjustment\":[{\"_obj\":\"levelRecord\",\"channel\":{\"_ref\":\"channel\",\"_enum\":\"channel\",\"_value\":\"composite\"},\"inputFloor\":10,\"inputCeiling\":245,\"outputFloor\":15,\"outputCeiling\":255,\"gamma\":1.1}]}}},{\"_obj\":\"make\",\"_target\":[{\"_ref\":\"adjustmentLayer\"}],\"using\":{\"_obj\":\"adjustmentLayer\",\"type\":{\"_obj\":\"brightnessEvent\",\"brightness\":5,\"contrast\":-5}}}]",
          "contentType": "application/json"
        }
      }
    ]
  },
  "outputs": [
    {
      "mediaType": "image/jpeg",
      "destination": {
        "validityPeriod": 3600
      }
    }
  ]
}'

Working with UXP scripts

The execute-actions endpoint supports UXP (Unified Extensibility Platform) scripts, allowing you to leverage modern JavaScript (ES6+) for advanced automation.

Example: flatten document and export

Flatten all layers in a PSD file and export as JPEG:

curl -X POST "https://photoshop-api.adobe.io/v2/execute-actions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "image": {
    "source": {
      "url": "https://your-storage.com/input.psd"
    }
  },
  "options": {
    "uxp": {
      "source": {
        "content": "const { app } = require(\"photoshop\"); async function main() { await app.activeDocument.flatten(); } main();",
        "contentType": "application/javascript"
      }
    }
  },
  "outputs": [
    {
      "mediaType": "image/jpeg",
      "destination": {
        "validityPeriod": 3600
      }
    }
  ]
}'

Sandboxing

UXP scripts run in a sandboxed environment for security. The sandbox restricts file system access (limited to plugin-temp:/ for output and additionalContents for input), blocks network requests, prevents shell command execution, and isolates scripts from system resources.

Restricted operations:

Working with files:

To work with files in UXP scripts, use the plugin-temp:/ directory for all file I/O operations. Write any output files (JSON, text, logs, metadata) to plugin-temp:/filename, and they will be automatically captured as outputs when you specify a matching scriptOutputPattern. For reading files, use the additionalContents array with placeholder syntax (__ADDITIONAL_CONTENTS_PATH_0__) to access external files passed to your script.

Example: writing and reading files

This example demonstrates file I/O within the sandbox by writing processing results and reading configuration data:

curl -X POST "https://photoshop-api.adobe.io/v2/execute-actions" \
  -H "Authorization: Bearer $TOKEN" \
  -H "x-api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "image": {
    "source": {
      "url": "https://your-storage.com/input.psd"
    }
  },
  "options": {
    "additionalContents": [
      {
        "source": {
          "url": "https://your-storage.com/config.json"
        }
      }
    ],
    "uxp": {
      "source": {
        "content": "const { app } = require(\"photoshop\"); const fs = require(\"fs\"); async function main() { const configPath = \"__ADDITIONAL_CONTENTS_PATH_0__\"; const config = JSON.parse(fs.readFileSync(configPath, \"utf-8\")); const doc = app.activeDocument; if (config.applyResize && doc.width > config.maxWidth) { await app.batchPlay([{\"_obj\":\"imageSize\",\"width\":{\"_unit\":\"pixelsUnit\",\"_value\":config.maxWidth},\"constrainProportions\":true}], {}); } const report = { originalSize: { width: doc.width, height: doc.height }, configApplied: config, processedAt: new Date().toISOString() }; fs.writeFileSync(\"plugin-temp:/process-report.json\", JSON.stringify(report, null, 2)); } main();",
        "contentType": "application/javascript"
      }
    }
  },
  "outputs": [
    {
      "mediaType": "image/jpeg",
      "destination": {
        "validityPeriod": 3600
      }
    },
    {
      "mediaType": "application/json",
      "destination": {
        "validityPeriod": 3600
      },
      "scriptOutputPattern": "process-report.json"
    }
  ]
}'

Advanced features

Executing a specific action by name

When an .atn file contains multiple named actions, use actionName to execute only one of them. If actionName is omitted, all actions in the file run in order.

{
  "options": {
    "actions": [
      {
        "source": {
          "url": "https://your-storage.com/my-actions.atn"
        },
        "actionName": "Vignette Effect"
      }
    ]
  }
}

You can mix targeted and full-execution actions in the same request:

{
  "options": {
    "actions": [
      {
        "source": {
          "url": "https://your-storage.com/my-actions.atn"
        },
        "actionName": "Resize to Web"
      },
      {
        "source": {
          "url": "https://your-storage.com/color-grading.atn"
        }
      }
    ]
  }
}

In the example above, only "Resize to Web" runs from the first file, while all actions run from the second file.

Multiple actions in sequence

Execute up to 10 actions one after another:

{
  "options": {
    "actions": [
      {
        "source": {
          "content": "[{\"_obj\":\"imageSize\",\"width\":{\"_unit\":\"pixelsUnit\",\"_value\":800}}]",
          "contentType": "application/json"
        }
      },
      {
        "source": {
          "url": "https://your-storage.com/color-correction.json"
        }
      },
      {
        "source": {
          "url": "https://your-storage.com/add-watermark.atn"
        }
      }
    ]
  }
}

Combining actions with UXP scripts

You can combine multiple actions with one UXP script in a single request. Actions execute first, then the UXP script runs.

data-variant=info
data-slots=text
Only one UXP script is allowed per request, but you can include up to 10 actions.
{
  "options": {
    "actions": [
      {
        "source": {
          "content": "[{\"_obj\":\"imageSize\",\"width\":{\"_unit\":\"pixelsUnit\",\"_value\":1920}}]",
          "contentType": "application/json"
        }
      }
    ],
    "uxp": {
      "source": {
        "content": "const app = require('photoshop').app; const doc = app.activeDocument; /* custom UXP code */",
        "contentType": "application/javascript"
      }
    }
  }
}

Using additional contents

Composite multiple images using placeholders. Use __ADDITIONAL_CONTENTS_PATH_{index}__ in your action JSON or UXP script where {index} is the zero-based index of the additionalContents array.

{
  "image": {
    "source": {
      "url": "https://your-storage.com/background.jpg"
    }
  },
  "options": {
    "additionalContents": [
      {
        "source": {
          "url": "https://your-storage.com/logo.png"
        }
      },
      {
        "source": {
          "url": "https://your-storage.com/overlay.png"
        }
      }
    ],
    "actions": [
      {
        "source": {
          "content": "[{\"_obj\":\"placeEvent\",\"null\":{\"_kind\":\"local\",\"_path\":\"__ADDITIONAL_CONTENTS_PATH_0__\"}}]",
          "contentType": "application/json"
        }
      }
    ]
  }
}

Script output patterns

Capture files created by UXP scripts using glob patterns:

{
  "outputs": [
    {
      "mediaType": "image/png",
      "destination": {
        "validityPeriod": 3600
      },
      "scriptOutputPattern": "layer-*.png"
    }
  ]
}

Loading custom resources

Fonts:

{
  "options": {
    "fontOptions": {
      "additionalFonts": [
        {
          "source": {
            "url": "https://your-storage.com/custom-font.ttf"
          }
        }
      ]
    }
  }
}

Brushes and patterns:

{
  "options": {
    "brushes": [
      {
        "source": {
          "url": "https://your-storage.com/custom-brush.abr"
        }
      }
    ],
    "patterns": [
      {
        "source": {
          "url": "https://your-storage.com/custom-pattern.pat"
        }
      }
    ]
  }
}

Common issues

Issue: invalid content type

Error: Invalid contentType for inline action

Solution: Always specify "contentType": "application/json" for actions and "contentType": "application/javascript" for UXP scripts.

Issue: placeholder not replaced

Error: File path contains literal __ADDITIONAL_CONTENTS_PATH_0__

Solution: Ensure you've provided additionalContents array in options and the index matches (0-based).

Issue: script output not found

Error: No files matching pattern

Solution: Verify your UXP script writes to plugin-temp:/filename and scriptOutputPattern matches the filename.