Skip to main content
This walkthrough follows one buyer agent from nothing to a working account with a seller it has never spoken to. The agent publishes its identity, reads the seller’s onboarding posture, makes first contact with a signed request, and provisions an account. It never runs a capabilities endpoint of its own and never receives a seller-issued credential: the signature is the credential.
Experimental, AdCP 3.3. This flow uses several experimental surfaces:They may change between 3.x releases with at least 6 weeks’ notice. SDK support is in progress, so the examples show the wire, not SDK calls. Design and decisions: #8118, DR-0023, DR-0025.

Scenario

Sam runs buying at Pinnacle Agency. Pinnacle’s buyer agent buys media for Nova Motors and other advertisers. Priya runs ad products at StreamHaus, a streaming publisher whose sales agent Pinnacle has never called. Sam wants the agent to open an operator-billed account with StreamHaus for Nova Motors, today, without a partnerships email thread.

What open onboarding delivers

Self-onboarding automates becoming known. Under an open mode, a buyer agent gets a verified identity, sandbox access, and the ability to join a billing relationship that already exists, such as its operator’s account with the seller. It does not let an unknown agent start spending: agent-billed accounts and new commercial relationships still go through the seller’s application, with a human on each side.

0. Publish your identity

The agent’s identity is its origin, one agent per origin. Three files make it verifiable. The key directory, at the agent’s origin, holds its signing keys. It is a JWK Set; each kid is the key’s RFC 7638 thumbprint, and keys carry no AdCP-specific members:
The response is signed with each key and carries an expires, so publishing it is a scheduled job (regenerate it before it expires), not a one-time upload. The trust record, at the registrable domain, lists the origin and its role. It carries no keys:
served at https://pinnacle-agency.example/.well-known/trust.json. Sellers derive this location from the agent’s origin, so nothing points to it. The revocation list, at the agent’s origin, is where the agent revokes a key. It is served at /.well-known/governance-revocations.json as a JWS of type adcp-gov-revocation+jws, signed with a key from the directory, even while it revokes nothing. Its payload:
The list is re-signed before its next_update, so publishing it is a scheduled job too. The format and polling rules are in Transport revocation. Without it, a seller can verify you but cannot bind you for spend. Grants from the advertisers. Nova Motors grants Pinnacle authority to buy for it, in Nova Motors’ own trust.json:
A grant is published by the party making it, so the seller can verify it without trusting Pinnacle’s word.

1. Find sellers

The agent finds StreamHaus through the AgenticAdvertising.org registry or the publisher’s adagents.json, as today.

2. Read the seller’s onboarding posture

An unsigned get_adcp_capabilities returns StreamHaus’s posture. It is the same for every caller:

3. Check yourself against the requirements

The agent compares each requirement with what it knows about itself:
  • Operator-billed (open, requires grant): Nova Motors’ trust.json grants Pinnacle adcp:media_buying, and Pinnacle already bills with StreamHaus. Usable now.
  • Agent-billed (application): needs a billing entity and a contract. That goes to Sam, with the URL.
  • Unknown requirement kinds would fall back to message and url.
A seller marked invite_only would be dropped from the plan with the reason recorded.

4. First contact

The agent signs a get_principal request with the Web Bot Auth profile. The covered Signature-Agent member names its origin:
StreamHaus fetches the directory at the named origin, finds the key by thumbprint, verifies the signature, and checks the origin’s revocation list. Pinnacle is now identity-established: StreamHaus knows exactly which origin is calling, but has not bound it to anything. get_principal returns:

5. Configure the principal

The agent calls sync_principal with its declarations and webhooks. Because StreamHaus’s posture has an open mode, StreamHaus:
  1. derives Pinnacle’s trust record from the origin (https://pinnacle-agency.example/.well-known/trust.json);
  2. confirms it lists https://buyer.pinnacle-agency.example with role adcp:buying;
  3. creates a principal limited to the open modes.
A seller without an open mode would hold this for its own approval. In that case get_principal shows relationship.status: "pending_approval", with any applicant-specific setup step.

6. Open the account

The agent calls sync_accounts for Nova Motors, operator-billed:
StreamHaus checks authority from Nova Motors’ trust.json. The grant names Pinnacle, covers adcp:media_buying, and account.operator is Pinnacle, the signer’s operator. Pinnacle already has an operator billing relationship with StreamHaus, so the account comes back active. Without the grant, the request would fail with PERMISSION_DENIED and reason: "grant_absent". No account is created, and Sam knows to ask Nova Motors for the grant.

7. Stay current

The agent subscribes to principal.changed. When StreamHaus approves agent billing, suspends the relationship, or the relationship nears its expires_at, the webhook fires (relationship_changed, relationship_expiring), and the agent re-reads get_principal. A lapse arrives as a warning, not as AGENT_SUSPENDED in the middle of a campaign.

Failure branches

If you sign with the RFC 9421 profile today

The RFC 9421 profile (adcp/request-signing/v1, keys through brand.json) is deprecated in 3.3 and works through 3.x. A buyer moving to Web Bot Auth publishes the directory, trust record, and revocation list above and keeps its brand.json keys until the sellers it uses have moved. A seller binds the new origin onto an existing principal only with explicit approval, so arrange that with each seller. See the agent identity migration guide.