Akeneo PIM — integration guide
This page walks through connecting a brand's Akeneo Enterprise or Community PIM to DPP Agent, end to end. It is the canonical reference for IT-security review.
What the adapter does, in one sentence
DPP Agent reads a tightly-scoped slice of Akeneo's product catalogue (specific channel, locale, family, attribute allowlist) and maps each returned product to a Digital Product Passport row, with explicit field-level mappings under your control. No write-back, no schema changes on your PIM, no internal-only data leaves your tenant.
Authentication
OAuth2 client credentials + service-account password — the standard
Akeneo grant_type=password flow. You provide four secrets at
connection time:
{
"client_id": "from Akeneo → Connect → Connection settings",
"client_secret": "(generated by Akeneo for this connection)",
"username": "akeneo-svc-dppagent",
"password": "(service account password)"
}
How those four secrets are protected
The client_secret and password are the sensitive pair. We treat
them as write-only credentials:
- Encrypted at rest. All four values land in our credential vault encrypted with AES-256-GCM. Each row is encrypted with a unique per-record nonce; the master key is held only in our hosting platform's encrypted environment-variable store. See the Security & privacy chapter for the full vault model.
- Never echoed back to the UI. Once saved, the admin UI shows a
redacted fingerprint (
first-4 + last-4). The full value is not retrievable through the UI by any role — to rotate, the operator pastes a new value over the old, which clears the row before the new one is written. - Never logged. Plaintext does not appear in application logs, request traces, audit-log payloads, or error reports.
- Decrypted only in-memory during a sync. The plaintext lives in the running serverless function for the duration of the Akeneo HTTP call, then drops out of scope when the function returns. No background workers hold the credentials between syncs.
- Easy revocation. We do not cache access tokens across runs. Revoking the service account on your Akeneo side takes effect on the next sync run with no further action from us.
Token exchange happens at the start of every sync run using the
stored client_id / client_secret plus the service account
username / password. The bearer token returned by Akeneo is
held only for the duration of that single run.
Scope limitation — six independent levers
DPP Agent is designed to read as little as possible from your PIM. Each lever below can be combined; together they reduce what leaves your Akeneo instance from "every attribute" down to "the eight fields ESPR + AGEC actually need".
1. Read-only OAuth scope
The Akeneo connection your admin creates for DPP Agent needs only:
Products: readChannels: readCatalog structure: read
No write scopes are requested. Even if our code were compromised it would be incapable of PATCHing a product, mutating an attribute, or seeding data back into your PIM.
2. Channel scoping
settings.channel = "ecommerce" (or your brand's actual channel
code) restricts every product fetch to attributes scope-linked to
that channel. Attributes scoped to internal channels — R&D,
finance, supply-chain ops — are never returned to us in the first
place. This is enforced by Akeneo, not by our code.
3. Locale scoping
settings.locale (or settings.locales for the multi-locale ingest
described in Locale) controls which translations
Akeneo serialises. A brand operating 30 locales internally for
print-catalog and translation-vendor purposes can ship us 1–6
public-facing locales for the consumer DPP.
4. Family filter
settings.family = "apparel,footwear" (comma-separated) makes the
adapter issue a server-side search query that filters on
family IN (...). Spare parts, marketing-only items, and unrelated
product trees never enter the response payload.
Akeneo also supports more elaborate filters (categories, completeness,
custom attribute predicates). Talk to us if you need a non-family
restriction — the settings.search_extra setting accepts any
Akeneo-valid JSON filter expression and we pass it through unchanged.
5. Attribute allowlist (per-tenant)
The adapter's Edit modal exposes a block of attr_* settings that
pin our canonical ESPR field IDs to your actual Akeneo attribute
codes:
attr_gtin = ean_code (default: ean)
attr_name = product_name (default: name)
attr_description = product_description (default: description)
attr_color = main_color (default: color)
attr_size = size_id (default: size)
attr_composition = fabric_content (default: composition)
attr_care = wash_label_text (default: care_instructions)
attr_image = primary_image (default: image)
Only attribute codes present in this allowlist are read out of the Akeneo response payload. If your products carry 400 attributes, the mapper still touches only the 8 you mapped. Everything else is dropped before it leaves the response parser.
6. Network-level IP allowlist (optional)
Our sync runs on Vercel's serverless platform. Vercel publishes the egress IP ranges for each region at https://vercel.com/docs/edge-network/regions. Your firewall / WAF can restrict your Akeneo API to those ranges so no other tenant of Vercel — and no actor outside it — can reach your PIM through our network path.
Settings reference
| Setting | Required | Purpose |
|---|---|---|
base_url |
yes | https://your-pim.cloud.akeneo.com |
client_id |
yes | Per Authentication |
client_secret |
yes | (password-style — write-only in UI) |
username |
yes | service-account username |
password |
yes | service-account password |
channel |
yes | Akeneo channel code, e.g. ecommerce |
locale |
yes | Default locale, e.g. en_GB. See Locale for multi. |
locales |
no | Array of locales to ingest as {locale: value} shape |
page_size |
no | 50–100. Default 100. |
family |
no | Comma-separated family codes to restrict |
search_extra |
no | Raw Akeneo search JSON to merge into the default query |
attr_* |
no | Per-field attribute-code overrides |
sync_max_pages |
no | Hard ceiling on per-sync pages — capped-by-default safety lever |
sync_unlimited |
no | true to walk the entire catalogue. Use after a small-batch dry run. |
sync_mode |
no | skeleton (default — fast, no heavy attributes) | full |
Sync semantics
Two modes:
- Skeleton (default, recommended for initial sync). Pulls identifiers + product/variant structure + GTIN + image only. Lands fast — enterprise-scale catalogues (≈10k products) inside the 300s Vercel function ceiling. Used to validate field mapping and populate skeleton DPPs that the brand can then publish or enrich.
- Full. Pulls every mapped attribute including BOM, supplier list, care instructions, certifications. Use after the skeleton pass confirms the mapping is right.
Subsequent sync runs respect the Conflict policy:
- Draft DPPs are overwritten freely
- Active DPPs spawn
dpp_revisions(status=pending)for human review - Withdrawn / archived / recalled DPPs are skipped
There is no automatic deletion. If a product disappears from your
Akeneo catalogue, the corresponding DPP remains in dpp_runtime and
becomes orphan (its source = akeneo but the upstream row is gone).
Orphans surface in the admin and the brand decides to withdraw,
archive, or leave them as historical.
What does the public URL serve after the upstream row is gone?
This is by regulatory design. EN 18219 §4.4 and EN 18223 §4.1.2 require a DPP to outlive the product it describes — both for consumer recall scenarios and for the audit retention period (default 10 years). Removing a product from Akeneo does not authorise removal of the DPP.
Concretely, while the DPP carries dpp_status = active:
- The public URL keeps resolving with the last successfully synced data. No new updates can arrive (Akeneo has nothing to give), so the rendered content is frozen at the most recent sync.
- Consumer scanners, brand-owned client apps, and the JSON-LD representation all see that frozen state.
- Market-surveillance and customs roles still have read access to the row even after the brand later flips status — that's the whole point of the retention requirement.
The brand decides — explicitly — when (and whether) to change status:
| Action | Public response | When to use |
|---|---|---|
| Stay active | 200 with the last-synced page | Product is no longer sold but a consumer may still own one |
| Withdrawn | 410 with a branded "no longer available" notice | Reformulated, replaced, or pulled from market |
| Archived | 410 with a historical-record notice | Permanently retired, kept for audit trail only |
| Recalled | 410 with a safety-recall notice + manufacturer link | Active safety recall |
In every case the row itself is preserved. Audit roles (market-surveillance, manufacturer, customs) can still resolve the content under their authentication; only the public face changes. This separation is what lets the brand take the consumer-facing page offline (or change its wording) without erasing the underlying record a regulator may need years later.
Onboarding sequence
- Brand-side IT-security approves the read-only service account
- Brand pastes
client_id / secret / username / passwordinto the DPP Agent Connect modal - Connection test: we run a probe against your channel — succeeds if the four secrets work and the channel is reachable
- Brand picks
channel,locale[s], optionallyfamily, fills in theattr_*allowlist if defaults don't match - Skeleton sync with
sync_max_pages = 2-3— confirms mapping on ~10–30 products - Brand reviews the resulting drafts in admin, tunes the
attr_*mapping if needed, and re-runs - Full sync (
sync_unlimited = true,sync_mode = full) once the mapping is locked in - Cron takes over — auto-sync on a 6h schedule by default. The interval is configurable per tenant: hourly, 6h, 12h, 24h, weekly or monthly. Choose the cadence that matches how often the brand updates its catalogue and how quickly downstream consumers need to see those changes
What you DON'T need to do
- No schema changes in Akeneo
- No PIM downtime
- No new attributes added (we read what you already have)
- No publishing back to your PIM
- No webhooks (we pull, you don't push — unless you want the separate webhook integration for real-time updates)
- No data leaves your channel/locale/family scope
Compliance hooks
The Akeneo adapter populates the following ESPR + AGEC fields out of the box (assuming the brand has filled them in upstream):
| ESPR / AGEC field | Akeneo source (default attribute code) |
|---|---|
espr.pid.gtin |
ean |
espr.pid.product_name |
name |
espr.pid.product_description |
description |
espr.pid.color |
color |
espr.pid.size |
size |
espr.mat.composition |
composition |
espr.care.instructions |
care_instructions |
tex.product.hero_image_url |
image |
espr.pid.product_brand |
(from tenant.brand_name) |
Use the per-tenant attr_* overrides to wire your actual codes.
For AGEC traceability (tex.journey.steps), Akeneo does not
typically structure supply-chain data in a way we can auto-map.
The pragmatic pattern is to maintain those rows in
TrusTrace or a dedicated traceability tool and
connect that adapter alongside.
Troubleshooting
- HTTP 401 on token exchange — service-account password is wrong, or the connection was deactivated in Akeneo
- HTTP 403 with
forbidden_scope— the connection lacks one of the three read scopes - Empty
_embedded.itemsdespite Akeneo having products — channel + locale combination has no scope-linked attributes - Slow sync, hitting 300s ceiling — switch to
sync_mode = skeletonfor the first pass, or setfamilyto restrict scope - HTTP 400 on the
search_extrafilter — Akeneo rejects the JSON; copy-paste your filter into Akeneo's own REST API console to validate the shape before pasting back here
Open a ticket at support@dppagent.com with the integration ID and
last sync log entry; we respond inside one business day.