Modular source operations are storefront-operator endpoints. Buyer agents do not call them directly. Buyers still transact through the storefront’s AdCP media-buy workflow.
prepare_modular_source, get_modular_inventory_source_readiness, and
prepare_modular_avails_commit. The Page and Tasks use named api_call
operations for their data and confirmed writes. The operator-facing operation
set also includes list_modular_inventory_source_products,
get_modular_inventory_capabilities, and
search_modular_source_inventory_selectors. Work-item detail, capacity and
booking changes, module configuration, and credential entry remain in
confirmation-gated trusted Tasks or the operator UI.
All examples use the storefront base URL:
When to use a modular source
Set up your storefront first, then decide which source type each inventory system should become:
A modular source is the right pilot shape when the seller can provide a feed of
wholesale inventory but cannot yet expose the whole lifecycle through one
upstream API.
A manual or back-office source is modular because no single system can answer
the complete AdCP workflow. A spreadsheet may describe what can be sold, an OMS
may record the booking, an ad server may execute it, and a person may move the
creative or final report between them. Modeling those responsibilities as
modules makes every supported and human-owned step explicit without claiming
automation that is not connected.
Modules
Each module owns one part of the lifecycle. A source can start with manual modules and later replace a step with an automated integration.
Every modular setup must account for these lifecycle responsibilities, even
when the current answer is a named human rather than an integration:
Manual ownership does not make a stage
READY. Readiness is reported from the
configured modules and runtime inputs; a human-owned stage can remain
HITL_PENDING, MISSING_SETUP, or UNSUPPORTED until its requirements are
met.
The CitrusAd pilot is a standard composition, so sellers connect it as an
ad server rather than assembling modules. It uses a retailer-namespace catalog
module and a campaign-execution module behind one inventory source. Because
that source is still composed from modules, creating it requires the same
Enterprise modular-source entitlement even though its dedicated ad-server
route is exposed independently of the advanced modular-source pilot flag. The
storefront remains in CHEF routing mode; buyers see the storefront, not a
CitrusAd adapter or the private module graph.
The first recipe supports sponsored-product campaigns. Their creative is
rendered from retailer catalog data, so creative sync completes without an
asset upload. CitrusAd also offers static banners and Banner X, but those are
not exposed by this recipe until separate banner campaign and creative APIs are
implemented and verified.
Connect the standard recipe through
POST /inventory-sources/ad-servers/citrusad, then save the storefront-scoped
credential through
POST /inventory-sources/{sourceId}/ad-servers/citrusad/credentials/{credentialKind}.
These ad-server endpoints do not expose the private module graph.
Module credentials
Credentials belong to one module instance on one storefront inventory source. They are not process environment variables and are not shared across storefronts. Rawauth values are written to the configured secret manager;
the API response contains status, scopes, expiry, and other non-secret metadata
only. Credential mutations require a storefront admin role or the dedicated
StorefrontCredentialsAdmin permission.
Create a credential with POST, rotate it with PUT, and revoke it with
DELETE at:
credentialKind is API_KEY, BEARER, or OAUTH. For example:
auth.
Storefront-level approval policies still apply outside the modular source. For
example, advertiser approval and creative review can happen before a modular
source asks a human to map the approved creative into an upstream ad server.
Seller setup checklist
Ask the seller for whatever they already have, then classify each file into one of these setup areas:
Prepare representative materials next: a real avails export, existing booking
or reservation export, creative specifications and approval rules, a sample
trafficking order, and a sample delivery report. Include the identifiers used
to join those artifacts and name the human owner for every manual handoff. A
media kit or rate card is useful context, but it does not replace row-level
avails or lifecycle evidence.
Media kits, co-viewing studies, market-size notes, and screenshots are useful
context. They are not avails feeds unless they contain row-level sellable
capacity for a specific collection and time window.
The three setup surfaces in chat
Setting up and running a modular source is three surfaces, each with one job. Ask Murph for any of them by name, or move between them from the workspace.
Two things follow from that split:
- Creating a source does not ask how you book, traffic, clear creative, or report. Those are derived per lifecycle stage from the modules attached to the source, and shown in the workspace — see Check readiness. Every modular source starts with an avails-feed module and Interchange’s booking ledger.
- Nothing is offered to buyers until avails rows are committed. A source with no committed rows is registered but has no sellable capacity.
Preview buyer discovery from one source
When Get products is Ready in the Modular inventory source workspace, select Preview buyer discovery. In the Source discovery preview Task, enter a representative buyer brief and select Run preview. The Task shows only products that source would return through your storefront. The preview rechecks that the source is active and Get products is still Ready when you run it. If either check fails, the Task stops and asks you to finish the source setup. It never includes products from another inventory source. The Task records the preview decision for later review. It does not create a media buy, change live products or source setup, or turn on transacting. Brief tests started outside a source workspace remain storefront-wide.Avails feed requirements
The first successful upload should be boring: one row per sellable line, with stable ids and clear capacity semantics. Required columns:
Optional columns:
If the seller only has gross capacity, include one of
avails, impressions,
monthlyAvails, monthlyVideoAvails, or monthlyHomeScreenAvails, plus
bookedImpressions, upstreamBookedImpressions, booked, or
reservedImpressions. Preview will calculate net capacity as:
150m, 25 million, or 1.5B.
Murph walkthrough
Murph should not force a seller into a blank template. A good setup conversation looks like this:- Ask the seller to upload what they have: deck, media kit, rate card, publisher list, avails export, booking export, creative specs, reporting export, or screenshots.
- Classify the material. Keep market context as context; do not commit it as avails.
- Ask for missing row-level avails if the seller only supplied market scale.
- Create the modular source only after the operator confirms the source name and lifecycle plan.
- Preview the feed. Show normalized rows and whether capacity is net or gross-minus-booked.
- Commit only after explicit confirmation.
- Run a small test reservation and validate the handoff requirements. Only run finalization when the source has a real execution module or a configured demo execution module.
- Route unsupported integrations or missing systems into the normal Murph escalation flow so the need is tracked centrally.
1. Check readiness
Start with the runtime projection:lifecycleSummary, one row per stage:
activeAvailCount tells you whether the source currently has usable avails. openWorkItemCountsByKind tells you what human queue is blocking a module or stage, for example MAP_CREATIVE_REF or UPLOAD_FINAL_REPORT.
2. Preview, then commit avails
An inventory-feed module accepts normalized avails. The feed can come from a publisher upload, a pasted CSV/JSON payload, or a file that Murph has parsed into rows. Murph previews parsed rows first; commit the feed only after an operator confirms the normalized rows and confirmsimpressionsCapacity is net sellable capacity.
A preview is row-tolerant: one invalid row does not hide valid rows. The response
returns normalized accepted rows in avails, invalid source rows in
rejectedRows, and non-blocking normalization facts in warnings, with matching
counts. Each diagnostic has a stable code, source rowNumber, safe message,
and field when the field is known. Commit by sending only the accepted avails
back with confirmed: true; rejected rows and warnings are never committed.
Current row diagnostic codes are REQUIRED_FIELD_MISSING, INVALID_DATE_TIME,
INVALID_NUMBER, INVALID_CANONICAL_FORMATS,
INVALID_PUBLISHER_PROPERTIES, and GROSS_CAPACITY_NORMALIZED. The last is a
warning that net sellable capacity was calculated by subtracting upstream
booked impressions from gross capacity.
Formats and properties belong to the stable collection rather than one
availability window. Supply formatOptions and publisherProperties on each
row, or send defaultFormatOptions and defaultPublisherProperties alongside a
JSON preview request. Spreadsheet cells use JSON-encoded arrays. Every row with
the same collectionId must repeat the same declarations. The service rejects
missing, conflicting, malformed, or URL-bearing declarations and never infers a
format from channel.
availId. If an avail already has held or booked impressions, the feed update will not reduce capacity below those commitments.
3. Inspect product projections
List the product-like projections generated from active avails:formatOptions and publisherProperties. A collection missing valid
declarations fails closed with the typed MODULAR_CATALOG_NOT_READY readiness
code before Chef or another model can see it. Generic feed-only sources return
cadent: null. Cadent fields appear only when the source has an active Cadent
execution module, so uploaded publisher avails are not implicitly treated as
Cadent-backed inventory.
4. Reserve capacity
If the source has an active booking-ledger module, hold capacity before finalizing:productId or availId. The reservation prevents this modular source from overbooking against capacity it controls. Upstream booked supply must be removed from impressionsCapacity before commit; upstreamBookedImpressions is explanatory metadata and does not increase sellable capacity.
5. Prepare supported execution handoffs
Execution is module-specific. In the current Cadent demo path, the handoff requires an active booking ledger and an active Cadent execution module. It keeps the reservationHELD, renders the Cadent campaign XML, and creates source-side work items for trafficking, creative mapping, and reporting. Do not treat the line as upstream-booked until the trafficking step succeeds; completing the TRAFFIC_TO_CADENT work item moves the held reservation to BOOKED.
6. Release capacity
Release a held reservation when a buyer cancels, a hold expires, or the execution handoff fails:HELD reservations continue to reduce
available capacity until they expire or are released through this operation;
BOOKED reservations remain booked. Open source-side work items and pending
reports keep their current status and history. Disabling pilot access prevents
new modular-source interactions, but it does not represent upstream
cancellation, completed human work, or completed reporting.
7. Work the human queue
Some stages are intentionally human-in-the-loop. Common examples:
These work items belong to the inventory source, not the buyer-facing storefront task. The storefront can still expose buyer task status or webhooks while source operators complete the underlying queue.
Demo profiles and legacy source recipes
The canonical product demo is Demo Storefront. Its versioned, code-owned profiles compose existing modular contracts and are clearly labeled synthetic. They do not call or claim to emulate a vendor API.POST /inventory-sources/modular/cadent-demo creates or resets a demo-only source with static avails, a booking ledger, and Cadent execution. Use it to test the end-to-end lifecycle, not as a publisher avails ingestion path. Demo source IDs must be talpa-cadent-demo or start with demo-.
That Cadent-shaped recipe and the FreeWheel sandbox route remain useful
integration-development precedents. They are not the backing data plane for
Demo Storefront and do not turn a vendor sandbox into the canonical demo.
Related tasks
Get modular readiness
Inspect lifecycle status, module contracts, setup gaps, and work-item counts
Update module config
Write non-secret module setup fields