Adobe Campaign Standard Developer Expert study materials
Adobe Campaign Standard Developer Expert study resources

480,000 API upserts a night, or one file

Adobe Campaign Standard Developer Expert · Put your knowledge to work in context.

All case studies

480,000 API upserts a night, or one file

A loyalty platform must push 480,000 profile updates (points balance and tier) to ACS every night by 06:00, and read back delivery logs for 2 million messages. The proposed design is a script that calls the Profile and Services API once per profile, then a workflow JavaScript activity that re-queries each profile to write the tier change. In test, 5,000 profiles took 11 minutes and produced HTTP 429 responses, which the script retried immediately. Reading logs through the API times out for large date ranges. The loyalty platform can write a CSV to the SFTP server shared with ACS. The security team asks how API credentials would be issued and whether the integration can use an existing shared administrator login to save time.

1. Choose the integration pattern for the write path and the log read-back. Show the arithmetic and the decision rule, not just the preference.

Read the worked answer

Do the arithmetic first. 5,000 profiles in 11 minutes is about 7.5 profiles per second. At that rate, 480,000 profiles takes about 17.5 hours, which does not fit a 06:00 deadline before any retry, and the 429 responses mean real throughput is lower still.

Use the file for bulk writes: the loyalty platform writes a CSV to the shared SFTP, a scheduled workflow transfers and imports it into a staging resource, reconciles on the loyalty ID as the dedup key, updates profile fields, and exports a result and error file back to SFTP. This is one stream, restartable, and independent of API rate limits. It is also idempotent: the extract date plus loyalty ID identifies the update.

Keep the API for what it is good at: single events, near-real-time updates, and reads that cannot wait for a batch.

For the 2 million log rows, do not read message by message. Export broadLog rows from a workflow with a date filter, in daily slices, and let the loyalty platform collect the files. The decision rule is volume, latency requirement and idempotency, not a preference for REST.

2. Specify the credential model, the retry behaviour and the error handling you would require from the integration team.

Read the worked answer

Credentials: create a dedicated Adobe I/O server-to-server credential for this integration and map it to a product profile with only the permissions the integration needs. Store the secret in a managed vault and rotate it on a schedule. A shared administrator login is not acceptable: it cannot be attributed in the audit trail, it cannot be scoped, and rotating it breaks every integration at once. It should never sit in a workflow JavaScript activity, on the SFTP server, or in client code; if a third party needs access, put a controlled middleware in front.

Retries: treat 429 as back-pressure, not as an error. Honour Retry-After, use exponential backoff with jitter, cap concurrency, and add a circuit breaker so a struggling instance is not hammered. Every retry must be idempotent: update or upsert by business key, never create, and carry a correlation ID so duplicates can be detected.

Error handling: log status code, request ID and payload key for every failure, write rejected rows to an error file with a reason, alert on error rate rather than on single failures, and make the batch resumable so a restart does not reprocess the whole extract.

3. The workflow JavaScript activity re-queries each profile to write the tier. Replace it and justify the change in terms the developer will accept.

Read the worked answer

The JavaScript is doing an N+1 query: one query per profile, 480,000 times, each a round trip plus deserialisation, plus a write each time. The cost is latency per row, not computation, which is why it looks fine on 500 rows and collapses on 480,000.

Replace it with declarative activities. One Query or Enrichment joins the staging rows to profiles, and an Update data activity writes the tier field in bulk. The workflow engine is built for set-based work. The JavaScript interpreter is for orchestration, validation and small transformations, and a long loop holds an engine process, risks a timeout, and makes restarts expensive.

If JavaScript is genuinely needed, read the population once into a collection, compute in memory, and write in batches with a single session call per batch, with a hard cap and a checkpoint so a restart does not begin at zero.

Better still, compute the tier at the reconciliation step where the points balance already changes, so there is one write path and one place to test. Show the developer the before-and-after activity durations; that argument lands better than a style opinion.