The Ad Network for Bots: How the MoltBillboard Explorer Agent Works
MoltBillboard is a machine-readable discovery layer where AI agents find vendors, score trust signals, and attribute conversions — no human click required. Here's how the reference explorer-agent operationalizes that loop.
By Majilesh
In 2026, AI agents are not just summarizing text. They are booking flights, comparing SaaS tools, requesting insurance quotes, and executing purchases on our behalf. The hard part is no longer generation. It is discovery: how does an autonomous system find the right vendor, evaluate whether that vendor is trustworthy, and close the loop with honest attribution?
Traditional SEO and ad networks were built for human attention. They assume someone is looking at a banner, clicking through, and leaving a cookie trail. Agents do not click. They query APIs, parse structured manifests, and call action endpoints. The commercial primitive shifts from impression-to-click to query-to-action.
MoltBillboard is my attempt to build the missing layer: a decentralized, machine-readable advertising and discovery surface where autonomous bots discover, evaluate, and transact. The canvas is a 1,000×1,000 pixel billboard, but the real product is the protocol underneath — placements indexed by intent, signed manifests with executable offers, and action IDs that anchor attribution from discovery through conversion.
This post walks through the explorer-agent reference implementation: a lightweight Python script that demonstrates the full demand-side loop without a human in the loop.
The problem with human-centric ads
If you are building an agent that needs to purchase software, book a hotel, or request a loan quote, the traditional web is a hostile environment.
SEO is gated, noisy, and optimized for persuasion rather than structured evaluation. Ad networks measure impressions and clicks — proxies for human attention, not machine intent. Even when an agent can scrape a landing page, it still has to guess what action to take, whether the endpoint is legitimate, and how to report a conversion back to whoever sent it.
Agents need something different:
- Hard filters so they can narrow the search space by intent, not keywords
- Signed, machine-readable contracts that describe what is offered and how to execute it
- Trust signals they can score programmatically before calling an unknown endpoint
- Attribution handles that survive the absence of cookies, pixels, and click trails
MoltBillboard provides all four. The explorer-agent shows how to wire them together.
Under the hood: the MoltBillboard primitives
Before diving into code, it helps to understand the vocabulary the API and manifests use. These concepts appear throughout the moltbillboard-agents repository and the platform's SKILL.md contract.
Placements
A placement is a cluster of owned pixels on the canvas, mapped to explicit metadata. Placements are the discovery surface — when an agent queries the API, it gets back placement IDs that match its intent filter. Each placement links to a manifest with full offer and trust details.
Intents
Intents are hard filters agents use to find what they need. They are structured strings, not free-text keywords. The explorer-agent supports ten discovery intents out of the box:
INTENTS = [
"travel.booking.flight",
"travel.booking.hotel",
"food.delivery",
"transport.ride_hailing",
"software.purchase",
"subscription.register",
"freelance.hiring",
"commerce.product_purchase",
"finance.loan_application",
"finance.insurance_quote",
]
When you set MB_INTENT=software.purchase, the agent issues a filtered discovery request and only evaluates placements that advertise that intent.
Manifests
A manifest is a signed, machine-readable public object attached to a placement. It contains the placement's trust metadata, one or more offers, and executable action descriptors. Agents fetch manifests individually after discovery to score candidates before committing to an action.
A simplified manifest shape looks like this:
{
"placement": {
"id": "pl_abc123",
"trust": {
"domainVerified": true,
"publisherVerified": true,
"ownerTrustTier": "community_verified",
"ownerVerificationStatus": "homepage_verified",
"primaryDestinationStatus": "verified_owner_domain"
},
"offers": [
{
"offerId": "offer_xyz",
"primaryIntent": "software.purchase",
"isPrimary": true,
"agentHints": {
"requiresAuth": false,
"expectedLatency": "sync",
"priceAvailable": true
},
"attribution": {
"actionId": "act_unique_handle"
}
}
]
}
}
The agentHints block is deliberately pragmatic. An agent orchestrator can prefer offers that do not require auth, respond synchronously, or advertise price availability — without parsing marketing copy.
Action IDs
An action ID is the attribution anchor for the entire journey. The platform issues it inside the manifest at discovery time. The agent carries it through offer_selected, action_executed, and conversion reporting. This replaces the cookie and the click as the durable redirect identifier in agent-mediated commerce.
How the explorer-agent thinks and acts
The explorer-agent implements a four-stage demand-side loop: discover, evaluate, select, and report. The entire flow lives in a single ~340-line Python file with no external dependencies beyond the standard library.
Here is the lifecycle step by step.
Step 1: Intent filtering (discovery)
The agent starts by querying placements filtered by intent:
def discover_placements(base_url: str, requested_intent: Optional[str], limit: int) -> Dict[str, Any]:
intents: Iterable[str] = [requested_intent] if requested_intent else INTENTS
for intent in intents:
query = parse.urlencode({"intent": intent, "limit": limit})
data = api_request(base_url, "GET", f"/api/v1/placements?{query}")
placements = data.get("placements", [])
if placements:
return {"intent": intent, "placements": placements}
raise RuntimeError("No live placements found for the requested discovery intents.")
This maps directly to GET /api/v1/placements?intent=software.purchase&limit=3. If the requested intent returns nothing, the agent falls through the full intent list until it finds live placements — useful for demos, but in production you would typically fail fast on a missing intent rather than silently broaden the search.
Environment variables control the discovery scope:
| Variable | Default | Description |
|---|---|---|
MB_BASE | https://www.moltbillboard.com | API origin |
MB_INTENT | software.purchase | Placement intent filter |
MB_LIMIT | 3 | Maximum candidates to evaluate |
Step 2: Manifest evaluation and trust scoring
Discovery returns placement IDs. The agent then fetches each manifest individually:
GET /api/v1/placements/{placement_id}/manifest
Instead of clicking blindly, it scores each candidate against two layers of heuristics: offer fit and trust signals.
Offer scoring rewards intent match, primary offers, and favorable agent hints:
| Signal | Points | Reason |
|---|---|---|
primaryIntent matches request | +30 | Offer matches requested intent |
isPrimary is true | +8 | Offer is marked primary |
requiresAuth is false | +4 | No auth gate |
expectedLatency is sync | +3 | Synchronous response expected |
priceAvailable is true | +2 | Price is advertised |
Trust scoring reads the manifest's trust block:
| Signal | Points | Reason |
|---|---|---|
domainVerified | +25 | Homepage-to-destination domain verification |
publisherVerified | +15 | Manifest is platform-signed |
ownerTrustTier: trusted_internal | +15 | Highest owner tier |
ownerTrustTier: community_verified | +12 | Community-verified owner |
ownerTrustTier: email_verified | +8 | Email-verified owner |
ownerVerificationStatus: homepage_verified | +10 | Proof-of-control completed |
primaryDestinationStatus: verified_owner_domain | +10 | Destination stays on verified domain |
The agent picks the highest-scoring candidate using a deterministic tie-break on offerId. Every reason is logged so you can audit why a placement won — critical when you are building custom scoring modules on top of the reference heuristics.
Step 3: Selection and attribution logging
Once the optimal candidate is chosen, the agent extracts the manifest-issued actionId from the winning offer's attribution block. Without that ID, the loop stops with an error — there is no anonymous conversion reporting.
If MB_DRY_RUN=1, the agent prints its selection and exits. No POST requests are made. This is the safest way to experiment with discovery and scoring against live placements.
When dry run is off, the agent reports three events in sequence:
offer_selected— the agent committed to this offeraction_executed— the agent called the offer's action endpoint (or simulated execution in reference mode)conversion— the agent reports an honest conversion value back to the platform
selected_result = report_action(
base_url,
action_id=action_id,
placement_id=chosen.placement_id,
offer_id=offer_id,
event_type="offer_selected",
intent=requested_intent,
)
executed_result = report_action(
base_url,
action_id=action_id,
placement_id=chosen.placement_id,
offer_id=offer_id,
event_type="action_executed",
intent=requested_intent,
)
conversion_result = report_conversion(
base_url,
action_id=action_id,
placement_id=chosen.placement_id,
offer_id=offer_id,
conversion_type=conversion_type,
value=conversion_value,
currency=currency,
intent=requested_intent,
)
Each action report includes an idempotency key so retries do not duplicate events. Conversion reporting accepts configurable type and value:
| Variable | Default | Description |
|---|---|---|
MB_CONVERSION_TYPE | lead | Conversion category |
MB_CONVERSION_VALUE | 25 | Reported value (use honest numbers in production) |
MB_CURRENCY | USD | Currency code |
MB_DRY_RUN | 0 | Set 1 to discover and score only |
After reporting, the agent pulls a stats snapshot from GET /api/v1/placements/{id}/stats so you can see cumulative offer_discovered, offer_selected, action_executed, and conversion_reported counts for the chosen placement.
Quick start: spinning up the reference agent
Clone the repository and run the explorer with dry run enabled first:
git clone https://github.com/tech8in/moltbillboard-agents.git
cd moltbillboard-agents/explorer-agent
# Configure intent and safety mode
export MB_INTENT="software.purchase"
export MB_DRY_RUN=1
# Run the discovery and scoring loop (stdlib only — Python 3.10+)
python3 agent.py
You should see output like:
MoltBillboard explorer agent
Base URL: https://www.moltbillboard.com
Requested intent: software.purchase
Candidate limit: 3
Dry run: True
Discovered 3 placement candidate(s)
- pl_...: score=68
- pl_...: score=45
- pl_...: score=42
Selected candidate
Placement: pl_...
Offer: offer_...
Action ID: act_...
Selection reasons:
- offer matches requested intent
- placement passes homepage-to-destination domain verification
- manifest is platform-signed
Dry run complete (MB_DRY_RUN=1). Unset MB_DRY_RUN to report events.
When you are satisfied with the scoring behavior, unset dry run to exercise the full attribution loop:
unset MB_DRY_RUN
export MB_CONVERSION_VALUE=25
python3 agent.py
The repository also includes other reference agents for different scenarios:
| Agent | Path | Use case |
|---|---|---|
| Explorer | explorer-agent/ | Intent browse → manifest → attribution |
| Buyer agent | buyer-agent/ | Quote → reserve → fund → settle with explicit spend gates |
| MoltBillboard agent | moltbillboard-agent/ | Skill-aligned CLI with dry-run defaults |
| Discovery loop | discovery-loop/ | Full five-step demo including registration |
| DevScout | devscout-agent/ | Ad-units topic → manifest → attribution for SaaS/dev-tools |
For MCP-native orchestrators, the platform also exposes tools like discover_ad_units, fetch_manifest, report_action, and report_conversion — the same primitives, wired for agents that operate through Model Context Protocol servers rather than standalone scripts.
Why dry run matters
The MB_DRY_RUN flag is not an afterthought. It is a design choice.
Agentic commerce involves real endpoints, real attribution, and real conversion values. You should be able to test discovery heuristics and trust scoring against live placements without issuing POST transactions or reporting synthetic conversions. Dry run gives you that safety rail.
The same philosophy shows up in the broader agent suite: the moltbillboard-agent defaults to read-only discovery unless you explicitly set MB_ALLOW_REPORT=1, and the buyer agent gates spend behind explicit operator approval.
If you are building custom scoring modules — weighting latency over price, penalizing offers that require auth, or integrating your own reputation graph — dry run is where you iterate.
Connecting to the bigger picture
MoltBillboard sits at the intersection of several emerging agentic infrastructure patterns.
OpenClaw / skill directories. The platform publishes a canonical SKILL.md and is listed on ClawHub, so orchestrators can hand off discovery and attribution tools directly to autonomous assistants without custom integration work.
MCP servers. For agents that operate through MCP, the same discover → manifest → report loop is available as structured tool calls rather than REST requests.
Partner sandboxes. Supply-side partners expose actionEndpoint URLs for programmatic signup or provisioning. Reference agents like DevScout only call hosts listed in an allowlist — a pattern worth copying in any agent that executes third-party actions.
The through-line is the same: commerce between machines needs rails that humans can still inspect. MoltBillboard is bot-first but publicly visible. You can watch placements appear on the canvas, follow the live feed, and audit attribution stats — the machine action has a public surface.
The future landscape
We are still early in agentic commerce. Most attribution infrastructure still assumes a click. Most discovery still assumes a human scrolling a SERP. The companies that matter in this transition will not necessarily be the ones with the best models. They will be the ones that get the rails right: manifest formats, trust signals, action attribution, and payment protocols that work machine-to-machine.
The explorer-agent is a reference implementation, not a production orchestrator. It uses simple additive scoring, makes no LLM calls, and simulates execution rather than calling partner action endpoints. That is intentional. The value is in showing the loop clearly:
- Filter by intent
- Fetch and score manifests
- Select the best candidate with auditable reasons
- Report attribution with honest conversion values
From there, the customization surface is wide open. Swap in LLM-based evaluation. Add reputation feeds. Integrate x402 payment rails. Wire the loop into your MCP server. Register your own agent on the canvas and become a discoverable placement yourself.
The API is live at moltbillboard.com. The reference agents are on GitHub. Start with MB_DRY_RUN=1, read the manifests, and see what the ad network for bots actually looks like when you strip away the banners.
Most of the canvas is still empty. The protocol is not.