Actions Migration
This guide helps you migrate from v1's Photoshop action endpoints to the unified /v2/execute-actions endpoint.
Overview
In v1, Photoshop actions were spread across multiple endpoints. In the v2 API, all action-based operations are consolidated into a single /v2/execute-actions endpoint, including:
- Traditional Photoshop
.atnaction files - ActionJSON (inline actions)
- Convenience APIs (productCrop, depthBlur, splitView, sideBySide)
- UXP scripts (limited availability)
The v1 endpoints being consolidated are:
/pie/psdService/photoshopActions/v2/execute-actions with .atn files/pie/psdService/actionJSON/v2/execute-actions with inline actionJSON/pie/psdService/productCrop/v2/execute-actions with published action/pie/psdService/depthBlur/pie/psdService/splitView/v2/execute-actions with published action/pie/psdService/sideBySide/v2/execute-actions with published actionBenefits of the unified endpoint
- Single Integration Point: One endpoint for all action-based workflows
- Multiple Actions: Execute multiple actions in sequence
- Additional Resources: Reference brushes, patterns, fonts, and additional images
- Published Actions: Access to previously server-side action files
- Script-Based Outputs: Use
scriptOutputPatternto capture files generated by UXP scripts
Traditional Photoshop actions
Using .atn files
Key Differences:
- Input structure changed from
inputs[].hreftoimage.source.url - Action structure changed from
options.actions[].hreftooptions.actions[].source.url - Output structure uses
destinationobject withurl - Media type changed from
typetomediaType storageTypeis optional for external presigned URLs and only required for Azure or Dropbox
V1 Approach:
curl -X POST \
https://image.adobe.io/pie/psdService/photoshopActions \
-H "Authorization: Bearer $token" \
-H "x-api-key: $apiKey" \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"href": "<SIGNED_GET_URL>",
"storage": "external"
}
],
"options": {
"actions": [
{
"href": "<ACTION_FILE_URL>",
"storage": "external"
}
]
},
"outputs": [
{
"href": "<SIGNED_POST_URL>",
"storage": "external",
"type": "image/vnd.adobe.photoshop"
}
]
}'
V2 Approach:
curl -X POST \
https://photoshop-api.adobe.io/v2/execute-actions \
-H "Authorization: Bearer $token" \
-H "x-api-key: $apiKey" \
-H "Content-Type: application/json" \
-d '{
"image": {
"source": {
"url": "<SIGNED_GET_URL>"
}
},
"options": {
"actions": [
{
"source": {
"url": "<ACTION_FILE_URL>"
}
}
]
},
"outputs": [
{
"destination": {
"url": "<SIGNED_POST_URL>"
},
"mediaType": "image/vnd.adobe.photoshop"
}
]
}'
Multiple actions
In the v2 API, you can execute multiple actions in sequence. A maximum of 10 actions allowed per request. Actions execute in the order specified.
curl -X POST \
https://photoshop-api.adobe.io/v2/execute-actions \
-H "Authorization: Bearer $token" \
-H "x-api-key: $apiKey" \
-H "Content-Type: application/json" \
-d '{
"image": {
"source": {
"url": "<SIGNED_GET_URL>"
}
},
"options": {
"actions": [
{
"source": {
"url": "<ACTION_1_URL>"
}
},
{
"source": {
"url": "<ACTION_2_URL>"
}
},
{
"source": {
"url": "<ACTION_3_URL>"
}
}
]
},
"outputs": [
{
"destination": {
"url": "<SIGNED_POST_URL>"
},
"mediaType": "image/vnd.adobe.photoshop"
}
]
}'
Using inline ActionJSON
Key Differences:
- ActionJSON is now provided as inline
contentin the action source contentType: "application/json"must be specified- ActionJSON should now be stringified JSON
- All other structures follow the same pattern as
.atnfiles
V1 Approach:
curl -X POST \
https://image.adobe.io/pie/psdService/actionJSON \
-H "Authorization: Bearer $token" \
-H "x-api-key: $apiKey" \
-H "Content-Type: application/json" \
-d '{
"inputs": [
{
"href": "<SIGNED_GET_URL>",
"storage": "external"
}
],
"options": {
"actionJSON": [
{
"_obj": "convertMode",
"to": {
"_enum": "colorSpaceMode",
"_value": "grayscale"
}
}
]
},
"outputs": [
{
"href": "<SIGNED_POST_URL>",
"storage": "external",
"type": "image/jpeg"
}
]
}'
V2 Approach:
curl -X POST \
https://photoshop-api.adobe.io/v2/execute-actions \
-H "Authorization: Bearer $token" \
-H "x-api-key: $apiKey" \
-H "Content-Type: application/json" \
-d '{
"image": {
"source": {
"url": "<SIGNED_GET_URL>"
}
},
"options": {
"actions": [
{
"source": {
"content": "[{\"_obj\":\"convertMode\",\"to\":{\"_enum\":\"colorSpaceMode\",\"_value\":\"grayscale\"}}]",
"contentType": "application/json"
}
}
]
},
"outputs": [
{
"destination": {
"url": "<SIGNED_POST_URL>"
},
"mediaType": "image/jpeg"
}
]
}'
Additional contents in actions
ActionJSON can reference additional contents using placeholders. This is useful when your actions need to composite or reference multiple contents.
How It Works:
- Provide additional contents in the
options.additionalContentsarray. A maximum of 25 additional contents are allowed per request. - Reference them in ActionJSON using
__ADDITIONAL_CONTENTS_PATH_0__,__ADDITIONAL_CONTENTS_PATH_1__, etc. - The placeholder index corresponds to the array index (0-based).
Example:
curl -X POST \
https://photoshop-api.adobe.io/v2/execute-actions \
-H "Authorization: Bearer $token" \
-H "x-api-key: $apiKey" \
-H "Content-Type: application/json" \
-d '{
"image": {
"source": {
"url": "<BASE_IMAGE_URL>"
}
},
"options": {
"additionalContents": [
{
"source": {
"url": "<OVERLAY_IMAGE_URL>"
}
},
{
"source": {
"url": "<BACKGROUND_IMAGE_URL>"
}
}
],
"actions": [
{
"source": {
"content": "[{\"_obj\":\"placeEvent\",\"null\":{\"_path\":\"__ADDITIONAL_CONTENTS_PATH_0__\",\"_kind\":\"local\"}}]",
"contentType": "application/json"
}
}
]
},
"outputs": [
{
"destination": {
"url": "<SIGNED_POST_URL>"
},
"mediaType": "image/jpeg"
}
]
}'
Additional resources
Actions can reference additional resources like brushes, patterns, and fonts. A maximum of 10 items are allowed for each resource type.
{
"options": {
"actions": [...],
"brushes": [
{
"source": {
"url": "<BRUSH_FILE_URL>"
}
}
],
"patterns": [
{
"source": {
"url": "<PATTERN_FILE_URL>"
}
}
],
"fontOptions": {
"additionalFonts": [
{
"source": {
"url": "<FONT_FILE_URL>"
}
}
],
"missingFontStrategy": "use_default"
}
}
}
Inline content usage
The /v2/execute-actions endpoint supports inline content (embedded content within the API request) for text-based resources only. Understanding which resources support inline content is important for proper API usage.
data-variant=warning
data-slots=header, text
Supported: Actions and UXP scripts
Inline content is allowed for:
- ActionJSON - Inline JSON action definitions
- UXP Scripts - Inline JavaScript code
Example: Inline ActionJSON
{
"options": {
"actions": [
{
"source": {
"content": "[{\"_obj\":\"gaussianBlur\",\"radius\":{\"_unit\":\"pixelsUnit\",\"_value\":5}}]",
"contentType": "application/json"
}
}
]
}
}
Note: The content field must contain stringified JSON (a JSON string), not a direct JSON object or array. The ActionJSON array is converted to a string using JSON.stringify().
Example: Inline UXP Script
{
"options": {
"uxp": {
"source": {
"content": "const doc = app.activeDocument; doc.flatten();",
"contentType": "application/javascript"
}
}
}
}
Not supported: Binary resources
Inline content is NOT allowed for:
- Images - Main input image or additional images
- Brushes -
.abrbrush files - Patterns -
.patpattern files - Fonts - Font files
These binary resources must be provided as external file references using:
url- Presigned URL for external storagecreativeCloudPath- Adobe Creative Cloud storage pathcreativeCloudFileId- Creative Cloud file identifierlightroomPath- Adobe Lightroom storage path (for images only)
Correct Usage: External References
{
"image": {
"source": {
"url": "<SIGNED_IMAGE_URL>"
}
},
"options": {
"additionalContents": [
{
"source": {
"url": "<ADDITIONAL_IMAGE_URL>"
}
}
],
"brushes": [
{
"source": {
"url": "<BRUSH_FILE_URL>"
}
}
],
"patterns": [
{
"source": {
"url": "<PATTERN_FILE_URL>"
}
}
]
}
}
Incorrect Usage: Attempting Inline Content
{
"options": {
"brushes": [
{
"content": "base64_encoded_data..." // NOT SUPPORTED
}
]
}
}
data-variant=warning
data-slots=text
Migrating Convenience APIs
V1 had several convenience APIs that used predefined server-side action files. In v2, these action files are published and available for you to use, customize, and learn from.
Key Benefits:
- Access to the exact ActionJSON definitions Adobe uses
- Full customization of all parameters and workflows
- Learn from Adobe's implementation
- Create your own variants and extensions
- Consistent with the unified
/v2/execute-actionsendpoint
Available convenience APIs
Each convenience API has a detailed migration guide with complete ActionJSON definitions, customization examples, and use cases:
/pie/psdService/productCrop/pie/psdService/splitView/pie/psdService/sideBySidedata-variant=info
data-slots=text
Script-based output discovery
When using UXP scripts that generate files, use the scriptOutputPattern parameter to specify which files should be captured as outputs. This supports both exact filenames and glob patterns for dynamic output discovery.
Supported Destination Types:
data-variant=warning
data-slots=text
scriptOutputPattern only works with embedded and hosted destinations. External storage (presigned URLs, Dropbox, Azure) is not supported for script-generated outputs.Example - Single File:
{
"outputs": [
{
"destination": {
"hosted": true
},
"mediaType": "image/jpeg",
"scriptOutputPattern": "final_composite.jpg"
}
]
}
Example - Multiple Files with Glob Pattern:
{
"outputs": [
{
"destination": {
"embedded": "json"
},
"mediaType": "application/json",
"scriptOutputPattern": "*.json"
},
{
"destination": {
"hosted": true
},
"mediaType": "image/png",
"scriptOutputPattern": "result-*.png"
}
]
}
How It Works:
- UXP scripts generate files to the plugin temporary directory
- The system matches files using the pattern and filters by
mediaType - Each matched file becomes a separate output entry
- Files are uploaded to the specified destination
Glob Pattern Examples:
output.json- Matches single specific file*.json- Matches all JSON filesresult-*.png- Matches result-1.png, result-2.png, etc.layer-[0-9].jpg- Matches layer-0.jpg through layer-9.jpg
data-variant=info
data-slots=text
scriptOutputPattern is specified, output parameters like quality, compression, width, and height are ignored. Files are used as-is from the script's output directory. Keep glob patterns simple and use standard glob syntax (e.g., *.json, result-*.png, layer-[0-9].jpg).UXP scripts (limited availability)
UXP scripts provide powerful automation capabilities with modern JavaScript. This section is for approved users only.
UXP Script Basics
UXP scripts can be provided as external .psjs (Photoshop JavaScript) file or inline content:
External UXP Script:
{
"options": {
"uxp": {
"source": {
"url": "<UXP_SCRIPT_URL>"
}
}
}
}
Inline UXP Script:
{
"options": {
"uxp": {
"source": {
"content": "const app = require('photoshop').app; app.activeDocument.flatten();",
"contentType": "application/javascript"
}
}
}
}
UXP output configuration
UXP scripts can only access the UXP plugin temporary directory:
How It Works:
-
Write to Plugin Temp Directory:
- Use the literal path
plugin-temp:/filename.extin your UXP script - Or use the UXP module's
storage.localFileSystemobject to access plugin temporary directory - It automatically maps
plugin-temp:/to the correct temporary directory
- Use the literal path
-
Specify Output Pattern:
- Define
scriptOutputPatternin your output configuration to specify which files to capture - Supports exact filenames (e.g.,
"result.json") or glob patterns (e.g.,"*.png","layer-*.jpg") - Only files matching both the pattern and
mediaTypeare captured as outputs
- Define
-
File Discovery and Upload:
- After UXP execution completes, the worker scans the plugin temp directory
- Matches files against your
scriptOutputPatternand filters bymediaType - Each matched file becomes a separate output entry in the job result
- Files are uploaded to the specified destination (
hosted,embedded, or external storage)
Example UXP Script:
const { app } = require("photoshop");
const fs = require("fs");
const path = require("path");
// using plugin-temp: to access plugin temporary directory
const outputFile = 'plugin-temp:/generated.json';
async function main() {
// Generate your output data
const result = {
documentName: app.activeDocument.name,
layers: app.activeDocument.layers.length,
timestamp: new Date().toISOString(),
};
// Save output files to the UXP temp folder
fs.writeFileSync(outputFile, JSON.stringify(result, null, 2));
console.log("Output saved to:", outputFile);
}
main().catch((err) => {
console.error("Error:", err);
process.exit(1);
});
Using scriptOutputPattern with UXP:
curl -X POST \
https://photoshop-api.adobe.io/v2/execute-actions \
-H "Authorization: Bearer $token" \
-H "x-api-key: $apiKey" \
-H "Content-Type: application/json" \
-d '{
"image": {
"source": {
"url": "<SIGNED_GET_URL>"
}
},
"options": {
"uxp": {
"source": {
"url": "<UXP_SCRIPT_URL>"
}
}
},
"outputs": [
{
"destination": {
"hosted": true
},
"mediaType": "application/json",
"scriptOutputPattern": "result.json"
},
{
"destination": {
"hosted": true
},
"mediaType": "image/jpeg",
"scriptOutputPattern": "processed_image.jpg"
}
]
}'
Additional contents in UXP scripts
UXP scripts can reference additional contents using placeholders, similar to ActionJSON. This is useful when your scripts need to access or composite multiple images.
How It Works:
- Provide additional contents in the
options.additionalContentsarray - Reference them in UXP scripts using
__ADDITIONAL_CONTENTS_PATH_0__,__ADDITIONAL_CONTENTS_PATH_1__, etc. - The placeholder index corresponds to the array index (0-based)
- The worker automatically replaces these placeholders with actual file paths before execution
Example:
curl -X POST \
https://photoshop-api.adobe.io/v2/execute-actions \
-H "Authorization: Bearer $token" \
-H "x-api-key: $apiKey" \
-H "Content-Type: application/json" \
-d '{
"image": {
"source": {
"url": "<BASE_IMAGE_URL>"
}
},
"options": {
"additionalContents": [
{
"source": {
"url": "<OVERLAY_IMAGE_URL>"
}
},
{
"source": {
"url": "<LOGO_IMAGE_URL>"
}
}
],
"uxp": {
"source": {
"content": "const { app } = require(\"photoshop\"); const fs = require(\"fs\"); async function main() { const overlayPath = \"__ADDITIONAL_CONTENTS_PATH_0__\"; const logoPath = \"__ADDITIONAL_CONTENTS_PATH_1__\"; await app.batchPlay([{ _obj: \"placeEvent\", null: { _path: overlayPath, _kind: \"local\" } }], {}); fs.writeFileSync(\"plugin-temp:/result.json\", JSON.stringify({ overlayUsed: overlayPath, logoUsed: logoPath })); } main();",
"contentType": "application/javascript"
}
}
},
"outputs": [
{
"destination": {
"hosted": true
},
"mediaType": "application/json",
"scriptOutputPattern": "result.json"
}
]
}'
data-variant=info
data-slots=text
Best Practices for UXP Scripts:
- File Extension: UXP scripts use
.psjsextension (Photoshop JavaScript) to distinguish them from generic JavaScript files - Output Path: Use the
__UXP_OUTPUT_PATH__placeholder to reference the output directory--------- - Path Handling: Use
path.join()to construct file paths - NEVER use hardcoded separators like"/"or"\\" - Path Utilities: Use
path.dirname(),path.basename(), and other Node.js path module functions - Platform-Agnostic: Avoid platform-specific code or APIs (Windows/macOS/Linux)
- Descriptive Names: Use descriptive filenames for outputs
- Output Patterns: Specify the filename or pattern in
scriptOutputPatternparameter - Destinations: Use
embeddedorhosteddestinations (external storage not supported for script outputs) - Media Types: Filter outputs by
mediaType- only matching file extensions are included - Error Handling: Handle errors gracefully in your UXP scripts
- Testing: Test thoroughly before production use
data-variant=warning
data-slots=text
path.join() for constructing paths (e.g., path.join(outputFolder, "result.json") instead of outputFolder + "/result.json"). This prevents runtime issues across different operating systems.data-variant=warning
data-slots=text
Multiple outputs
You can specify multiple outputs in different formats. A maximum of 25 outputs are allowed per request.
{
"outputs": [
{
"destination": {
"url": "<JPEG_URL>"
},
"mediaType": "image/jpeg",
"quality": "maximum"
},
{
"destination": {
"url": "<PNG_URL>"
},
"mediaType": "image/png",
"compression": "medium"
},
{
"destination": {
"url": "<PSD_URL>"
},
"mediaType": "image/vnd.adobe.photoshop"
},
{
"destination": {
"embedded": "json"
},
"mediaType": "application/json"
}
]
}
Common migration issues
Action file not found
❌ Problem: Using v1-style action reference
"actions": [
{
"href": "<ACTION_URL>",
"storage": "external"
}
]
✅ Solution: Use v2 source structure
"actions": [
{
"source": {
"url": "<ACTION_URL>"
}
}
]
Additional contents not working
❌ Problem: Incorrect placeholder format
"content": "[{\"file\":\"additional_contents_path_0\"}]"
✅ Solution: Use correct placeholder format
"content": "[{\"file\":\"__ADDITIONAL_CONTENTS_PATH_0__\"}]"
Invalid contentType
❌ Problem: Missing or incorrect contentType for inline content
✅ Solution: Always specify contentType for inline content
{
"source": {
"content": "...",
"contentType": "application/json"
}
}
Next steps
- Review the storage solutions guide for storage options
- Check the composite migration guide for layer operations
- Test your action workflows with development endpoints
Need help?
Contact the Adobe DI ART Service team for:
- Published action file access
- UXP script approval and vetting
- Migration technical support