What this is and why you need it
Gradial’s default AEM integration uses Adobe’s legacy IMS / JWT credential surface — which works well for page authoring and classic Launches. Content Fragment Launches, however, require a separate path: Adobe’s newer OpenAPI layer (/adobe/launches/...). To use that path, you need an OAuth Server-to-Server credential in Adobe Developer Console, an allowlist entry deployed via Cloud Manager’s Config Pipeline, and a second integration row in Gradial that pairs with your existing AEM row.
When this is set up correctly, Grady stages every CF edit into an AEM Launch under /content/launches/... instead of writing directly to master. Your team reviews the launch in Gradial, then promotes it when ready — no AEM UI required.
The seven gates
A Content Fragment edit successfully routed through Adobe’s OpenAPI only happens when all seven of the following conditions are true. Skip one and you will see 401, 403, or Gradial silently writing tomaster.
Roles and team coordination
This setup spans multiple Adobe consoles and typically requires 2–3 people on the customer side.XXXXXXXXXXXXXXXXX@AdobeOrg) is required throughout. Find it in Admin Console → Settings → Identity.
Prerequisites
Confirm the following before starting:Part 1 — Adobe-side setup (Steps 0–6)
Modernize the AEM environment (skip if profiles already exist)
- Go to adminconsole.adobe.com and switch to the customer’s IMS org.
- Products → Cloud Manager → add the customer’s admin to the Business Owner – Cloud Service profile.
- Open Cloud Manager, find the AEM environment, and click ⋮ → Add product profiles.
- Wait a few minutes, then refresh.
AEM Administrators - author - Program {ID} - Environment {ID}AEM Sites Content Managers - author - Program {ID} - Environment {ID}AEM Assets Collaborator Users - author - Program {ID} - Environment {ID}
Create an Adobe Developer Console project
- Go to developer.adobe.com and sign in with the customer’s IMS org.
- Rename the project to something recognizable (e.g.
Sites OpenAPIor<Customer> AEM OpenAPI). The auto-generated short name is the internal ID.
Add API products to the project
- Click Add to Project → API, search for the product name, then click Next.
- For Authentication type, select OAuth Server-to-Server.
- The first API you add creates the credential. Subsequent APIs attach to the same credential — one credential, multiple APIs is the correct result.
- For Select Product Profile, picking
AEM Administrators - author - …is the simplest path for initial setup. You can tighten this in Step 3. - Click Next → Save configured API.
Bind the credential to a product profile (Admin Console)
- Credential = who is calling (the OAuth S2S credential from Step 2)
- Product profile = what they can do in AEM (backed by an AEM system user with predefined ACLs)
- In Admin Console → Products → Adobe Experience Manager as a Cloud Service.
- Pick the environment (e.g.
Customer AEM Sandbox - author). Repeat for every environment you want OpenAPI active in. - Pick the profile:
AEM Administrators - author - Program {ID} - Environment {ID}. - Click the API Credentials tab → Add API Credential.
- Find your project (e.g.
Sites OpenAPI) and save.
AEM Sites Content Managers - author - …, then in AEM add that profile’s auto-created group to a custom group (e.g. openapi-cf-users) and grant it:- Read + write on
/content/launches - Read + write on the CF folders under
/content/dam/<site> - Execute on any workflow models triggered by API operations
Allowlist the Client ID via config/api.yaml
config/api.yaml at the top level of the AEM project repo:author:kindmust be"API",versionmust be"1.0"metadata.envTypescontrols which environments this applies to- Only add
publishorpreviewkeys if you are using the CF Delivery API on those tiers — for CF management and Launches,authoronly is correct - The Client ID is the long hex string from the Developer Console credential page — not the IMS Org ID and not the technical account email
- Cloud Manager → Pipelines → Add Pipeline.
- Select Non-Production (dev / stage) or Production pipeline type.
- Set the pipeline type to Deploy Configuration (not Fullstack, not Web Tier).
- Point it at the same Git repo and the branch where you committed
config/api.yaml. - Set Repository path to
/. - Save and run the pipeline.
api.yaml schema) and Set up OpenAPI-based AEM APIsCopy the credentials
invalid_scope, the most common cause is that AEM CS Sites Content Management was not attached in Step 2.Verify Adobe-side end-to-end
curl commands to confirm all Adobe gates are open before wiring up Gradial.Token exchange:/tmp/token means IMS is happy. invalid_scope means Step 2 or Step 3 has a gap.Hit an authenticated AEM endpoint:Part 2 — Gradial-side wiring (Step 7)
Once thecurl from Step 6 returns 200, the Adobe side is complete. Gradial-side wiring is quick but has a few important details.
Confirm both feature flags are enabled
- The AEM OpenAPI integration option — makes the Add Integration → AEM OpenAPI button appear in Settings
- The CF Launches routing — tells Gradial to route Content Fragment edits through the OpenAPI Launches path instead of writing directly to
master
Add the AEM OpenAPI integration row
aem row (for legacy page authoring) and a new aem-openapi row (for CF Launches). Do not modify the classic row’s credentials — you will only update its CF workflow setting in Step 4.To add the new row:- Fill in the form:
- Click Validate. Gradial performs a token-exchange dry run and hits the AEM endpoint. A green tick means credentials work.
- Click Save.
config/api.yaml not deployed (Step 4).Pair the classic aem row and aem-openapi row by service endpoint
service_endpoint string match. They must be byte-identical. Common mismatches:- Trailing slash on one row but not the other
- Vanity domain on one row,
*.adobeaemcloud.comon the other - HTTP vs HTTPS
- Different casing
https://author-p{PROGRAM}-e{ENV}.adobeaemcloud.com — no trailing slash, all lowercase.When pairing fails, aem.integration.cfWorkflow returns openApiAvailable: false even though credentials validated correctly.Set CF workflow mode to Staging Variation
aem row, not the OpenAPI row.- Settings → Integrations → click the classic
aemrow to edit it. - Under the General tab, find Content Fragments Workflow.
- Select Staging Variation.
- Save.
master even with the OpenAPI row configured.Verify with aem.integration.cfWorkflow
“What’s the active CF workflow for this integration?”Grady calls the
aem.integration.cfWorkflow tool and should return:Run a live smoke test
- Live edit. Ask Grady: “Update the headline of [some CF] to [new value].” Grady will create a launch, poll until it materializes, apply the edit to the launch copy, and hand off for review.
- AEM verification. Browse to
/content/launches/{YYYY}/{MM}/{DD}/...in the AEM author UI — the launch should be there with the staged edit. - Promote. In Gradial’s artifact card for the launch, click Promote. Gradial calls
POST /adobe/launches/{id}/promotethen cleans up the launch. Verify the source CF onmasterreflects the edit and the launch root is gone.
master directly, Step 4 (CF workflow) or Step 1 (AEM OpenAPI routing not enabled) is wrong. If the launch creation fails with 403, revisit the Step 6 curl test.Troubleshooting
401 from the IMS token endpoint
401 from the IMS token endpoint
- Wrong client secret — re-paste from Developer Console.
- Wrong scope — the scope list contains a scope no attached API grants. Usually
aem.launcheswhen AEM CS Sites Content Management is not attached. - Wrong token URL region — default is
ims-na1.adobelogin.com. EU / APAC orgs may need a different region.
401 from /adobe/... (AEM endpoint)
401 from /adobe/... (AEM endpoint)
- Token still cached — Gradial caches IMS tokens for ~5 minutes. Click Validate on the integration row to force a refresh.
- Missing
X-Api-Keyheader — Adobe requires both the Bearer token andX-Api-Keyon every OpenAPI call. Gradial sends both automatically; this matters only when testing with rawcurl.
403 from /adobe/... (most common failure)
403 from /adobe/... (most common failure)
- Step 4 was missed —
config/api.yamlis not deployed for this environment, or the Client ID in the file does not match the credential. Re-check that the Cloud Manager Config Pipeline ran successfully and the Client ID is listed underdata.allowedClientIDs.author. - Step 3 was missed — credential exists in Developer Console but is not bound to a product profile. Re-check Admin Console → Products → AEM CS → env → profile → API Credentials.
- Wrong product profile — credential is bound to a read-only profile and you are attempting a write operation. Bind to
AEM Administrators - author -…or follow the least-privilege recipe in Step 3.
404 from /adobe/...
404 from /adobe/...
AEM CS Sites Content Management does not appear in Developer Console
AEM CS Sites Content Management does not appear in Developer Console
Config pipeline succeeded but 403 persists
Config pipeline succeeded but 403 persists
- Wait 1–3 minutes. There is a short propagation delay after pipeline success.
- Check pipeline type. Must be Deploy Configuration, not Fullstack. Fullstack pipelines only deploy code-package artifacts (
ui.apps,ui.config). - Check
envTypesinapi.yaml. If you listed["dev"]and are testing against stage, the allowlist is inactive on stage. - Force-refresh the token. Click Validate on the Gradial integration row.
Add Integration → AEM OpenAPI option does not appear
Add Integration → AEM OpenAPI option does not appear
Validation passes but openApiAvailable is false
Validation passes but openApiAvailable is false
aem row and the aem-openapi row. Compare them character by character. Canonical form: https://author-p{PROGRAM}-e{ENV}.adobeaemcloud.com — no trailing slash, all lowercase.Edits land on master even though the OpenAPI row is configured
Edits land on master even though the OpenAPI row is configured
- CF workflow on the classic
aemrow is set to Staging Variation (not Direct Edit). - The AEM OpenAPI routing is enabled for your organization — confirm with your Gradial representative.
- Service endpoint matches byte-for-byte.
aem.integration.cfWorkflow check (Step 7e) to pinpoint which condition is failing.Rolling back
Rolling back
aem-openapi integration row can stay in place; it will be idle until OpenAPI routing is re-enabled.Multi-environment strategy
For production-grade rollouts, use a separate Developer Console project per environment type so credentials and blast radius stay isolated.config/api.yaml can list all three Client IDs under author with envTypes: ["dev", "stage", "prod"] — Adobe only honors entries matching the environment type the file is deployed to.
A note on JWT credentials
If you find documentation or sample code referencing Service Account (JWT) credentials, that path is end-of-life:- Adobe stopped supporting new Service Account (JWT) credentials on June 30, 2025
- Existing JWT credentials stop working when their certificate expires or by March 1, 2026, whichever is first
developer.adobe.com) which issues short-lived JWT dev tokens — those are not subject to the March 2026 deprecation and are used by Gradial’s classic aem row. For OpenAPI, you want developer.adobe.com and OAuth Server-to-Server only.End-to-end checklist
Print this and tick boxes as you go. The most recently ticked box is usually the right place to start debugging if something fails. Adobe-side- Prerequisites — IMS Org ID captured, AEM CS version confirmed ≥ 2026.01, AEM CS Sites Content Management entitlement present on the org
- Step 0 — Modernization done, or
AEM Administrators - author - …profile already visible in Admin Console - Step 1 — Empty Adobe Developer Console project created and renamed
- Step 2 — AEM CS Sites Content Management + AEM Assets Author API added, credential type is OAuth Server-to-Server
- Step 3 — Credential bound to the right product profile on every target environment; write access confirmed if CUD operations are needed
- Step 4a —
config/api.yamlcommitted to<repo>/config/api.yamlwith correctenvTypesand Client IDs - Step 4b — Cloud Manager Config Pipeline (Deploy Configuration) created, run successfully
- Step 5 — Client ID, Client Secret, and Token URL captured
- Step 6a —
curltoken exchange returns anaccess_token - Step 6b —
curlto/adobe/launchesreturns 200 with JSON
- Step 7a — Gradial representative has confirmed both AEM OpenAPI capabilities are enabled for your organization
- Step 7b —
aem-openapiintegration row added in Settings → Integrations, Validate returned green - Step 7c —
service_endpointmatches the classicaemrow byte-for-byte (no trailing slash, lowercase,*.adobeaemcloud.com) - Step 7d — Classic
aemrow’s Content Fragments Workflow set to Staging Variation - Step 7e —
aem.integration.cfWorkflowreturnsmode: "variation",openApiAvailable: true,openApiRoutingEnabled: true,recommendedPath: "cf-launches" - Step 7f — Test edit produces a launch under
/content/launches/...; Promote cleans up correctly
Reference
Adobe documentation- OpenAPI-based AEM APIs Overview
- Set up OpenAPI-based AEM APIs
- OpenAPI-Based APIs —
api.yamlschema - API Credentials and Product Profile management
- Services user-group permission management
- AEM as a Cloud Service Team and Product Profiles — modernization details
- JWT → OAuth Server-to-Server Migration Guide