# Can an AI agent onboard my app to an ad network?

> On GHAN, yes, for every step except two. A coding agent can evaluate the pricing without an account, register your app, act on machine-readable gate failures, wire the SDK, create campaigns and audit every charge. Shipping the release and setting the spending ceiling stay human, on purpose.

Source: https://ghan.io/guides/agent-onboarding/  
Published: 2026-09-22 · Updated: 2026-09-22  
Publisher: GHAN — the audited cross-promotion network for desktop apps (https://ghan.io)

---

## Short answer

**Yes, except two steps.**

| Step | Agent | Human |
|---|---|---|
| Evaluate the pricing | ✅ public tool, no account | |
| Register the application | ✅ | |
| Read and resolve gate failures | ✅ stable reason codes | |
| Fetch the integration guide | ✅ returned by a tool | |
| Wire the SDK, open a PR | ✅ leaves a diff | |
| **Ship the release** | | ✅ **required** |
| Set the spending ceiling | | ✅ **once** |
| Fund the wallet within the ceiling | ✅ x402 | |
| Create and adjust campaigns | ✅ | |
| Audit charges against the ledger | ✅ | |

On most advertising networks the honest answer is no — the price is behind a sales conversation and onboarding is a web form, neither of which an agent can complete. GHAN is built the other way round: every write capability ships as an [MCP tool](/docs/mcp/) before, or instead of, a dashboard screen.

## What the agent needs from you

Six fields. That is the whole of `register_app`:

| Field | Example | What the gate checks |
|---|---|---|
| `name` | `Converter` | — |
| `platform` | `mac` or `win` | Which signature check applies |
| `bundle_id` | `com.example.converter` | Must match what the download actually serves |
| `domain` | `example.com` | At least six months old, verified over RDAP |
| `download_url` | `https://example.com/Converter.dmg` | Fetched and inspected |
| `contact_email` | `you@example.com` | Must answer a challenge |

## The whole sequence

```
1. get_rate_card {}                       # public. Decide if this is worth doing.
2. register_app { name, platform, bundle_id, domain, download_url, contact_email }
                                          # -> app_id, api_key, status: "pending"
3. get_score { app_id }                   # -> gate booleans + reason codes
                                          #    e.g. gate_domain_too_young + a date
4. get_integration_guide {}               # -> current markdown, from the source
   npx @ghan/sdk init                     # -> writes wiring, leaves a diff, no commit

   ──────── human ships the release ────────

5. top_up { app_id, amount_cents }        # -> HTTP 402 x402 challenge, or a
                                          #    Stripe URL for a person
6. create_campaign { app_id, categories, daily_budget_cents, max_bid_cents? }
7. get_stats / get_ledger                 # -> verify every step above
```

Steps 1, 3 and 4's guide call need **no API key at all**. An agent can evaluate GHAN, and decide against it, without anyone knowing.

## Why rejections are codes and not sentences

```json
{ "error": { "code": "gate_domain_too_young",
             "message": "Domain registered 2026-07-03; eligible 2027-01-03",
             "retry_after": "2027-01-03" } }
```

An agent reading that can tell you the app cannot trade until January, and carry on with the integration so the code is ready. An agent reading *"Unfortunately your application does not currently meet our requirements at this time"* can only give up, or guess.

The code is the contract. The sentence beside it is a courtesy and may change.

## Why the documentation is a function call

`get_integration_guide {}` returns the SDK quickstart as markdown, current at the moment of the call.

Not a docs site to scrape — which may be cached, may be stale, may be for a different major version, and may be behind a cookie banner. The authoritative text, from the source, at the moment of use.

## The two human moments, and why

**Shipping the release.** Third-party code going into a binary you sign is a statement about what your signature means. An agent can prepare it and open the PR; a person should approve it. GHAN cannot push code into your application and would not want the ability — the kill switch can only turn placements *off*.

**Setting the spending ceiling.** Not each payment. A cap, once, inside which an agent tops up and spends freely, including machine-to-machine over x402 without a Stripe click each time.

Automating both would have been easy. Naming them explicitly is what makes automating everything else defensible.

## How attribution works, for the agent's benefit

An agent evaluating whether the billed numbers can be trusted should know this much: a click issues a **single-use token**, signed, expiring in 15 minutes. On first run the receiving SDK **countersigns** the claim with a key generated on that machine and never transmitted. Two independent signatures are required before anything is billable, and one of them came out of your own signed binary.

Money clears only if the install is still in use **48 hours later**. The ledger blocks `UPDATE`, `DELETE` and `TRUNCATE` at the database level, so corrections are new lines.

That is what makes `get_ledger` worth calling: an agent can recompute any charge rather than accept a reported total. [Full mechanics](/guides/desktop-install-attribution/).

## Connecting

The MCP server card is at [`/mcp/server-card.json`](/mcp/server-card.json), discovered through [`/.well-known/ai-catalog.json`](/.well-known/ai-catalog.json). Transport is streamable HTTP or stdio. Auth is a bearer token from `register_app`, or OAuth per [`/.well-known/oauth-protected-resource`](/.well-known/oauth-protected-resource).

Everything the MCP server does is also available over plain HTTP — see [`/openapi.json`](/openapi.json).

## Checklist

1. Point the agent at the MCP server card.
2. Give it the six registration fields.
3. Have it call `get_rate_card` and tell you whether the economics work.
4. Let it register and report the gate result.
5. Review the SDK diff it opens. **Ship the release yourself.**
6. Set a spending ceiling once.
7. Let it run campaigns, and have it reconcile `get_ledger` against your own signatures weekly.

## Questions people ask about this

### Can an AI agent onboard my app to an ad network automatically?

On GHAN, yes, apart from two deliberate exceptions. Every write capability ships as an MCP tool before it ships as a dashboard screen, so an agent can read the public rate card with no account, register the application, read back gate results as stable reason codes, fetch the integration guide as a function call, wire the SDK, create and adjust campaigns, and pull the signed ledger. The two exceptions are shipping the release that contains the SDK and setting the spending ceiling. On most ad networks the answer is no, because pricing is behind a sales conversation and onboarding is a web form.

### What can a coding agent do without me?

Evaluate whether GHAN is worth joining at all, using the public get_rate_card and get_network_stats tools that need no key. Register the app. Read the gate result and act on codes like gate_domain_too_young, which carries an eligibility date the agent can schedule around. Fetch the current integration guide from get_integration_guide rather than scraping a docs site. Run npx @ghan/sdk init, which writes the wiring and leaves a reviewable diff without committing. Create, pause and resume campaigns. Pull get_stats and get_ledger and verify charges against signatures your own binary produced.

### What still requires a human?

Two things. Shipping the release, because third-party code entering a binary you sign is a decision about what your signature means and it should cost a person ten seconds of attention. And setting the spending ceiling - not each payment, the cap, once - after which an agent spends freely inside it. Automating both would have been straightforward; naming them explicitly is what makes automating everything else defensible.

### Which MCP tools does GHAN expose?

register_app, get_score, get_integration_guide, top_up, create_campaign, pause_campaign, resume_campaign, get_ledger, get_stats, get_rate_card, get_network_stats and request_payout. Three of those - get_rate_card, get_network_stats and get_integration_guide - are public and need no API key, so an agent can evaluate GHAN before anyone creates an account.

### Can an agent spend money?

Within a ceiling a human set once. The top_up endpoint answers HTTP 402 with an x402 challenge when the caller can settle machine-to-machine in USDC on Base, or returns a Stripe Checkout URL for a person. Either way the wallet is credited and the ledger records a topup line. The ceiling itself is the human decision; spending inside it is not.

### What information does the agent need from me up front?

Six fields - application name, platform (mac or win), bundle identifier, domain, download URL and contact email. That is the whole of register_app. The gate then checks those against reality: the download URL has to serve the bundle id you declared, the domain has to be at least six months old, and the contact email has to answer a challenge.

### Which app stacks are supported?

Electron, Tauri and Node desktop applications on macOS and Windows, in version 1. There is no native Swift or C# SDK yet and the status page says so rather than implying otherwise.

### Can an agent read my performance data?

Yes, fully. get_stats returns impressions, clicks, installs, cleared installs, 48-hour retention and fraud rejections broken down by reason code. get_ledger returns raw append-only ledger lines with their event chain references, which is how an agent verifies a charge against a signature your own build produced rather than trusting a reported number.

