adobe-pack
Claude Code skill pack for Adobe (30 skills)
Installation
Open Claude Code and run this command:
/plugin install adobe-pack@claude-code-plugins-plus
Use --global to install for all projects, or --project for current project only.
What It Does
> 30 production-grade skills for Adobe Firefly Services, PDF Services, Photoshop API, Lightroom API, and I/O Events. Real OAuth Server-to-Server auth, real endpoints, real SDK patterns.
Skills (30)
'Apply advanced debugging techniques for Adobe API issues: IMS token.
Adobe Advanced Troubleshooting
Overview
Deep debugging techniques for complex Adobe API issues that resist standard troubleshooting: IMS token problems, Firefly async job failures, PDF Services edge cases, and network-layer diagnostics.
Prerequisites
- Access to production logs and metrics
curlwith verbose mode for HTTP debugging- Understanding of OAuth 2.0 token flows
- Network capture tools (
tcpdump,openssl s_client)
Instructions
Technique 1: IMS Token Introspection
When auth issues occur, decode the access token to check claims:
# Adobe IMS tokens are JWTs — decode the payload (middle segment)
TOKEN=$(curl -s -X POST 'https://ims-na1.adobelogin.com/ims/token/v3' \
-d "client_id=${ADOBE_CLIENT_ID}&client_secret=${ADOBE_CLIENT_SECRET}&grant_type=client_credentials&scope=${ADOBE_SCOPES}" | jq -r '.access_token')
# Decode JWT payload (base64url-decode the middle segment)
echo "$TOKEN" | cut -d. -f2 | tr '_-' '/+' | base64 -d 2>/dev/null | python3 -m json.tool
# Look for:
# - "exp": expiration timestamp (is it expired?)
# - "iss": should be "ims-na1.adobelogin.com"
# - "as": scopes granted (do they match what you requested?)
# - "client_id": verify it matches your ADOBE_CLIENT_ID
Technique 2: Verbose HTTP Request Tracing
# Full HTTP trace against Firefly API
curl -v -X POST 'https://firefly-api.adobe.io/v3/images/generate' \
-H "Authorization: Bearer ${TOKEN}" \
-H "x-api-key: ${ADOBE_CLIENT_ID}" \
-H "Content-Type: application/json" \
-d '{"prompt":"test","n":1,"size":{"width":512,"height":512}}' 2>&1 | tee firefly-debug.log
# Check for:
# - TLS handshake issues (look for SSL/TLS lines)
# - Request headers actually sent
# - Response headers (Retry-After, x-request-id, x-adobe-*)
# - Response body with error details
Technique 3: Firefly Async Job Failure Analysis
// When async Firefly jobs fail, the status endpoint returns error details
async function diagnoseFireflyJob(jobId: string, statusUrl: string) {
const token = await getAccessToken();
const response = await fetch(statusUrl, {
headers: {
'Authorization': `Bearer ${token}`,
'x-api-key': process.env.ADOBE_CLIENT_ID!,
},
});
const status = await response.json();
console.log('=== Firefly Job Diagnosis ===');
console.log('Job ID:', jobId);
console.log('Status:', status.status);
if (status.status === 'failed') {
console.log('Error code:', status.error?.code);
console.log('Error'Choose and implement Adobe architecture blueprints: standalone SDK integration,.
Adobe Architecture Variants
Overview
Three validated architecture blueprints for Adobe integrations: (A) direct SDK integration in existing app, (B) Adobe App Builder with Runtime actions, and (C) dedicated microservice with event-driven pipelines.
Prerequisites
- Understanding of team size and throughput requirements
- Decision on which Adobe APIs to use (Firefly, PDF, Photoshop, Events)
- Knowledge of deployment infrastructure
- Growth projections for API usage
Instructions
Variant A: Direct SDK Integration (Simple)
Best for: MVPs, small teams (1-5), < 100 API calls/day, single Adobe API
my-app/
├── src/
│ ├── adobe/
│ │ ├── auth.ts # OAuth token management
│ │ ├── firefly.ts # or pdf-services.ts — one API client
│ │ └── types.ts
│ ├── routes/
│ │ └── api/
│ │ └── generate.ts # Direct API call in route handler
│ └── index.ts
├── .env # ADOBE_CLIENT_ID, ADOBE_CLIENT_SECRET
└── package.json # @adobe/firefly-apis or @adobe/pdfservices-node-sdk
// Direct integration — API call in route handler
app.post('/api/generate', async (req, res) => {
try {
const token = await getCachedToken();
const result = await fetch('https://firefly-api.adobe.io/v3/images/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'x-api-key': process.env.ADOBE_CLIENT_ID!,
'Content-Type': 'application/json',
},
body: JSON.stringify({ prompt: req.body.prompt, n: 1, size: { width: 1024, height: 1024 } }),
});
res.json(await result.json());
} catch (error: any) {
res.status(500).json({ error: error.message });
}
});
Pros: Fastest to build, simplest deployment, no extra infrastructure
Cons: No background processing, route handler blocks for 5-30s on Firefly calls
Variant B: Adobe App Builder (Native Adobe)
Best for: Adobe-centric workflows, teams using Adobe ecosystem, event-driven CC Library automation
my-adobe-app/
├── actions/ # Runtime actions (serverless functions)
│ ├── generate-image/
│ │ └── index.js # Firefly image generation action
│ ├── extract-pdf/
│ │ └── index.js # PDF extraction action
│ └── webhook-handler/
│ └── index.js # I/O Events webhook processor
├── web-src/ # Optional frontend (React/SPA)
│ └── src/
├── app.config.yaml # App Builder configuration
├── .aio # AIO CLI configuration
└── package.json
# app.config.yaml
application'Configure CI/CD pipelines for Adobe integrations with GitHub Actions,.
Adobe CI Integration
Overview
Set up CI/CD pipelines for Adobe API integrations with proper credential management, unit/integration test separation, and secret scanning for Adobe-specific credential patterns.
Prerequisites
- GitHub repository with Actions enabled
- Adobe Developer Console credentials for CI (separate from production)
- npm/pnpm project with vitest configured
Instructions
Step 1: Store Adobe Credentials as GitHub Secrets
# Set OAuth Server-to-Server credentials
gh secret set ADOBE_CLIENT_ID --body "your-ci-client-id"
gh secret set ADOBE_CLIENT_SECRET --body "your-ci-client-secret"
gh secret set ADOBE_SCOPES --body "openid,AdobeID,firefly_api"
Step 2: Create CI Workflow
# .github/workflows/adobe-integration.yml
name: Adobe Integration Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
# Unit tests run with mocked Adobe APIs — no credentials needed
secret-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan for Adobe credentials
run: |
FOUND=0
# Adobe OAuth client secrets start with p8_
if grep -rE "p8_[A-Za-z0-9_-]{20,}" --include="*.ts" --include="*.js" --include="*.py" --include="*.json" . 2>/dev/null; then
echo "::error::Adobe client_secret pattern found in source"
FOUND=1
fi
# Adobe IMS access tokens
if grep -rE "eyJ[A-Za-z0-9_-]{50,}" --include="*.ts" --include="*.js" . 2>/dev/null; then
echo "::warning::Potential Adobe access token found"
fi
exit $FOUND
integration-tests:
needs: [unit-tests, secret-scan]
runs-on: ubuntu-latest
# Only run on main branch (uses real API credentials)
if: github.ref == 'refs/heads/main'
env:
ADOBE_CLIENT_ID: ${{ secrets.ADOBE_CLIENT_ID }}
ADOBE_CLIENT_SECRET: ${{ secrets.ADOBE_CLIENT_SECRET }}
ADOBE_SCOPES: ${{ secrets.ADOBE_SCOPES }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- run: npm ci
- name: Verify Adobe OAuth credentials
run: |
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST \
'https://ims-na1.adobelogin.com/ims/token/v3' \
-d "client_id=${ADOBE_CLIENT_ID}&client_secret=${AD'Diagnose and fix common Adobe API errors across Firefly Services, PDF.
Adobe Common Errors
Overview
Quick reference for the most common errors across Adobe APIs with real error messages, root causes, and verified fixes.
Prerequisites
- Adobe SDK or API credentials configured
- Access to Adobe Developer Console (https://developer.adobe.com/console)
- Access to error logs or API responses
Instructions
Step 1: Identify the HTTP Status Code and Error Body
Adobe APIs return structured error responses:
{
"error_code": "403003",
"message": "Api Key is invalid"
}
Step 2: Match Error Below and Apply Fix
Error 1: 401 Unauthorized — Token Expired or Invalid
{"error":"invalid_token","error_description":"Could not match jwt signature to any of the bindings"}
Cause: Access token expired (24h TTL) or you are still using deprecated JWT credentials.
Fix:
# Regenerate OAuth Server-to-Server token
curl -X POST 'https://ims-na1.adobelogin.com/ims/token/v3' \
-d "client_id=${ADOBE_CLIENT_ID}&client_secret=${ADOBE_CLIENT_SECRET}&grant_type=client_credentials&scope=${ADOBE_SCOPES}"
# If using JWT: migrate immediately — JWT reached EOL June 2025
# See: https://developer.adobe.com/developer-console/docs/guides/authentication/ServerToServerAuthentication/migration
Error 2: 403 Forbidden — API Not Entitled
{"error_code":"403003","message":"Api Key is invalid"}
Cause: Your Developer Console project does not have the API added, or the product profile is missing.
Fix: Go to Developer Console > Project > Add API > Select the API > Assign product profile.
Error 3: 429 Too Many Requests — Rate Limited
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Cause: Exceeded API rate limits. Adobe rate limits vary by API:
- Firefly: ~20 req/min on trial
- PDF Services: 500 transactions/month (free tier)
- Events Publishing: 3,000 req/5sec per api-key
Fix:
// Honor the Retry-After header
const retryAfter = parseInt(response.headers.get('Retry-After') || '30');
await new Promise(r => setTimeout(r, retryAfter * 1000));
Error 4: 400 Bad Request — Firefly Content Policy
{"type":"INPUT_VALIDATION_ERROR","title":"prompt is not allowed by the content policy"}
Cause: Firefly prompt contains prohibited content (real people, tra
'Execute Adobe Firefly Services workflow: AI image generation, generative.
Adobe Core Workflow A — Firefly Services
Overview
Primary creative workflow using Adobe Firefly v3 APIs: text-to-image generation, generative fill (inpainting), and image expansion (outpainting). These are the most common Firefly Services operations for marketing asset automation.
Prerequisites
- Completed
adobe-install-authwith Firefly API scopes (fireflyapi,ffapis) @adobe/firefly-apisinstalled, or direct REST access- Pre-signed cloud storage URLs for input/output images (S3, Azure Blob, or Dropbox)
Instructions
Step 1: Text-to-Image Generation (Synchronous)
// src/workflows/firefly-generate.ts
import { getAccessToken } from '../adobe/client';
interface FireflyGenerateOptions {
prompt: string;
negativePrompt?: string;
width?: number; // 1024, 1472, 1792, 2048
height?: number;
n?: number; // 1-4 images
contentClass?: 'art' | 'photo';
style?: {
presets?: string[]; // e.g., ['digital_art', 'cinematic']
strength?: number; // 0-100
};
}
interface FireflyOutput {
outputs: Array<{
image: { url: string };
seed: number;
}>;
}
export async function generateImage(opts: FireflyGenerateOptions): Promise<FireflyOutput> {
const token = await getAccessToken();
const body: Record<string, any> = {
prompt: opts.prompt,
n: opts.n || 1,
size: { width: opts.width || 1024, height: opts.height || 1024 },
contentClass: opts.contentClass || 'photo',
};
if (opts.negativePrompt) body.negativePrompt = opts.negativePrompt;
if (opts.style?.presets) {
body.styles = { presets: opts.style.presets };
}
const response = await fetch('https://firefly-api.adobe.io/v3/images/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'x-api-key': process.env.ADOBE_CLIENT_ID!,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
if (!response.ok) {
const err = await response.text();
throw new Error(`Firefly generate failed (${response.status}): ${err}`);
}
return response.json();
}
Step 2: Async Generation (for High Volume)
// For production pipelines, use async endpoint to avoid HTTP timeouts
export async function generateImageAsync(opts: FireflyGenerateOptions) {
const token = await getAccessToken();
const response = await fetch('https://firefly-api.adobe.io/v3/images/generate-async', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'x-api-key': process.env.ADOBE_CLIENT_ID!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt: opts.prompt,
n: op'Execute Adobe PDF Services workflow: create PDFs from HTML/DOCX, extract.
Adobe Core Workflow B — PDF Services
Overview
Document automation using Adobe PDF Services API: create PDFs from HTML/DOCX, extract structured text and tables with Sensei AI, generate documents from Word templates with JSON data, and convert PDFs to LLM-friendly Markdown.
Prerequisites
- Completed
adobe-install-authwith PDF Services credentials npm install @adobe/pdfservices-node-sdk(v4.x+)- 500 free document transactions/month on the free tier
Instructions
Step 1: Create PDF from HTML
// src/workflows/pdf-create.ts
import {
ServicePrincipalCredentials,
PDFServices,
MimeType,
CreatePDFJob,
CreatePDFResult,
} from '@adobe/pdfservices-node-sdk';
import * as fs from 'fs';
const credentials = new ServicePrincipalCredentials({
clientId: process.env.ADOBE_CLIENT_ID!,
clientSecret: process.env.ADOBE_CLIENT_SECRET!,
});
const pdfServices = new PDFServices({ credentials });
export async function htmlToPdf(htmlPath: string, outputPath: string): Promise<void> {
const inputStream = fs.createReadStream(htmlPath);
const inputAsset = await pdfServices.upload({
readStream: inputStream,
mimeType: MimeType.HTML,
});
const job = new CreatePDFJob({ inputAsset });
const pollingURL = await pdfServices.submit({ job });
const result = await pdfServices.getJobResult({
pollingURL,
resultType: CreatePDFResult,
});
const resultAsset = result.result!.asset;
const streamAsset = await pdfServices.getContent({ asset: resultAsset });
const output = fs.createWriteStream(outputPath);
streamAsset.readStream.pipe(output);
await new Promise((resolve, reject) => {
output.on('finish', resolve);
output.on('error', reject);
});
console.log(`PDF created: ${outputPath}`);
}
Step 2: Extract Text and Tables from PDF (Sensei AI)
// src/workflows/pdf-extract.ts
import {
PDFServices,
MimeType,
ExtractPDFParams,
ExtractElementType,
ExtractPDFJob,
ExtractPDFResult,
ExtractRenditionsElementType,
} from '@adobe/pdfservices-node-sdk';
import * as fs from 'fs';
import AdmZip from 'adm-zip';
export async function extractPdfContent(
pdfPath: string,
options?: { tables?: boolean; figures?: boolean }
): Promise<{ text: string; tables: any[]; }> {
const inputStream = fs.createReadStream(pdfPath);
const inputAsset = await pdfServices.upload({
readStream: inputStream,
mimeType: MimeType.PDF,
});
const elements = [ExtractElementType.TEXT];
if (options?.tables !== false) elements.push(ExtractElementType.TABLES);
const params = new ExtractPDFParams({
elementsToExtract: elements,
...(options?.figures && {
elementsToExtractRenditions: [ExtractRenditionsElementType.FIGURES],
}),
});
const job = new'Optimize Adobe API costs across Firefly Services (generative credits),.
Adobe Cost Tuning
Overview
Optimize costs across Adobe's consumption-based APIs. Each API family has different pricing models: Firefly uses generative credits, PDF Services uses document transactions, and Photoshop/Lightroom use API call credits.
Prerequisites
- Access to Adobe Admin Console billing (https://adminconsole.adobe.com)
- Understanding of current API usage patterns
- Monitoring infrastructure for usage tracking
Instructions
Step 1: Understand Adobe API Pricing Models
| API | Free Tier | Paid Unit | Key Limit |
|---|---|---|---|
| PDF Services | 500 tx/month | Document Transaction | Per-page for extract, per-file for create |
| Firefly API | Trial credits | Generative Credit | 1 credit per image generated |
| Photoshop API | Trial credits | API Credit | 1 credit per operation (cutout, actions, etc.) |
| Lightroom API | Trial credits | API Credit | 1 credit per auto-edit |
| I/O Events | Included | Free with entitlement | 3,000 events/5sec rate limit |
| Document Generation | Part of PDF Services | Document Transaction | Per-document generated |
Step 2: Track Usage per API
// src/adobe/usage-tracker.ts
interface ApiUsageEntry {
api: 'firefly' | 'pdf-services' | 'photoshop' | 'lightroom';
operation: string;
timestamp: Date;
durationMs: number;
creditsUsed: number;
}
class AdobeUsageTracker {
private entries: ApiUsageEntry[] = [];
record(entry: Omit<ApiUsageEntry, 'timestamp'>): void {
this.entries.push({ ...entry, timestamp: new Date() });
}
getMonthlySummary(): Record<string, { calls: number; credits: number }> {
const now = new Date();
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
const monthly = this.entries.filter(e => e.timestamp >= monthStart);
return monthly.reduce((acc, entry) => {
const key = entry.api;
if (!acc[key]) acc[key] = { calls: 0, credits: 0 };
acc[key].calls++;
acc[key].credits += entry.creditsUsed;
return acc;
}, {} as Record<string, { calls: number; credits: number }>);
}
checkBudget(api: string, monthlyLimit: number): { remaining: number; warning: boolean } {
const summary = this.getMonthlySummary();
const used = summary[api]?.credits || 0;
const remaining = monthlyLimit - used;
return { remaining, warning: remaining < monthlyLimit * 0.2 };
}
}
Step 3: Cost Reduction Strategies
Strategy
'Implement data handling for Adobe APIs including PII redaction in logs,.
Adobe Data Handling
Overview
Handle sensitive data correctly when integrating with Adobe APIs. Key concerns include Firefly content policy compliance, PII in PDF extraction results, credential redaction in logs, and GDPR/CCPA compliance using Adobe Privacy Service API.
Prerequisites
- Understanding of your data classification requirements
- Adobe SDK with appropriate API access
- Database for audit logging
- Familiarity with GDPR/CCPA obligations
Instructions
Step 1: Data Classification for Adobe API Data
| Category | Examples | Handling |
|---|---|---|
| Credentials | client_secret, access tokens |
Never log; rotate regularly |
| User Content | Uploaded images, PDFs | Encrypt at rest; delete per retention policy |
| Generated Content | Firefly outputs, processed PDFs | Time-limited URLs (24h); cache intentionally |
| Extraction Results | PDF text, tables, structured data | May contain PII; scan and redact |
| API Metadata | Job IDs, request IDs, timestamps | Safe to log; useful for debugging |
Step 2: PII Detection in PDF Extraction Results
PDF Extract API returns raw text that may contain customer PII:
// src/adobe/pii-scanner.ts
const PII_PATTERNS = [
{ type: 'email', regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g },
{ type: 'phone', regex: /\b\d{3}[-.]?\d{3}[-.]?\d{4}\b/g },
{ type: 'ssn', regex: /\b\d{3}-\d{2}-\d{4}\b/g },
{ type: 'credit_card', regex: /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/g },
];
interface PiiFinding {
type: string;
count: number;
// Never store the actual PII value
}
export function scanForPii(text: string): PiiFinding[] {
return PII_PATTERNS
.map(pattern => {
const matches = text.matchAll(pattern.regex);
const count = [...matches].length;
return count > 0 ? { type: pattern.type, count } : null;
})
.filter(Boolean) as PiiFinding[];
}
export function redactPii(text: string): string {
let redacted = text;
for (const pattern of PII_PATTERNS) {
redacted = redacted.replace(pattern.regex, `[REDACTED-${pattern.type.toUpperCase()}]`);
}
return redacted;
}
// Usage after PDF extraction
const extracted = await extractPdfContent('customer-form.pdf');
const piiFindings = scanForPii(extracted.text);
if (piiFindings.length > 0) {
console.warn('PII detected in extraction:', piiFindings);
// Store redacted version, or encrypt at rest
const safeText = redactPii(extracted.text);
}
Step 3: Firefly Content Policy Compliance
Firefly API h
'Collect Adobe debug evidence for support tickets and troubleshooting.
Adobe Debug Bundle
Overview
Collect all necessary diagnostic information for Adobe support tickets. This script gathers SDK versions, credential validation status, API connectivity, and redacted configuration into a support-ready archive.
Prerequisites
- Adobe credentials configured (env vars or
.envfile) - Node.js or Python environment with Adobe SDKs installed
- Permission to run network diagnostics
Instructions
Step 1: Create Debug Bundle Script
#!/bin/bash
# adobe-debug-bundle.sh — Collects diagnostic info for Adobe support
set -euo pipefail
BUNDLE_DIR="adobe-debug-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BUNDLE_DIR"
echo "=== Adobe Debug Bundle ===" | tee "$BUNDLE_DIR/summary.txt"
echo "Generated: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$BUNDLE_DIR/summary.txt"
echo "Hostname: $(hostname)" >> "$BUNDLE_DIR/summary.txt"
echo "" >> "$BUNDLE_DIR/summary.txt"
# --- Environment ---
echo "--- Runtime Environment ---" >> "$BUNDLE_DIR/summary.txt"
node --version >> "$BUNDLE_DIR/summary.txt" 2>&1 || echo "Node.js: not found" >> "$BUNDLE_DIR/summary.txt"
npm --version >> "$BUNDLE_DIR/summary.txt" 2>&1 || echo "npm: not found" >> "$BUNDLE_DIR/summary.txt"
python3 --version >> "$BUNDLE_DIR/summary.txt" 2>&1 || echo "Python: not found" >> "$BUNDLE_DIR/summary.txt"
# --- Adobe SDK Versions ---
echo "" >> "$BUNDLE_DIR/summary.txt"
echo "--- Adobe SDK Versions ---" >> "$BUNDLE_DIR/summary.txt"
npm list @adobe/pdfservices-node-sdk 2>/dev/null >> "$BUNDLE_DIR/summary.txt" || echo "PDF Services SDK: not installed" >> "$BUNDLE_DIR/summary.txt"
npm list @adobe/firefly-apis 2>/dev/null >> "$BUNDLE_DIR/summary.txt" || echo "Firefly APIs: not installed" >> "$BUNDLE_DIR/summary.txt"
npm list @adobe/photoshop-apis 2>/dev/null >> "$BUNDLE_DIR/summary.txt" || echo "Photoshop APIs: not installed" >> "$BUNDLE_DIR/summary.txt"
npm list @adobe/lightroom-apis 2>/dev/null >> "$BUNDLE_DIR/summary.txt" || echo "Lightroom APIs: not installed" >> "$BUNDLE_DIR/summary.txt"
npm list @adobe/aio-sdk 2>/dev/null >> "$BUNDLE_DIR/summary.txt" || echo "AIO SDK: not installed" >> "$BUNDLE_DIR/summary.txt"
# --- Credential Status (NEVER log actual values) ---
echo "" >> "$BUNDLE_DIR/summary.txt"
echo "--- Credential Status ---" >> "$BUNDLE_DIR/summary.txt"
echo 'Deploy Adobe-powered applications to Vercel, Cloud Run, and Adobe App.
Adobe Deploy Integration
Overview
Deploy Adobe-powered applications to three platforms: Vercel (serverless), Google Cloud Run (containers), and Adobe App Builder (native Adobe Runtime). Each with proper OAuth credential management.
Prerequisites
- Adobe OAuth Server-to-Server credentials for production
- Platform CLI installed (
vercel,gcloud, oraio) - Application tested in staging environment
Instructions
Option A: Adobe App Builder (Native Adobe Hosting)
App Builder deploys serverless Runtime actions directly to Adobe infrastructure:
# Login to Adobe I/O CLI (requires IMS auth since AIO CLI v11)
aio login
# Select your project and workspace
aio console project select
aio console workspace select Production
# Deploy all actions, static assets, and event registrations
aio app deploy
# Check deployed actions
aio runtime action list
# View action logs
aio runtime activation list --limit 10
aio runtime activation logs <activationId>
// app.config.yaml — App Builder configuration
application:
actions: actions
web: web-src
runtimeManifest:
packages:
my-adobe-app:
actions:
process-image:
function: actions/process-image/index.js
runtime: nodejs:20
inputs:
ADOBE_CLIENT_ID: $ADOBE_CLIENT_ID
ADOBE_CLIENT_SECRET: $ADOBE_CLIENT_SECRET
annotations:
require-adobe-auth: true
final: true
Option B: Vercel Deployment
# Set Adobe credentials as Vercel environment variables
vercel env add ADOBE_CLIENT_ID production
vercel env add ADOBE_CLIENT_SECRET production
vercel env add ADOBE_SCOPES production
# Deploy
vercel --prod
// vercel.json
{
"functions": {
"api/**/*.ts": {
"maxDuration": 60
}
},
"env": {
"ADOBE_CLIENT_ID": "@adobe_client_id",
"ADOBE_CLIENT_SECRET": "@adobe_client_secret",
"ADOBE_SCOPES": "@adobe_scopes"
}
}
// api/firefly/generate.ts — Vercel serverless function
import type { VercelRequest, VercelResponse } from '@vercel/node';
import { getAccessToken } from '../../src/adobe/client';
export default async function handler(req: VercelRequest, res: VercelResponse) {
if (req.method !== 'POST') return res.status(405).end();
try {
const token = await getAccessToken();
const fireflyResponse = await fetch(
'https://firefly-api.adobe.io/v3/images/generate',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'x-api-key&'Configure Adobe enterprise identity with Admin Console SCIM provisioning,.
Adobe Enterprise RBAC
Overview
Configure enterprise-grade access control for Adobe integrations using Admin Console product profiles, User Management API (UMAPI) for programmatic user provisioning, and SCIM-based identity sync with Azure AD or Google Workspace.
Prerequisites
- Adobe Enterprise or Teams subscription
- Adobe Admin Console system administrator access
- Identity Provider (Azure AD, Google Workspace, or Okta) for SSO
- Understanding of SCIM 2.0 protocol
Instructions
Step 1: Set Up Federated Identity in Admin Console
- Go to https://adminconsole.adobe.com > Settings > Identity
- Create a Federated ID directory
- Configure SSO:
- Azure AD: Admin Console > Add Azure Sync > Follow SCIM setup
- Google Workspace: Admin Console > Add Google Sync > SCIM provisioning
- Generic SAML: Upload IdP metadata XML
# SAML Configuration Values (for your IdP)
Adobe SP Entity ID: https://federatedid-na1.services.adobe.com/federated/saml/metadata
ACS URL: https://federatedid-na1.services.adobe.com/federated/saml/SSO
Name ID Format: urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress
Step 2: Define Product Profiles (Adobe's RBAC Mechanism)
Product Profiles in Admin Console are Adobe's native RBAC system. Create profiles that map to your application roles:
| Profile Name | Adobe APIs Granted | Application Role |
|---|---|---|
API-Developers |
Firefly, PDF Services, Photoshop | Full API access |
API-Viewers |
PDF Services (read-only) | Report viewers |
API-Automation |
PDF Services, Document Generation | CI/CD service accounts |
API-Admin |
All APIs + Admin Console | Platform administrators |
Step 3: Programmatic User Management via UMAPI
// src/adobe/user-management.ts
// Adobe User Management API (UMAPI) — manage users and product profile assignments
const UMAPI_BASE = 'https://usermanagement.adobe.io/v2/usermanagement';
interface UmapiUser {
email: string;
firstname: string;
lastname: string;
country: string;
}
export async function addUserToProductProfile(
user: UmapiUser,
productProfile: string
): Promise<void> {
const token = await getAccessToken();
const response = await fetch(`${UMAPI_BASE}/action/${process.env.ADOBE_IMS_ORG_ID}`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'x-api-key': process.env.ADOBE_CLIENT_ID!,
'Content-Type': 'application/json',
},
body: JSON.stringify([{
'Create minimal working examples for Adobe APIs: Firefly image generation,.
Adobe Hello World
Overview
Three minimal working examples covering Adobe's core API surfaces: Firefly AI image generation, PDF content extraction, and Photoshop background removal.
Prerequisites
- Completed
adobe-install-authsetup - Valid OAuth Server-to-Server credentials
- Node.js 18+ with
@adobe/firefly-apisor@adobe/pdfservices-node-sdkinstalled
Instructions
Example 1: Firefly Text-to-Image Generation
// hello-firefly.ts
import 'dotenv/config';
import { getAdobeAccessToken } from './adobe/auth';
async function generateImage() {
const token = await getAdobeAccessToken();
const response = await fetch(
'https://firefly-api.adobe.io/v3/images/generate',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'x-api-key': process.env.ADOBE_CLIENT_ID!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt: 'A futuristic cityscape at sunset with flying cars',
n: 1, // number of images
size: {
width: 1024,
height: 1024,
},
contentClass: 'art', // "art" or "photo"
}),
}
);
if (!response.ok) {
throw new Error(`Firefly API error: ${response.status} ${await response.text()}`);
}
const result = await response.json();
console.log('Generated image URL:', result.outputs[0].image.url);
return result;
}
generateImage().catch(console.error);
Example 2: PDF Text Extraction
// hello-pdf.ts
import {
ServicePrincipalCredentials,
PDFServices,
MimeType,
ExtractPDFParams,
ExtractElementType,
ExtractPDFJob,
ExtractPDFResult,
} from '@adobe/pdfservices-node-sdk';
import * as fs from 'fs';
async function extractPDF() {
const credentials = new ServicePrincipalCredentials({
clientId: process.env.ADOBE_CLIENT_ID!,
clientSecret: process.env.ADOBE_CLIENT_SECRET!,
});
const pdfServices = new PDFServices({ credentials });
// Upload the PDF
const inputStream = fs.createReadStream('./sample.pdf');
const inputAsset = await pdfServices.upload({
readStream: inputStream,
mimeType: MimeType.PDF,
});
// Configure extraction (text + tables)
const params = new ExtractPDFParams({
elementsToExtract: [ExtractElementType.TEXT, ExtractElementType.TABLES],
});
// Run extraction job
const job = new ExtractPDFJob({ inputAsset, params });
const pollingURL = await pdfServices.submit({ job });
const result = await pdfServices.getJobResult({
pollingURL,
resultType: ExtractPDFResult,
});
// Download result ZIP containing structuredData.json
const resultAsset = result.result!.resource;
cons'Execute Adobe incident response procedures with triage, mitigation,.
Adobe Incident Runbook
Overview
Rapid incident response procedures for Adobe API-related outages, covering IMS authentication failures, Firefly/Photoshop API downtime, PDF Services quota exhaustion, and I/O Events delivery failures.
Prerequisites
- Access to Adobe Developer Console and Admin Console
- Access to application monitoring (Grafana, Datadog, etc.)
- kubectl access to production cluster (if applicable)
- Communication channels (Slack, PagerDuty)
Severity Matrix
| Level | Definition | Response Time | Example |
|---|---|---|---|
| P1 | Complete Adobe integration failure | < 15 min | IMS auth broken, all APIs down |
| P2 | Single API degraded | < 1 hour | Firefly 429s, Photoshop timeouts |
| P3 | Minor impact | < 4 hours | Webhook delays, slow PDF extraction |
| P4 | No user impact | Next business day | Monitoring gap, metric anomaly |
Quick Triage (Run These First)
# 1. Is Adobe itself down?
curl -s -o /dev/null -w "Adobe Status: %{http_code}\n" https://status.adobe.com
# 2. Can we generate an access token?
curl -s -o /dev/null -w "IMS Auth: %{http_code}\n" -X POST \
'https://ims-na1.adobelogin.com/ims/token/v3' \
-d "client_id=${ADOBE_CLIENT_ID}&client_secret=${ADOBE_CLIENT_SECRET}&grant_type=client_credentials&scope=${ADOBE_SCOPES}"
# 3. Can we reach each API endpoint?
for endpoint in firefly-api.adobe.io image.adobe.io pdf-services.adobe.io; do
CODE=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "https://$endpoint" 2>/dev/null || echo "UNREACHABLE")
echo "$endpoint: $CODE"
done
# 4. Check our app health
curl -sf https://your-app.com/health | python3 -m json.tool
# 5. Recent errors in our logs (last 5 min)
kubectl logs -l app=adobe-service --since=5m 2>/dev/null | grep -i "error\|failed\|429\|401\|500" | tail -20
Decision Tree
Adobe APIs returning errors?
├── YES: Is status.adobe.com reporting an incident?
│ ├── YES → Adobe-side outage. Enable fallback mode. Monitor status page.
│ └── NO → Check our credentials and config.
│ ├── 401 errors → Credentials expired/rotated. See "Auth Recovery" below.
│ ├── 429 errors → Rate limited. See "Rate Limit Recovery" below.
│ └── 500/503 errors → Adobe server issue (unreported). Open support ticket.
└── NO: Is our application healthy?
├── YES → Likely resolved or intermittent. Continue monitoring.
└── NO → Our infrastructure issue. Check pods, memory, network.
Recovery Procedures
Auth Recovery (401/403)
'Install and configure Adobe Developer Console OAuth Server-to-Server.
Adobe Install & Auth
Overview
Set up Adobe Developer Console OAuth Server-to-Server credentials and install the appropriate SDK for your use case. As of January 2025, JWT (Service Account) credentials are deprecated -- all new integrations must use OAuth Server-to-Server.
Prerequisites
- Node.js 18+ or Python 3.10+
- Adobe Developer Console account (https://developer.adobe.com/console)
- An Adobe organization with API access entitlements
- Admin or Developer role in Adobe Admin Console
Instructions
Step 1: Create Project in Adobe Developer Console
- Go to https://developer.adobe.com/console
- Click Create new project > Add API
- Select the API you need (e.g., Firefly Services, PDF Services, Creative Cloud Libraries)
- Choose OAuth Server-to-Server credential type
- Select the product profiles to scope access
- Save your
clientid,clientsecret, andscopes
Step 2: Install the SDK for Your Use Case
# Firefly Services (Photoshop API, Lightroom API, Firefly API)
npm install @adobe/firefly-apis @adobe/photoshop-apis @adobe/lightroom-apis
# PDF Services (create, extract, convert, generate documents)
npm install @adobe/pdfservices-node-sdk
# Adobe I/O Events (webhooks, event-driven)
npm install @adobe/aio-lib-events
# Adobe I/O SDK (App Builder, Runtime actions)
npm install @adobe/aio-sdk
# Adobe I/O CLI (global install for aio commands)
npm install -g @adobe/aio-cli
# Python — PDF Services
pip install pdfservices-sdk
Step 3: Configure OAuth Server-to-Server Credentials
# .env (NEVER commit — add to .gitignore)
ADOBE_CLIENT_ID=your_client_id_from_console
ADOBE_CLIENT_SECRET=your_client_secret_from_console
ADOBE_SCOPES=openid,AdobeID,read_organizations,firefly_api,ff_apis
ADOBE_IMS_ORG_ID=your_org_id@AdobeOrg
Step 4: Generate Access Token
// src/adobe/auth.ts
import 'dotenv/config';
interface AdobeTokenResponse {
access_token: string;
token_type: string;
expires_in: number; // seconds, typically 86400 (24h)
}
let cachedToken: { token: string; expiresAt: number } | null = null;
export async function getAdobeAccessToken(): Promise<string> {
// Return cached token if still valid (with 5min buffer)
if (cachedToken && cachedToken.expiresAt > Date.now() + 300_000) {
return cachedToken.token;
}
const response = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.ADOBE_CLIENT_ID!,
client_secret: process.env.ADOBE_CLIENT_SECRET!,
'Identify and avoid Adobe-specific anti-patterns: using deprecated JWT.
Adobe Known Pitfalls
Overview
The 10 most common mistakes when integrating with Adobe APIs, based on real production issues. Each pitfall includes the anti-pattern, why it fails, and the correct approach.
Prerequisites
- Access to your Adobe integration codebase
- Understanding of Adobe API architecture (OAuth, async jobs, rate limits)
Instructions
Pitfall 1: Still Using JWT (Service Account) Credentials
Status: CRITICAL — JWT credentials reached End of Life June 2025.
// WRONG: JWT auth (no longer works as of 2025)
import jwt from 'jsonwebtoken';
import fs from 'fs';
const privateKey = fs.readFileSync('private.key');
const jwtToken = jwt.sign({
exp: Math.round(Date.now() / 1000) + 86400,
iss: orgId,
sub: technicalAccountId,
aud: `https://ims-na1.adobelogin.com/c/${clientId}`,
}, privateKey, { algorithm: 'RS256' });
// RIGHT: OAuth Server-to-Server (current standard)
const res = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.ADOBE_CLIENT_ID!,
client_secret: process.env.ADOBE_CLIENT_SECRET!,
grant_type: 'client_credentials',
scope: process.env.ADOBE_SCOPES!,
}),
});
Pitfall 2: Not Caching IMS Access Tokens
IMS tokens are valid for 24 hours. Generating a new token per request wastes 200-500ms:
// WRONG: New token every request (200-500ms overhead each time)
async function callFirefly(prompt: string) {
const tokenRes = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', { ... });
const { access_token } = await tokenRes.json();
// ... use access_token
}
// RIGHT: Cache token with expiry check
let cached: { token: string; expiresAt: number } | null = null;
async function getToken(): Promise<string> {
if (cached && cached.expiresAt > Date.now() + 300_000) return cached.token;
const res = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', { ... });
const data = await res.json();
cached = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
return cached.token;
}
Pitfall 3: Using Firefly Sync Endpoint for Batch Operations
// WRONG: Sequential sync calls (each blocks 5-20s)
for (const prompt of prompts) {
const result = await fetch('https://firefly-api.adobe.io/v3/images/generate', {
method: 'POST', ...
});
results.push(await result.json());
}
// Total time: N * 5-20s = very slow
// RIGHT: Async endpoint with parallel submission
const jobs = await Promise.all(
prompts.map(prompt =>
fetch('https://firefly-api.adobe.'Implement load testing, auto-scaling, and capacity planning for Adobe.
Adobe Load & Scale
Overview
Load testing and scaling strategies for Adobe API integrations. Adobe APIs are async and relatively slow (5-30s per operation), requiring different load testing approaches than typical REST APIs.
Prerequisites
- k6 load testing tool installed (
npm install -g k6orbrew install k6) - Adobe Developer Console credentials for testing (separate from production)
- Kubernetes cluster with HPA configured (for auto-scaling)
- Understanding of your Adobe API rate limits
Instructions
Step 1: k6 Load Test for Firefly API
// adobe-firefly-load.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';
const errorRate = new Rate('adobe_errors');
const fireflyDuration = new Trend('firefly_duration');
export const options = {
stages: [
{ duration: '1m', target: 2 }, // Warm up (Adobe APIs are slow)
{ duration: '3m', target: 5 }, // Steady state
{ duration: '2m', target: 10 }, // Stress (watch for 429s)
{ duration: '1m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<30000'], // 30s — Firefly is async/slow
adobe_errors: ['rate<0.05'], // < 5% error rate
},
};
// Pre-generate token (shared across VUs)
const TOKEN = __ENV.ADOBE_ACCESS_TOKEN;
const CLIENT_ID = __ENV.ADOBE_CLIENT_ID;
export default function () {
const response = http.post(
'https://firefly-api.adobe.io/v3/images/generate',
JSON.stringify({
prompt: `Load test image ${Date.now()}`,
n: 1,
size: { width: 512, height: 512 }, // Smallest size for speed
}),
{
headers: {
'Authorization': `Bearer ${TOKEN}`,
'x-api-key': CLIENT_ID,
'Content-Type': 'application/json',
},
timeout: '60s',
}
);
const success = check(response, {
'status is 200': (r) => r.status === 200,
'status is not 429': (r) => r.status !== 429,
});
errorRate.add(!success);
fireflyDuration.add(response.timings.duration);
if (response.status === 429) {
const retryAfter = parseInt(response.headers['Retry-After'] || '30');
console.log(`Rate limited, waiting ${retryAfter}s`);
sleep(retryAfter);
} else {
sleep(3); // Respect rate limits between requests
}
}
Step 2: k6 Load Test for PDF Services
// adobe-pdf-load.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '1m', target: 3 },
{ duration: '5m', target: 10 },
{ duration: '1m', target: 0 },
],
thresholds: {
http_r'Configure Adobe local development with App Builder CLI, Runtime actions,.
Adobe Local Dev Loop
Overview
Set up a fast local development workflow for Adobe integrations using the aio CLI for App Builder projects, or a standalone Node.js setup for direct API usage (Firefly Services, PDF Services).
Prerequisites
- Completed
adobe-install-authsetup - Node.js 18+ with npm/pnpm
- Adobe Developer Console project configured
@adobe/aio-cliinstalled globally (for App Builder projects)
Instructions
Step 1: Choose Your Project Type
Option A — App Builder (serverless Runtime actions):
# Install Adobe I/O CLI
npm install -g @adobe/aio-cli
# Login (opens browser for IMS auth)
aio login
# Create new App Builder project
aio app init my-adobe-app
# Select: Firefly Services, Adobe I/O Events, etc.
# Run locally with hot reload
aio app run
# Serves at https://localhost:9080 with live Runtime action emulation
Option B — Standalone SDK project:
mkdir my-adobe-project && cd my-adobe-project
npm init -y
npm install @adobe/pdfservices-node-sdk @adobe/firefly-apis dotenv
npm install -D typescript tsx vitest @types/node
Step 2: Project Structure
my-adobe-project/
├── src/
│ ├── adobe/
│ │ ├── auth.ts # OAuth token management (from install-auth)
│ │ ├── firefly.ts # Firefly API client wrapper
│ │ ├── pdf-services.ts # PDF Services client wrapper
│ │ └── photoshop.ts # Photoshop API client wrapper
│ └── index.ts
├── tests/
│ ├── fixtures/
│ │ └── sample.pdf # Test PDF for extraction tests
│ ├── adobe-auth.test.ts
│ └── firefly.test.ts
├── .env.local # Local secrets (git-ignored)
├── .env.example # Template for team
├── tsconfig.json
└── package.json
Step 3: Configure Hot Reload and Scripts
{
"scripts": {
"dev": "tsx watch src/index.ts",
"test": "vitest",
"test:watch": "vitest --watch",
"test:integration": "vitest --config vitest.integration.config.ts",
"typecheck": "tsc --noEmit"
}
}
Step 4: Mock Adobe APIs for Unit Tests
// tests/adobe-auth.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
// Mock the global fetch for token endpoint
const mockFetch = vi.fn();
global.fetch = mockFetch;
import { getAdobeAccessToken } from '../src/adobe/auth';
describe('Adobe OAuth Auth', () => {
beforeEach(() => {
vi.clearAllMocks();
process.env.ADOBE_CLIENT_ID = 'test-client-id';
process.env.ADOBE_CLIENT_SECRET = 'test-secret';
process.env.ADOBE_SCOPES = 'Execute major Adobe re-architecture: migrating from legacy Adobe APIs.
Adobe Migration Deep Dive
Overview
Comprehensive guide for three major migration scenarios: (1) legacy Adobe API consolidation into Firefly Services, (2) migrating from competitor document/image APIs to Adobe, and (3) JWT credential migration to OAuth Server-to-Server.
Prerequisites
- Current system documentation with API inventory
- Adobe Developer Console project with target APIs
- Feature flag infrastructure
- Rollback strategy tested in staging
Instructions
Migration Type Assessment
| Type | From | To | Complexity | Duration |
|---|---|---|---|---|
| Auth migration | JWT credentials | OAuth Server-to-Server | Low | 1-2 days |
| API consolidation | Separate PS/LR endpoints | Firefly Services SDK | Medium | 1-2 weeks |
| Competitor replacement | Cloudinary/imgix/PDFTron | Adobe APIs | High | 4-8 weeks |
| Full replatform | Custom pipeline | Adobe App Builder | High | 2-3 months |
Scenario 1: Consolidate to Firefly Services SDK
The Photoshop and Lightroom APIs were previously separate. They are now part of Firefly Services with a unified SDK:
// BEFORE: Separate clients for each API
import { PhotoshopAPI } from 'some-old-photoshop-client';
import { LightroomAPI } from 'some-old-lightroom-client';
// AFTER: Unified Firefly Services SDK
import { PhotoshopClient } from '@adobe/photoshop-apis';
import { LightroomClient } from '@adobe/lightroom-apis';
import { FireflyClient } from '@adobe/firefly-apis';
// All use the same OAuth credentials
const config = {
clientId: process.env.ADOBE_CLIENT_ID!,
accessToken: await getAccessToken(),
};
const photoshop = new PhotoshopClient(config);
const lightroom = new LightroomClient(config);
const firefly = new FireflyClient(config);
Scenario 2: Migrate from Competitor to Adobe PDF Services
// src/adapters/document-adapter.ts
// Adapter pattern for gradual migration from PDFTron/other to Adobe
interface DocumentAdapter {
extractText(pdfPath: string): Promise<string>;
createPdf(htmlContent: string): Promise<Buffer>;
mergePdfs(pdfPaths: string[]): Promise<Buffer>;
}
// Old implementation
class PdfTronAdapter implements DocumentAdapter {
async extractText(pdfPath: string): Promise<string> {
// ... existing PDFTron code
}
// ...
}
// New Adobe implementation
class AdobePdfAdapter implements DocumentAdapter {
private pdfServices: PDFServices;
constructor() {
const credentials = new ServicePrincipalCredentials({
clientId: process.env.ADOBE_CLIENT_ID!,
clientSecret: process.en'Configure Adobe OAuth credentials and API access across development,.
Adobe Multi-Environment Setup
Overview
Configure Adobe APIs across development, staging, and production environments using separate Developer Console projects, environment-specific OAuth credentials, and cloud-native secret management.
Prerequisites
- Adobe Developer Console access (admin or developer role)
- Secret management solution (GCP Secret Manager, AWS Secrets Manager, or Vault)
- CI/CD pipeline with environment variable injection
Instructions
Step 1: Create Separate Developer Console Projects
Adobe best practice: one Developer Console project per environment with separate OAuth credentials.
| Environment | Console Project | Scopes | Product Profile |
|---|---|---|---|
| Development | my-app-dev |
openid,AdobeID |
Dev sandbox |
| Staging | my-app-staging |
openid,AdobeID,firefly_api |
Staging profile |
| Production | my-app-prod |
openid,AdobeID,fireflyapi,ffapis |
Production profile |
Step 2: Environment Configuration Files
// src/config/adobe.ts
interface AdobeEnvConfig {
imsEndpoint: string; // Same across all envs
fireflyEndpoint: string; // Same across all envs
photoshopEndpoint: string; // Same across all envs
scopes: string; // Different per env (least privilege)
retries: number;
timeoutMs: number;
cache: { enabled: boolean; ttlMs: number };
}
const configs: Record<string, AdobeEnvConfig> = {
development: {
imsEndpoint: 'https://ims-na1.adobelogin.com',
fireflyEndpoint: 'https://firefly-api.adobe.io',
photoshopEndpoint: 'https://image.adobe.io',
scopes: 'openid,AdobeID', // Minimal scopes for dev
retries: 1, // Fast failure in dev
timeoutMs: 15_000,
cache: { enabled: false, ttlMs: 0 }, // No cache in dev
},
staging: {
imsEndpoint: 'https://ims-na1.adobelogin.com',
fireflyEndpoint: 'https://firefly-api.adobe.io',
photoshopEndpoint: 'https://image.adobe.io',
scopes: 'openid,AdobeID,firefly_api',
retries: 3,
timeoutMs: 30_000,
cache: { enabled: true, ttlMs: 60_000 },
},
production: {
imsEndpoint: 'https://ims-na1.adobelogin.com',
fireflyEndpoint: 'https://firefly-api.adobe.io',
photoshopEndpoint: 'https://image.adobe.io',
scopes: 'openid,AdobeID,firefly_api,ff_apis',
retries: 5,
timeoutMs: 60_000,
cache: { enabled: true, ttlMs: 300_000 },
},
};
export function getAdobeConfig(): AdobeEnvConfig & { clientId: string; clientSecret: string } {
const env = process.env.N'Set up comprehensive observability for Adobe API integrations with.
Adobe Observability
Overview
Set up comprehensive observability for Adobe API integrations covering four pillars: metrics (Prometheus), traces (OpenTelemetry), logs (structured JSON), and alerts. Each Adobe API has different latency profiles requiring specific monitoring.
Prerequisites
- Prometheus or compatible metrics backend
- OpenTelemetry SDK (
@opentelemetry/api) - Grafana or similar dashboarding tool
- AlertManager or PagerDuty for alerts
Instructions
Step 1: Define Key Metrics by API
| Metric | Type | Labels | Description |
|---|---|---|---|
adobeimstokenrequeststotal |
Counter | status |
Token generation attempts |
adobeapirequests_total |
Counter | api,operation,status |
API calls by type |
adobeapiduration_seconds |
Histogram | api,operation |
Latency per operation |
adobeapierrors_total |
Counter | api,error_code |
Errors by code (401,403,429,500) |
adobejobpoll_count |
Histogram | api |
Polls before async job completes |
adoberatelimitretriestotal |
Counter | api |
429 retries |
adobepdftransactions_used |
Gauge | — | Monthly PDF Services usage |
Step 2: Instrumented Adobe Client
import { Counter, Histogram, Gauge, Registry } from 'prom-client';
const registry = new Registry();
const apiRequests = new Counter({
name: 'adobe_api_requests_total',
help: 'Total Adobe API requests',
labelNames: ['api', 'operation', 'status'] as const,
registers: [registry],
});
const apiDuration = new Histogram({
name: 'adobe_api_duration_seconds',
help: 'Adobe API request duration in seconds',
labelNames: ['api', 'operation'] as const,
buckets: [0.5, 1, 2, 5, 10, 20, 30, 60], // Adobe APIs are slow
registers: [registry],
});
const apiErrors = new Counter({
name: 'adobe_api_errors_total',
help: 'Adobe API errors by code',
labelNames: ['api', 'error_code'] as const,
registers: [registry],
});
export async function instrumentedAdobeCall<T>(
api: string,
operation: string,
fn: () => Promise<T>
): Promise<T> {
const timer = apiDuration.startTimer({ api, operation });
try {
const result = await fn();
apiRequests.inc({ api, operation, status: 'success' }'Optimize Adobe API performance with token caching, async job batching,.
Adobe Performance Tuning
Overview
Optimize Adobe API performance across Firefly Services, PDF Services, and Photoshop APIs. Key bottlenecks include IMS token generation, async job polling overhead, and cold-start latency on serverless platforms.
Prerequisites
- Adobe SDK installed and functional
- Understanding of which APIs your app uses most
- Redis or in-memory cache available (optional)
- Performance monitoring in place
Latency Benchmarks (Real-World)
| Operation | P50 | P95 | P99 |
|---|---|---|---|
| IMS Token Generation | 200ms | 500ms | 1s |
| Firefly Text-to-Image (sync) | 5s | 12s | 20s |
| Firefly Text-to-Image (async poll) | 8s | 15s | 25s |
| PDF Extract (10-page doc) | 3s | 8s | 15s |
| PDF Create from HTML | 2s | 5s | 10s |
| Photoshop Remove Background | 4s | 10s | 18s |
| Lightroom Auto Tone | 3s | 8s | 15s |
Instructions
Optimization 1: Cache IMS Access Tokens (Biggest Win)
The IMS token endpoint returns tokens valid for 24 hours. Never re-generate per request:
// WRONG: generates new token every call (adds 200-500ms each time)
async function makeRequest() {
const token = await getAccessToken(); // hits IMS every time
}
// RIGHT: cache token and only refresh when expiring
let tokenCache: { token: string; expiresAt: number } | null = null;
async function getCachedToken(): Promise<string> {
if (tokenCache && tokenCache.expiresAt > Date.now() + 300_000) {
return tokenCache.token; // Cache hit — 0ms
}
const res = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.ADOBE_CLIENT_ID!,
client_secret: process.env.ADOBE_CLIENT_SECRET!,
grant_type: 'client_credentials',
scope: process.env.ADOBE_SCOPES!,
}),
});
const data = await res.json();
tokenCache = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
return tokenCache.token;
}
Optimization 2: Parallel Async Job Submission
Firefly and Photoshop APIs are async — submit all jobs first, then poll all:
// SLOW: sequential (total = sum of all job times)
for (const prompt of prompts) {
const result = await generateImageSync(prompt); // 5-20s each
}
// FAST: parallel submit + parallel poll (total = max job time)
async function batchFireflyGenerate(prompts: string[]) {
const token = awa'Implement Adobe-specific lint rules, CI policy checks, and runtime guardrails.
Adobe Policy & Guardrails
Overview
Automated policy enforcement for Adobe integrations: credential pattern scanning (Adobe OAuth secrets use p8_ prefix), Firefly content policy pre-screening, PDF Services quota guardrails, and OAuth scope validation.
Prerequisites
- ESLint configured in project
- CI/CD pipeline (GitHub Actions)
- Understanding of Adobe credential patterns
Instructions
Guardrail 1: Adobe Credential Pattern Scanner
Adobe OAuth Server-to-Server secrets follow the p8_ prefix pattern:
# .github/workflows/adobe-security.yml
name: Adobe Security Scan
on: [push, pull_request]
jobs:
credential-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Scan for Adobe credential patterns
run: |
EXIT_CODE=0
# Adobe OAuth client secrets (p8_ prefix)
if grep -rE "p8_[A-Za-z0-9_-]{20,}" --include="*.ts" --include="*.js" --include="*.py" --include="*.json" --include="*.yaml" --include="*.yml" . 2>/dev/null | grep -v node_modules | grep -v '.git'; then
echo "::error::Adobe client_secret pattern (p8_) found in source code"
EXIT_CODE=1
fi
# Adobe IMS access tokens (JWT format)
if grep -rE "eyJ[A-Za-z0-9_-]{100,}\.[A-Za-z0-9_-]{100,}" --include="*.ts" --include="*.js" . 2>/dev/null | grep -v node_modules | grep -v '.git' | grep -v '\.test\.' | grep -v '__mock'; then
echo "::warning::Potential Adobe access token found in source (may be test fixture)"
fi
# Org IDs (format: HEXSTRING@AdobeOrg)
if grep -rE "[A-F0-9]{24}@AdobeOrg" --include="*.ts" --include="*.js" --include="*.json" . 2>/dev/null | grep -v node_modules | grep -v '.git' | grep -v '.env.example'; then
echo "::warning::Adobe Org ID found in source — consider using env var"
fi
exit $EXIT_CODE
Guardrail 2: Firefly Content Policy Pre-Screener
// src/adobe/guardrails/content-policy.ts
// Pre-screen prompts before sending to Firefly API to avoid wasted credits
interface ContentPolicyResult {
allowed: boolean;
violations: string[];
suggestions: string[];
}
const CONTENT_RULES = [
{
name: 'real-people',
pattern: /\b(photo of|portrait of|picture of)\s+(a\s+)?(real|actual|specific)\s+(person|man|woman|child)/i,
message: 'Firefly cannot generate images of specific real people',
suggestion: 'Use generic descriptions like "a professional in a business suit"',
},
{
name: 'trademarks',
pattern: /\b(nik'Execute Adobe production deployment checklist covering credential management,.
Adobe Production Checklist
Overview
Complete checklist for deploying Adobe API integrations to production, covering credential security, health monitoring, graceful degradation, and rollback procedures.
Prerequisites
- Staging environment tested and verified
- Production OAuth credentials created in Developer Console
- Deployment pipeline with secret injection
- Monitoring and alerting infrastructure ready
Instructions
Pre-Deployment: Credentials & Configuration
- [ ] Production OAuth Server-to-Server credentials created (separate from staging)
- [ ]
ADOBECLIENTIDandADOBECLIENTSECRETstored in secret manager (not env files) - [ ] Scopes are minimal: only APIs actually used in production
- [ ] Token caching implemented (avoid re-generating per request)
- [ ] I/O Events webhook endpoints use HTTPS with valid TLS cert
- [ ] Webhook challenge response handler implemented (for registration)
Pre-Deployment: Code Quality
- [ ] All tests passing (
npm test) - [ ] No hardcoded credentials (grep for
p8_prefix patterns) - [ ] Error handling covers:
401,403,429,500,503 - [ ] Rate limiting/backoff with
Retry-Afterheader support - [ ] Webhook signature verification using RSA-SHA256
- [ ] Logging redacts credentials and PII
- [ ] API response validation (Zod or equivalent)
Pre-Deployment: Infrastructure
- [ ] Health check endpoint verifies Adobe IMS token generation:
// api/health.ts
export async function adobeHealthCheck() {
const start = Date.now();
try {
// Test token generation (validates credentials are still valid)
const token = await getAccessToken();
return {
status: 'healthy',
latencyMs: Date.now() - start,
tokenValid: !!token,
};
} catch (error: any) {
return {
status: 'unhealthy',
latencyMs: Date.now() - start,
error: error.message,
};
}
}
- [ ] Circuit breaker configured for Adobe API calls
- [ ] Graceful degradation: app works (degraded) if Adobe is down
- [ ] PDF Services monthly quota tracking (if on free tier)
Deploy: Gradual Rollout
# 1. Pre-flight checks
curl -sf https://staging.example.com/health | jq '.services.adobe'
curl -s https://status.adobe.com | head -5
# 2. Verify production credentials work
curl -s -o /dev/null -w "%{http_code}" -X POST \
'https://ims-na1.adobelogin.com/ims/token/v3' \
-d "client_id=${ADOBE_CLIENT_ID}&client_secret=${ADOBE_CLIENT_SECRET}&grant_type=client_credentials&scope=${ADOBE_S'Implement Adobe API rate limiting, backoff, and quota management across.
Adobe Rate Limits
Overview
Handle Adobe API rate limits gracefully with exponential backoff, Retry-After header support, and proactive quota management. Each Adobe API has different rate limits.
Prerequisites
- Adobe SDK installed and authenticated
- Understanding of async/await patterns
- Awareness of your API tier and entitlements
Instructions
Step 1: Know Your Rate Limits by API
| API | Limit | Scope | Response |
|---|---|---|---|
| Firefly API | ~20 req/min (trial), higher on paid | Per api-key | 429 + Retry-After |
| PDF Services | 500 tx/month (free), unlimited (paid) | Per credential | 429 or QUOTA_EXCEEDED |
| Photoshop API | Varies by entitlement | Per api-key | 429 + Retry-After |
| Lightroom API | Varies by entitlement | Per api-key | 429 + Retry-After |
| I/O Events Publishing | 3,000 req/5sec | Per api-key | 429 + Retry-After |
| Analytics 2.0 API | 12 req/6sec per user (~120 req/min) | Per user | 429 + Retry-After |
| IMS Token Endpoint | ~100 req/min | Per client_id | 429 |
Step 2: Implement Retry-After Aware Backoff
// src/adobe/rate-limiter.ts
import { AdobeApiError } from './client';
export async function withAdobeBackoff<T>(
operation: () => Promise<T>,
config = { maxRetries: 5, baseDelayMs: 1000, maxDelayMs: 60_000 }
): Promise<T> {
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
try {
return await operation();
} catch (error: any) {
if (attempt === config.maxRetries) throw error;
// Only retry on 429 and 5xx
const status = error.status || error.response?.status;
if (status && status !== 429 && (status < 500 || status >= 600)) throw error;
// Honor Adobe's Retry-After header (seconds)
let delay: number;
if (error.retryAfter) {
delay = error.retryAfter * 1000;
} else {
// Exponential backoff with jitter
const exponential = config.baseDelayMs * Math.pow(2, attempt);
const jitter = Math.random() * config.baseDelayMs;
delay = Math.min(exponential + jitter, config.maxDelayMs);
}
console.warn(
`Adobe rate limited (attempt ${attempt + 1}/${config.maxRetries}). ` +
`Waiting ${(delay / 'Implement Adobe reference architecture for production integrations covering.
Adobe Reference Architecture
Overview
Production-ready architecture patterns for Adobe API integrations, designed around the three main API families: Firefly Services (creative AI), PDF Services (document automation), and I/O Events (event-driven).
Prerequisites
- Understanding of layered architecture
- TypeScript project setup
- Decision on which Adobe APIs to integrate
Instructions
Step 1: Project Structure
my-adobe-project/
├── src/
│ ├── adobe/ # Adobe client layer
│ │ ├── auth.ts # OAuth Server-to-Server token management
│ │ ├── firefly-client.ts # Firefly API wrapper (generate, fill, expand)
│ │ ├── pdf-client.ts # PDF Services wrapper (create, extract, merge)
│ │ ├── photoshop-client.ts # Photoshop API wrapper (cutout, actions)
│ │ ├── events-client.ts # I/O Events registration and verification
│ │ ├── types.ts # Shared Adobe types
│ │ └── errors.ts # Error classification (retryable vs permanent)
│ ├── services/ # Business logic layer
│ │ ├── image-generation.ts # Orchestrates Firefly + Photoshop workflows
│ │ ├── document-pipeline.ts # Orchestrates PDF create/extract/merge
│ │ └── event-processor.ts # Routes and processes I/O Events
│ ├── api/ # API layer (routes, controllers)
│ │ ├── health.ts # Health check including Adobe IMS
│ │ ├── webhooks/adobe.ts # I/O Events webhook endpoint
│ │ └── routes/
│ │ ├── images.ts # Image generation endpoints
│ │ └── documents.ts # Document processing endpoints
│ ├── jobs/ # Background job layer
│ │ ├── firefly-batch.ts # Batch image generation queue
│ │ └── pdf-extraction.ts # Async PDF extraction worker
│ └── index.ts
├── tests/
│ ├── unit/
│ │ ├── adobe/auth.test.ts
│ │ └── services/
│ └── integration/
│ └── adobe/
│ ├── firefly.test.ts
│ └── pdf-services.test.ts
├── config/
│ ├── adobe.development.json
│ ├── adobe.staging.json
│ └── adobe.production.json
└── package.json
Step 2: Layer Architecture
┌─────────────────────────────────────────────────────┐
│ API Layer │
│ Routes, Controllers, Webhook Endpoints │
├─────────────────────────────────────────────────────┤
│ Service Layer │
│ Business Logic, Workflow Orchestration │
│ (image-generation.ts, document-pipeline.ts) │
├─────────────────────────────────────────────────────┤
│ Adobe Client Layer │
│ auth.ts, firefly-client.ts, pdf-client.ts │
│ Token caching, retry, error classification │
├──────────────────────────────────────────'Implement reliability patterns for Adobe APIs: circuit breakers for.
Adobe Reliability Patterns
Overview
Production-grade reliability patterns for Adobe API integrations. Adobe APIs present unique challenges: IMS tokens expire after 24h, Firefly/Photoshop jobs are async with variable completion times, and rate limits vary by API. These patterns address each failure mode.
Prerequisites
- Understanding of circuit breaker pattern
opossuminstalled for circuit breaker (npm install opossum)- Queue infrastructure (BullMQ/Redis) for dead letter queue
- Caching layer for fallback data
Instructions
Pattern 1: Circuit Breaker per Adobe API
Different Adobe APIs fail independently — use separate circuit breakers:
import CircuitBreaker from 'opossum';
// IMS circuit breaker (auth failures cascade to everything)
const imsBreaker = new CircuitBreaker(
async () => {
const res = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.ADOBE_CLIENT_ID!,
client_secret: process.env.ADOBE_CLIENT_SECRET!,
grant_type: 'client_credentials',
scope: process.env.ADOBE_SCOPES!,
}),
});
if (!res.ok) throw new Error(`IMS ${res.status}`);
return res.json();
},
{
timeout: 10_000, // IMS should respond in 10s
errorThresholdPercentage: 30, // Open after 30% errors
resetTimeout: 60_000, // Try again after 1 min
volumeThreshold: 3, // Minimum calls before tripping
}
);
// Firefly circuit breaker (higher tolerance for latency)
const fireflyBreaker = new CircuitBreaker(
async (fn: () => Promise<any>) => fn(),
{
timeout: 60_000, // Firefly jobs can take up to 60s
errorThresholdPercentage: 50,
resetTimeout: 30_000,
volumeThreshold: 5,
}
);
// PDF Services circuit breaker
const pdfBreaker = new CircuitBreaker(
async (fn: () => Promise<any>) => fn(),
{
timeout: 30_000,
errorThresholdPercentage: 40,
resetTimeout: 30_000,
volumeThreshold: 5,
}
);
// Monitor circuit state
for (const [name, breaker] of [['ims', imsBreaker], ['firefly', fireflyBreaker], ['pdf', pdfBreaker]] as const) {
breaker.on('open', () => console.warn(`Circuit ${name} OPEN — failing fast`));
breaker.on('halfOpen', () => console.info(`Circuit ${name} HALF-OPEN — testing recovery`));
breaker.on('close', () => console.info(`Circuit ${name} CLOSED — normal`));
}
Pattern 2: Graceful Degradation with Fallback
// When Adobe is down, return cached/default data instead of failing
interface FallbackResult<T> {
data: T;
'Apply production-ready patterns for Adobe Firefly Services SDK, PDF.
Adobe SDK Patterns
Overview
Production-ready patterns for Adobe SDK usage across Firefly Services (@adobe/firefly-apis, @adobe/photoshop-apis, @adobe/lightroom-apis), PDF Services (@adobe/pdfservices-node-sdk), and direct REST API calls.
Prerequisites
- Completed
adobe-install-authsetup - Familiarity with async/await patterns
- Understanding of the Adobe API you are integrating
Instructions
Pattern 1: Singleton Auth Client with Token Caching
// src/adobe/client.ts
import { ServicePrincipalCredentials, PDFServices } from '@adobe/pdfservices-node-sdk';
let pdfServicesInstance: PDFServices | null = null;
let tokenCache: { token: string; expiresAt: number } | null = null;
export function getPDFServices(): PDFServices {
if (!pdfServicesInstance) {
const credentials = new ServicePrincipalCredentials({
clientId: process.env.ADOBE_CLIENT_ID!,
clientSecret: process.env.ADOBE_CLIENT_SECRET!,
});
pdfServicesInstance = new PDFServices({ credentials });
}
return pdfServicesInstance;
}
export async function getAccessToken(): Promise<string> {
if (tokenCache && tokenCache.expiresAt > Date.now() + 300_000) {
return tokenCache.token;
}
const res = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.ADOBE_CLIENT_ID!,
client_secret: process.env.ADOBE_CLIENT_SECRET!,
grant_type: 'client_credentials',
scope: process.env.ADOBE_SCOPES!,
}),
});
if (!res.ok) throw new Error(`Adobe IMS token error: ${res.status}`);
const data = await res.json();
tokenCache = { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1000 };
return tokenCache.token;
}
Pattern 2: Typed API Wrapper with Error Classification
// src/adobe/firefly-client.ts
export class AdobeApiError extends Error {
constructor(
message: string,
public readonly status: number,
public readonly code: string,
public readonly retryable: boolean,
public readonly retryAfter?: number
) {
super(message);
this.name = 'AdobeApiError';
}
}
export async function adobeApiFetch<T>(
url: string,
options: RequestInit & { apiKey?: string }
): Promise<T> {
const token = await getAccessToken();
const { apiKey, ...fetchOptions } = options;
const response = await fetch(url, {
...fetchOptions,
headers: {
'Authorization': `Bearer ${token}`,
'x-api-key': apiKey || process.env.ADOBE_CLIENT_ID!,
'Content-Type': 'application/json',
...fetchOptions.headers,
'Apply Adobe security best practices for OAuth credentials, secret rotation,.
Adobe Security Basics
Overview
Security best practices for Adobe OAuth Server-to-Server credentials, I/O Events webhook signature verification, and least-privilege access control across Adobe APIs.
Prerequisites
- Adobe Developer Console access
- Understanding of OAuth 2.0 client_credentials flow
- Access to secret management solution (Vault, AWS Secrets Manager, GCP Secret Manager)
Instructions
Step 1: Secure Credential Storage
# .env (NEVER commit to git)
ADOBE_CLIENT_ID=abc123def456
ADOBE_CLIENT_SECRET=p8_XYZ_your_secret_here
ADOBE_SCOPES=openid,AdobeID,firefly_api
# .gitignore — MUST include these
.env
.env.local
.env.*.local
*.pem
*.key
# Production: use your cloud provider's secret manager
# AWS Secrets Manager
aws secretsmanager create-secret \
--name adobe/production/credentials \
--secret-string '{"client_id":"...","client_secret":"..."}'
# GCP Secret Manager
echo -n "your-client-secret" | gcloud secrets create adobe-client-secret --data-file=-
# HashiCorp Vault
vault kv put secret/adobe/prod client_id="..." client_secret="..."
Step 2: Credential Rotation
Adobe OAuth Server-to-Server credentials support multiple client secrets simultaneously, enabling zero-downtime rotation:
# 1. In Adobe Developer Console, generate a NEW client_secret
# (old secret remains valid)
# 2. Update your secret manager with the new secret
aws secretsmanager update-secret \
--secret-id adobe/production/credentials \
--secret-string '{"client_id":"...","client_secret":"NEW_SECRET"}'
# 3. Deploy application with new secret
# 4. Verify new secret works
curl -X POST 'https://ims-na1.adobelogin.com/ims/token/v3' \
-d "client_id=${ADOBE_CLIENT_ID}&client_secret=${NEW_SECRET}&grant_type=client_credentials&scope=${ADOBE_SCOPES}"
# 5. Delete old client_secret in Developer Console
Step 3: Least-Privilege Scope Selection
| Scope | Grants | Use When |
|---|---|---|
openid |
Basic identity | Always required |
AdobeID |
Adobe identity info | Always required |
firefly_api |
Firefly image generation | Firefly workflows only |
ff_apis |
Firefly Services (Photoshop, Lightroom) | Creative API workflows |
read_organizations |
Org info access | Multi-tenant apps |
// Per-environment scope restriction
const SCOPES_BY_ENV: Record<string, string> = {
d"Analyze, plan, and execute Adobe SDK upgrades \u2014 including the critical\n\.
Adobe Upgrade & Migration
Overview
Guide for the most critical Adobe migrations: JWT to OAuth Server-to-Server, PDF Services SDK v3 to v4, Photoshop API v1 to v2 endpoints, and general SDK version upgrades.
Prerequisites
- Current Adobe SDK installed
- Git for version control
- Test suite covering Adobe integration points
- Staging environment for validation
Instructions
Migration 1: JWT to OAuth Server-to-Server (CRITICAL)
Deadline passed: JWT (Service Account) credentials reached EOL June 2025. If you are still using JWT, migrate immediately.
// BEFORE (JWT — no longer works)
import jwt from 'jsonwebtoken';
const jwtToken = jwt.sign({
exp: Math.round(Date.now() / 1000) + 60 * 60 * 24,
iss: orgId,
sub: technicalAccountId,
aud: `https://ims-na1.adobelogin.com/c/${clientId}`,
'https://ims-na1.adobelogin.com/s/ent_firefly_sdk': true,
}, privateKey, { algorithm: 'RS256' });
// AFTER (OAuth Server-to-Server — current standard)
const tokenResponse = await fetch('https://ims-na1.adobelogin.com/ims/token/v3', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
client_id: process.env.ADOBE_CLIENT_ID!,
client_secret: process.env.ADOBE_CLIENT_SECRET!,
grant_type: 'client_credentials',
scope: process.env.ADOBE_SCOPES!,
}),
});
Migration steps:
- In Developer Console, open your project
- Click Add credential > OAuth Server-to-Server
- Assign same product profiles as your JWT credential
- Update application code to use
client_credentialsgrant - Remove
jsonwebtokendependency and private key files - Test in staging
- Deploy to production
- Delete the old JWT credential in Developer Console
Migration 2: PDF Services SDK v3 to v4
# Check current version
npm list @adobe/pdfservices-node-sdk
# Upgrade
npm install @adobe/pdfservices-node-sdk@latest
Key breaking changes v3 -> v4:
// BEFORE (v3)
import PDFServicesSdk from '@adobe/pdfservices-node-sdk';
const credentials = PDFServicesSdk.Credentials
.serviceAccountCredentialsBuilder()
.fromFile('pdfservices-api-credentials.json')
.build();
const executionContext = PDFServicesSdk.ExecutionContext.create(credentials);
const extractPDFOperation = PDFServicesSdk.ExtractPDF.Operation.createNew();
// AFTER (v4)
import {
ServicePrincipalCredentials,
PDFServices,
ExtractPDFJob,
ExtractPDFParams,
ExtractElementType,
} from '@adobe/pdfservices-node-sdk';
const credentials = new ServicePrincipalCredentials({
cl'Implement Adobe I/O Events webhook registration, RSA-SHA256 signature.
Adobe Webhooks & Events
Overview
Implement Adobe I/O Events webhook endpoints with proper challenge-response handshake, RSA-SHA256 digital signature verification, and event routing for Creative Cloud Libraries, Experience Platform, and Firefly Services events.
Prerequisites
- Adobe Developer Console project with Events API enabled
- HTTPS endpoint accessible from the internet
@adobe/aio-lib-eventsinstalled (optional, for SDK approach)- Understanding of Adobe I/O Events architecture
Instructions
Step 1: Register Webhook via Adobe I/O Events API
// Register a webhook endpoint programmatically
import { getAccessToken } from '../adobe/client';
interface EventRegistration {
name: string;
description: string;
webhookUrl: string;
eventsOfInterest: Array<{
provider_id: string; // Event provider (e.g., Creative Cloud)
event_code: string; // Specific event type
}>;
deliveryType: 'webhook' | 'webhook_batch';
}
export async function registerWebhook(reg: EventRegistration): Promise<any> {
const token = await getAccessToken();
const response = await fetch(
`https://api.adobe.io/events/${process.env.ADOBE_IMS_ORG_ID}/integrations/${process.env.ADOBE_INTEGRATION_ID}/registrations`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'x-api-key': process.env.ADOBE_CLIENT_ID!,
'Content-Type': 'application/json',
},
body: JSON.stringify({
client_id: process.env.ADOBE_CLIENT_ID,
name: reg.name,
description: reg.description,
webhook_url: reg.webhookUrl,
events_of_interest: reg.eventsOfInterest,
delivery_type: reg.deliveryType || 'webhook',
}),
}
);
if (!response.ok) throw new Error(`Registration failed: ${await response.text()}`);
return response.json();
}
// Example: Register for Creative Cloud Library events
await registerWebhook({
name: 'CC Library Updates',
description: 'Track Creative Cloud Library changes',
webhookUrl: 'https://api.yourapp.com/webhooks/adobe',
eventsOfInterest: [
{ provider_id: 'ccstorage', event_code: 'library_create' },
{ provider_id: 'ccstorage', event_code: 'library_update' },
{ provider_id: 'ccstorage', event_code: 'library_delete' },
],
deliveryType: 'webhook',
});
Step 2: Implement Challenge-Response Handshake
When registering a webhook, Adobe sends a GET request with a challenge query parameter. Your endpoint must respond with the challenge value:
import express from 'express';
const app = express();
app.get('/webhooks/adobe', (req, res) =>Ready to use adobe-pack?
Related Plugins
supabase-pack
Complete Supabase integration skill pack with 30 skills covering authentication, database, storage, realtime, edge functions, and production operations. Flagship+ tier vendor pack.
/plugin install supabase-pack@claude-code-plugins-plus
vercel-pack
Complete Vercel integration skill pack with 30 skills covering deployments, edge functions, preview environments, performance optimization, and production operations. Flagship+ tier vendor pack.
/plugin install vercel-pack@claude-code-plugins-plus
clay-pack
Complete Clay integration skill pack with 30 skills covering data enrichment, waterfall workflows, AI agents, and GTM automation. Flagship+ tier vendor pack.
/plugin install clay-pack@claude-code-plugins-plus
cursor-pack
Complete Cursor integration skill pack with 30 skills covering AI code editing, composer workflows, codebase indexing, and productivity features. Flagship+ tier vendor pack.
/plugin install cursor-pack@claude-code-plugins-plus
exa-pack
Complete Exa integration skill pack with 30 skills covering neural search, semantic retrieval, web search API, and AI-powered discovery. Flagship+ tier vendor pack.
/plugin install exa-pack@claude-code-plugins-plus
firecrawl-pack
Complete Firecrawl integration skill pack with 30 skills covering web scraping, crawling, markdown conversion, and LLM-ready data extraction. Flagship+ tier vendor pack.
/plugin install firecrawl-pack@claude-code-plugins-plus