request_account is the one way to write an account. Send a brand and operator to open one: the seller looks for an existing account first, creates one if it should, or tells the buyer what a human has to do before it can. Send an account_id and attributes to change one. Every write is a request with the same lifecycle, read back through list_accounts.
Experimental. Account requests (
request_account, include_requests / request_ids on list_accounts, the account_request.changed webhook, the account.requests capability block, and the request_id, resolution, setup.waiting_on, and setup.kind fields on sync_accounts results) are an experimental AdCP 3.3 surface (#8280, #8543). They may change between 3.x releases with at least 6 weeks’ notice. Sellers implementing any part of it MUST declare account.requests in experimental_features. See experimental status.account/request-account-request.json
Response schema: account/request-account-response.json
Why a request, not an account
Getting an account usually involves a person. When a storefront receives “WPP Media FR for Renault,” someone in sales ops checks the CRM, where the record is named something like “Renault SAS – France,” not keyed on a domain. The review can end four ways: the seller already has a relationship and links it, creates a new account, asks a human on the buyer side to sign up first, or declines.sync_accounts models this as a placeholder account in pending_approval. A placeholder can’t become an existing account without changing its account_id, and a rejected placeholder keeps holding the brand and operator. A request is its own object instead: no account exists until the request resolves, so a match never changes an ID and a rejection never blocks a retry.
When to call
Call it only whenget_adcp_capabilities advertises account.requests.supported: true and lists account.requests in experimental_features. Otherwise, use sync_accounts, which stays stable and available in 3.3. account.requests.changeable_attributes lists the attributes a seller accepts in change requests; absent or empty means it supports opening accounts only.
- Agent-declared sellers (storefronts, inventory sources, house seats):
request_accountis the way to get an account. - Sellers that own their account namespace (platforms where the credential’s access decides which accounts exist):
request_accountis optional. When offered, it matches among accounts the credential already reaches, or returns a buyer action such as creating an ad account or asking the brand to grant access. Sellers that don’t offer it rely onlist_accounts, and the brand-to-account mapping stays on the buyer side.
Lifecycle
in_review and ambiguous are open; resolved and rejected are terminal for that request. A seller that approves automatically resolves in the same response. A seller that reviews starts in_review with waiting_on: seller.
pending_action.kind says what is needed: review, sign_up, accept_terms, credit_application, add_payment, grant, or connect_credentials. The list is extensible; for an unknown kind, fall back to message and url.
Request
Send either
account_id (to change or select), or brand and operator (to open). A request with account_id and operator, or with neither, fails with INVALID_REQUEST.
Changing an account
Send theaccount_id and only the attributes to change. The changeable attributes are those #8474 defines: the brand’s countries, operator_unit, billing, billing_entity, and payment_terms. currency and timezone are set when the account opens, and a seller MAY refuse to change them; sandbox never changes.
- It applies the change:
state: resolved,resolution: updated, andaccountcarrying the updated attributes and newrevision. - It reviews the change first:
state: in_reviewwith a pending action. The account keeps its current attributes until the request resolves. - It refuses the change:
state: rejectedwith the reason. The account is unchanged.
account_id, media buys, and reporting history either way. Changes never alter the account’s identity, so there is nothing to retire.
Who may change an account. The seller first resolves account_id against the caller’s own access and answers an unreachable account exactly as an unknown one, before validating anything else in the body, so a probe learns nothing. A change then requires request_account in the caller’s grant allowed_tasks for that account, with read_only: false. When the grant lists request_account under field_scopes, every changed field, including billing, billing_entity, and payment_terms, must be in that allowlist, as field_scopes already works. Otherwise the request fails with SCOPE_INSUFFICIENT, READ_ONLY_SCOPE, or FIELD_NOT_PERMITTED.
Concurrent changes. A change that waits in_review re-checks revision when it resolves. If the account changed in the meantime, the request is rejected with CONFLICT instead of being applied on top. A new change request for the same account from the same caller supersedes that caller’s open change request: the new request gets its own request_id and must carry every change still wanted, and the open one moves to rejected with CONFLICT and details.superseded_by. Change requests are never edited in place, and another caller’s requests are never affected. Subscription writes (webhook and reporting configuration) don’t advance the account’s revision, so they never fail a pending change.
One account per call. request_account has no batch form. A buyer opening many accounts makes one call per account, which keeps each request’s lifecycle separate.
Response
A completed response always carries the request, whatever its state.in_review and rejected are normal outcomes, not task failures; status: "failed" is reserved for calls the seller couldn’t process.
Deduplication
The protocol requires only that retries are safe. Replaying anidempotency_key returns the original request, and replaying it with a different body returns IDEMPOTENCY_CONFLICT, as on every AdCP mutation. A new key is a new request.
What a seller does with separate requests for the same advertiser is its choice. This guidance is non-normative:
- Per calling agent. Requests are scoped to the authenticated caller. Two agents asking for the same advertiser get two requests, and both may resolve to the same account (
matched), each with its own grant. - Match on brand and operator. Treat a request for the same brand (
domainandbrand_id) andoperatoras the same advertiser.brand.countries,currency,timezone,sandbox, andoperator_unitare attributes of the account: use them as filters that narrow which account fits, not as identity. - Terms are not identity.
billing,billing_entity, andpayment_termsare requested terms. One advertiser maps to one account whoever is invoiced.
Seller rules
- No provisional account IDs. Never issue an
account_idthat might later change; while a request might match an existing account, issue none until it resolves. Open requests are visible throughlist_accountswithinclude_requests, inaccount_requests[], never as accounts. - No existence oracle.
matchedandcandidates[]disclose only accounts the calling agent is, or is now, granted. When the only match is an account another operator or agent holds, every state, pending-action kind, and error the caller sees MUST be one the seller could also return when nothing matches: for example,reviewfollowed bysign_up, a new account, or a rejection with a generic reason. - Check reachability first. Resolve
account_idagainst the caller’s access before any other validation, soINVALID_REQUEST,CONFLICT, orUNSUPPORTED_FEATUREnever reveal that an unreachable account exists. - Never echo write-only fields.
requestednever includesbilling_entitybank details or other write-only fields. - Unreachable
account_ids look like unknown ones. For anaccount_idthe caller can’t reach, answer the same way whether or not the account exists: alwaysrejectedwithACCOUNT_NOT_FOUND, or, for sellers that support access requests, alwaysin_reviewwithwaiting_on: buyerandkind: grant. - Action URLs aren’t bearer links. Possession of
pending_action.url, or of arequest_idin it, MUST NOT by itself be enough to complete the action; the human authenticates with the seller first. - Grant the caller on resolution. A resolved account appears in the caller’s
list_accountswith its caller authorization. - Check authority while reviewing. Verify the operator against the brand’s
authorized_operators, and, where supported, the operator’s grant to the calling agent. A missing grant iswaiting_on: buyerwithkind: grant, not a bare error. - Expire buyer actions. A request still waiting on the buyer at
pending_action.expires_atmoves torejectedwith an error explaining the expiry. - Fire
account_request.changedon every seller-driven transition, after the new state is readable throughlist_accounts, when the capability declaresevent_type. Sellers that don’t fire it leave buyers to poll. - Fold in the agent application. If the calling agent’s own relationship with the seller is not yet active, the seller MAY review it together with its first account request, so the buyer makes one call and the seller one review.
Relation to sync_accounts
sync_accounts stays stable and available in 3.3; its formal deprecation follows account.requests leaving experimental status. A seller offering both keeps one internal request record behind them: sync_accounts provisioning rows are a projection of it. A sync_accounts settings update that lands while a request_account change is in_review advances the account’s revision, so that change fails its revision re-check and is rejected with CONFLICT.
Buyers that predate account requests. They ignore
request_id, resolution, and the new setup fields, and learn the outcome the way they do today: by replaying sync_accounts with the same natural key, from the one-shot push_notification_config, or from account.status_changed. When a request resolves, the seller activates the row’s account-level notification_configs first and then fires account.status_changed from pending_approval with the resolved account_id. The one visible difference is that a request-mode seller’s pending rows don’t appear in list_accounts until they resolve.
Placeholder accounts. A seller never issues an account_id it might later change. While a request is pending, the buyer tracks request_id. A seller that creates the account at approval and keeps the placeholder’s ID, so it never changes, MAY issue it early; any seller that might match the request to an existing account does not.
Buyer handling
- If the seller advertises
account.requests, callrequest_accountto open or change accounts. Otherwise usesync_accounts. - Store
request_idagainst (seller, key). - While the request is open, if the seller declares
account.requests.event_type, register foraccount_request.changedthroughsync_principalorsync_agent_notification_configs, or polllist_accountswithrequest_idsand backoff. - Until it resolves, call discovery tasks without
account, which returns the seller’s public view. - After a cold start,
list_accountswithinclude_requests: truerecovers accounts and open requests. Requests page separately from accounts: followaccount_requests_paginationwithrequests_paginationuntilhas_moreis false.