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.
This guide covers AEM as a Cloud Service only. If you are on AEM 6.5, the CF Launches OpenAPI path requires 6.5 LTS SP2+ and the same Adobe-side steps apply, but contact your Gradial representative before proceeding.

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 to master.
Gate 4 is the one nearly everyone misses. The credential will validate, the token will exchange, and AEM will still return 403 until the Client ID is in config/api.yaml and that file has been deployed via a Config Pipeline — not a Fullstack pipeline.
Gate 6 requires a byte-for-byte match. Gradial pairs the classic aem row and the aem-openapi row by exact service_endpoint string. A trailing slash on one row and not the other is enough to break pairing.

Roles and team coordination

This setup spans multiple Adobe consoles and typically requires 2–3 people on the customer side. The customer’s IMS Org ID (format: 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)

1

Modernize the AEM environment (skip if profiles already exist)

Many older environments do not show the OpenAPI-related product profiles by default.
  1. Go to adminconsole.adobe.com and switch to the customer’s IMS org.
  2. Products → Cloud Manager → add the customer’s admin to the Business Owner – Cloud Service profile.
  3. Open Cloud Manager, find the AEM environment, and click ⋮ → Add product profiles.
  4. Wait a few minutes, then refresh.
In Admin Console → Products → AEM CS → environment → Manage Access → Author Profiles, you should now see:
  • 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}
These are the profiles you will bind the credential to in Step 3.
2

Create an Adobe Developer Console project

  1. Go to developer.adobe.com and sign in with the customer’s IMS org.
  2. Rename the project to something recognizable (e.g. Sites OpenAPI or <Customer> AEM OpenAPI). The auto-generated short name is the internal ID.
The project starts with one workspace, Production. Do not add others — one workspace means one credential to maintain.
Create a separate Developer Console project per AEM environment type (dev / stage / prod). This keeps Client IDs cleanly separated and allows you to scope api.yaml allowlists per environment.
3

Add API products to the project

In the Production workspace, click Add to Project → API.Required for most CF workflows:How to add each API:
  1. Click Add to Project → API, search for the product name, then click Next.
  2. For Authentication type, select OAuth Server-to-Server.
    Do not pick the deprecated JWT flow. Adobe Service Account (JWT) credentials reached end of life on June 30, 2025, and remaining JWT credentials will stop working by March 1, 2026. All new integrations must use OAuth Server-to-Server.
  3. The first API you add creates the credential. Subsequent APIs attach to the same credential — one credential, multiple APIs is the correct result.
  4. For Select Product Profile, picking AEM Administrators - author - … is the simplest path for initial setup. You can tighten this in Step 3.
  5. Click Next → Save configured API.
After adding all APIs, verify that OAuth Server-to-Server appears under Credentials in the left sidebar with all APIs attached.
4

Bind the credential to a product profile (Admin Console)

Adobe’s authorization uses a two-layer model:
  • 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)
Binding a credential to a content-management product profile grants read access by default. For create, update, or delete operations, the technical account also needs a profile with write rights. The simplest option is AEM Administrators - author - …. See the least-privilege option below for production.
Standard binding (quickest path):
  1. In Admin Console → Products → Adobe Experience Manager as a Cloud Service.
  2. Pick the environment (e.g. Customer AEM Sandbox - author). Repeat for every environment you want OpenAPI active in.
  3. Pick the profile: AEM Administrators - author - Program {ID} - Environment {ID}.
  4. Click the API Credentials tab → Add API Credential.
  5. Find your project (e.g. Sites OpenAPI) and save.
If your project does not appear, the credential does not yet have an AEM API attached — go back to Step 2.Least-privilege binding (recommended for production):For production, use a narrower profile such as 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
5

Allowlist the Client ID via config/api.yaml

This is the step almost everyone misses. Even with a valid credential bound to the right product profile, AEM returns 403 on every OpenAPI call until the Client ID is allowlisted in config/api.yaml and deployed via a Config Pipeline. Fullstack pipelines do not deploy config/.
The fileAdd config/api.yaml at the top level of the AEM project repo:
Contents:
If you have separate Developer Console projects per environment, list all Client IDs under author:
Schema notes:
  • kind must be "API", version must be "1.0"
  • metadata.envTypes controls which environments this applies to
  • Only add publish or preview keys if you are using the CF Delivery API on those tiers — for CF management and Launches, author only 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
Deploy via Cloud Manager Config Pipeline
  1. Cloud Manager → Pipelines → Add Pipeline.
  2. Select Non-Production (dev / stage) or Production pipeline type.
  3. Set the pipeline type to Deploy Configuration (not Fullstack, not Web Tier).
  4. Point it at the same Git repo and the branch where you committed config/api.yaml.
  5. Set Repository path to /.
  6. Save and run the pipeline.
After the pipeline succeeds, allow 1–3 minutes before testing — there is a short propagation delay before AEM author starts accepting tokens.
If you have an RDE (Rapid Development Environment), you can skip the pipeline and install directly: aio aem rde install --config-pipeline ./config/api.yaml
6

Copy the credentials

In Developer Console → your project → OAuth Server-to-Server under Credentials:The OAuth scope set Gradial uses is:
Adobe derives these scopes automatically from the API products attached to the credential — you do not add scopes manually. If token exchange returns invalid_scope, the most common cause is that AEM CS Sites Content Management was not attached in Step 2.
7

Verify Adobe-side end-to-end

Run these two curl commands to confirm all Adobe gates are open before wiring up Gradial.Token exchange:
A JWT-like string in /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 the curl from Step 6 returns 200, the Adobe side is complete. Gradial-side wiring is quick but has a few important details.
1

