Programmatic Page Deletion in AEM as a Cloud Service — Looking for Feedback on Our Approach
Background
We are building an automated content sync pipeline on AEM as a Cloud Service that reads product data from an external PIM system (Salsify) and manages Content Fragments and Product Detail Pages on AEM Author.
We have three Adobe App Builder actions deployed on Adobe I/O Runtime that together manage the full product content lifecycle:
| Action | What it does | AEM credentials used |
|---|---|---|
| Product Sync | Fetches all products from the PIM channel export and upserts them as AEM Content Fragments (create or update). Includes a duplicate activation guard to prevent concurrent executions. | IMS OAuth (CF Management API) |
| PDP Page Creation | Reads synced parent Content Fragments, filters to those flagged for web publication, and deletes + recreates their Product Detail Pages on every run | IMS OAuth + AEM Developer Console JWT |
| Orphan Cleanup | Detects CFs and PDP pages in AEM whose products no longer exist in the PIM feed and deletes them. Supports two modes: single SKU (fast, no PIM API call needed) and full bulk reconciliation (fetches feed, compares against all CFs in AEM). Includes a feed-size guard that aborts all deletions if the feed returns fewer products than a configured minimum threshold. | IMS OAuth + AEM Developer Console JWT |
The PDP Page Creation and Orphan Cleanup actions both call the same custom Sling servlet on AEM Author for all page operations.
Our Planned Approach
We are planning to update the servlet to call Replicator.DEACTIVATE before the JCR removal:
@Reference
private Replicator replicator;
try {
replicator.replicate(session, ReplicationActionType.DEACTIVATE, pagePath);
log.info("Deactivated page {}", pagePath);
} catch (ReplicationException e) {
// If page was never published, deactivation fails — log and continue
log.warn("Deactivation failed for {} — continuing with Author delete: {}", pagePath, e.getMessage());
}
session.getNode(pagePath).remove();
session.save();
We also plan to add ReplicationActionType.ACTIVATE after page creation in the same servlet, since our sync deletes and recreates pages on every run. Without the ACTIVATE step, adding DEACTIVATE alone would take pages offline without ever republishing them.
For the service user, we plan to add crx:replicate to its principal ACL via repoinit:
ensure principal ACL for my-sync-service
allow jcr:read, jcr:write, jcr:versionManagement, jcr:nodeTypeManagement, crx:replicate on /content/your-site
allow jcr:read on /conf/your-site
allow jcr:read on /content/dam/your-project
end
---
Our Questions
1. Is the Replicator API the right approach for AEM as a Cloud Service, or should we be using the Sling Distributor API (DistributionRequestType.DELETE) instead?
2. Is crx:replicate the correct and sufficient privilege for the service user to trigger deactivation, or are additional permissions needed?
3. Are there any AEM as a Cloud Service-specific caveats we should be aware of with this approach — particularly around the service user context and the Session passed to Replicator.replicate()?
4. For the paired create/delete cycle (deactivate → delete → create → activate), is there a risk of the page being briefly unavailable on Publish between the deactivate and activate steps? If so, is there a recommended way to minimise this window?
5. Any other patterns or gotchas the community has encountered when programmatically managing page publish state from a service user context in AEMaaCS?
---
Additional Context
In our sync model, child/variant products are embedded inside the parent product's Content Fragment as a JSON field rather than stored as separate CFs or pages. This means child deletions are handled automatically by re-running the Product Sync — only parent product deletions require the explicit deactivate + delete flow via the Orphan Cleanup action.
Thanks in advance for any guidance!