Confirm both feature flags are enabled

Before wiring up the integration, confirm with your Gradial contact that your organization has the AEM OpenAPI integration enabled. Two capabilities must be active:
  • 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
Ask your Gradial contact or customer success manager to confirm both are enabled for your organization before proceeding.
2

Add the AEM OpenAPI integration row

In Gradial, navigate to Settings → Integrations for the customer’s workspace.You will end up with two AEM rows: the existing classic 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:
  1. Fill in the form:
  1. Click Validate. Gradial performs a token-exchange dry run and hits the AEM endpoint. A green tick means credentials work.
  2. Click Save.
If validation fails, the most common causes are: wrong client secret, missing API product on the credential (Step 2), no profile binding (Step 3), or config/api.yaml not deployed (Step 4).
3

Pair the classic aem row and aem-openapi row by service endpoint

Gradial pairs the two rows via exact 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.com on the other
  • HTTP vs HTTPS
  • Different casing
Canonical form: https://author-p{PROGRAM}-e{ENV}.adobeaemcloud.com — no trailing slash, all lowercase.
Vanity domains are not supported for the OpenAPI row. The IMS token’s aud claim is bound to the adobeaemcloud.com hostname. If the classic aem row uses a vanity domain, update it to use the canonical form as well.
When pairing fails, aem.integration.cfWorkflow returns openApiAvailable: false even though credentials validated correctly.
4

Set CF workflow mode to Staging Variation

This setting lives on the classic aem row, not the OpenAPI row.
  1. Settings → Integrations → click the classic aem row to edit it.
  2. Under the General tab, find Content Fragments Workflow.
  3. Select Staging Variation.
  4. Save.
Without this, Gradial writes CF edits directly to master even with the OpenAPI row configured.
5

Verify with aem.integration.cfWorkflow

Open a Gradial thread connected to the customer’s AEM workspace and ask Grady:
“What’s the active CF workflow for this integration?”
Grady calls the aem.integration.cfWorkflow tool and should return:
If any field is wrong:
6

Run a live smoke test

  1. 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.
  2. AEM verification. Browse to /content/launches/{YYYY}/{MM}/{DD}/... in the AEM author UI — the launch should be there with the staged edit.
  3. Promote. In Gradial’s artifact card for the launch, click Promote. Gradial calls POST /adobe/launches/{id}/promote then cleans up the launch. Verify the source CF on master reflects the edit and the launch root is gone.
If the edit lands on 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

IMS rejected the credentials. Most common causes:
  • Wrong client secret — re-paste from Developer Console.
  • Wrong scope — the scope list contains a scope no attached API grants. Usually aem.launches when 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.
Token exchange worked, but AEM rejected the token.
  • Token still cached — Gradial caches IMS tokens for ~5 minutes. Click Validate on the integration row to force a refresh.
  • Missing X-Api-Key header — Adobe requires both the Bearer token and X-Api-Key on every OpenAPI call. Gradial sends both automatically; this matters only when testing with raw curl.
Token is valid but not authorized. Almost always one of three things:
  1. Step 4 was missedconfig/api.yaml is 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 under data.allowedClientIDs.author.
  2. 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.
  3. 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.
The credential authorizes correctly but the AEM CS version does not include this API surface. Confirm the CS release is ≥ 2026.01 for CF Launches. Check Cloud Manager → environment details.
Adobe needs to enable this entitlement on the customer’s IMS org. Open a ticket with Adobe support, provide the IMS Org ID, and request that AEM CS Sites Content Management be enabled for the org’s AEM as a Cloud Service entitlement. This is an org-level toggle that applies to all environments.
  • 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 envTypes in api.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.
The AEM OpenAPI integration is not yet enabled for your organization. Contact your Gradial representative to have it activated.
Service endpoint mismatch between the classic 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.
Check all three:
  1. CF workflow on the classic aem row is set to Staging Variation (not Direct Edit).
  2. The AEM OpenAPI routing is enabled for your organization — confirm with your Gradial representative.
  3. Service endpoint matches byte-for-byte.
Run the aem.integration.cfWorkflow check (Step 7e) to pinpoint which condition is failing.
Contact your Gradial representative to disable the OpenAPI routing for your organization. Gradial will revert to the legacy Launch flow without requiring any changes to the integration row or AEM configuration. The 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. A single 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
Always use OAuth Server-to-Server in Step 2. If you are migrating an existing JWT credential, see Adobe’s JWT → OAuth migration guide — the new credential keeps the same Client ID, technical account, API attachments, and product profiles.
AEM also has its own AEM Developer Console (built into AEMaaCS, distinct from 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 4aconfig/api.yaml committed to <repo>/config/api.yaml with correct envTypes and Client IDs
  • Step 4b — Cloud Manager Config Pipeline (Deploy Configuration) created, run successfully
  • Step 5 — Client ID, Client Secret, and Token URL captured
  • Step 6acurl token exchange returns an access_token
  • Step 6bcurl to /adobe/launches returns 200 with JSON
Gradial-side
  • Step 7a — Gradial representative has confirmed both AEM OpenAPI capabilities are enabled for your organization
  • Step 7baem-openapi integration row added in Settings → Integrations, Validate returned green
  • Step 7cservice_endpoint matches the classic aem row byte-for-byte (no trailing slash, lowercase, *.adobeaemcloud.com)
  • Step 7d — Classic aem row’s Content Fragments Workflow set to Staging Variation
  • Step 7eaem.integration.cfWorkflow returns mode: "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

Need Help?

Contact your Gradial representative for support with your AEM OpenAPI setup, or reach out via the support portal.