> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How certification works

> How Apostra certifies a sales agent on live traffic, use case by use case: what makes an agent ready, what each check judges, how long it takes, and what every result and skip reason means.

Certification is how Apostra decides that a sales agent can take buys. Apostra
places a small test buy with your agent for each use case the agent declares,
lets it serve through your real ad server, and judges the result from Apostra's
own tracker and from the reports your agent sends. An agent is certified for a
use case when its test buy passes, and it stays certified while live traffic
for that use case keeps passing.

Certification is per use case. An agent that has passed display can take
display buys while video is still waiting for its result. Apostra does not wait
for everything to be perfect before it runs anything.

<Note>
  Saying your agent is ready from the Agent page is rolling out and is not
  available to every account yet; `save_agent` does it today. Once you say it,
  Apostra starts certification on its own. If no run starts for your agent
  within a day and the Agent page shows no setup step to fix, ask Apostra
  support. The rest of this page describes how certification works once a run
  starts.
</Note>

## What a pass unlocks

One passed use case completes the **Successful transaction** go-live step for
every Source that uses your agent. That is the step a seller's Storefront
needs before it can go live with a third-party sales agent. See
[Storefront readiness](/v2/storefront/tasks/get-readiness).

* A pass counts for the agent a Source was using when the run happened. It does
  not follow a Source that is later moved to a different agent.
* A pass stands until a later result for the same use case and product family
  fails. An inconclusive result never removes a pass.
* Certification does not publish your agent, grant Partner Program status or
  replace a seller's credentials, account mapping and health checks. Those stay
  separate.

## How a developer starts

Every agent lives in your organization's Developer account. See
[Create an agent in a Developer account](/v3/developer-agents).

Certification buys go through a seller Source that uses your agent, so an agent
needs a Source before it can be certified.

* If your organization runs its own storefront, connect the agent to it as a
  Source. See [Build a sales agent](/v2/storefront/inventory-sources/build-sales-agent).
* If you build an agent for other companies to use, ask Apostra support to
  start. Apostra arranges the connection today, and your agent is certified
  through the seller Source that uses it.

Then tell Apostra the agent is ready for certification.

1. Set `certificationRequested` on the agent with V3 `save_agent`, or say it
   from the Agent page (rolling out, see above). Only an administrator of the
   Developer account that owns the agent can set it, and only a sales agent can
   be requested. Set it back to `false` to stop new runs; buys already running
   continue.
2. Apostra starts a run on its own, with no one at Apostra acting. It runs on
   each active Source that uses your agent, usually within the hour. It checks the
   agent-wide baseline, then runs a certification buy for each use case the
   agent declares, on each matching product family.
3. Results appear on the Agent page under **Live certification**, and in the V3
   `get` read of the agent. Each use case shows **Passed**, **Failed** or
   **Inconclusive**, when it was last judged and a reason when it did not pass.

## What makes an agent ready

An agent is ready for certification when two things are true. The agent-wide
baseline holds, and the agent has a sandbox account that serves through your ad server.

### The agent-wide baseline

Apostra reads these from your `get_adcp_capabilities` response and the products
your agent returns. They apply to the whole agent, not to one use case.

* **AdCP 3.** `adcp.major_versions` includes 3, and responses validate against
  the version Apostra negotiates.
* **A billing model Apostra supports.** `supported_billing` includes `agent` or
  `operator`, or both.
* **A creative kind Apostra supplies.** At least one product accepts
  `display_tag`, `video_vast` or `audio_daast`, or declares legacy `format_ids`.

[Sales Agent requirements and best practices](/v2/storefront/inventory-sources/sales-agent-requirements)
covers the baseline and the seven-day behaviour thresholds in full.

### A sandbox account that serves

Before each buy, Apostra calls your `sync_accounts` with its certification test
identity: for example brand `acmeoutdoor.example`, operator
`pinnacle-agency.example` and `sandbox: true`. Apostra asks for a billing model
your agent offers: `agent` first, then `operator`, then `advertiser`.

Your agent is ready when:

1. `sync_accounts` accepts that identity and returns the account as
   `status: "active"` with `sandbox: true` and an `account_id`. Apostra uses
   that `account_id` for the creative and the buy, and never falls back to a
   default account.
2. Buys on that sandbox account serve through your real ad server, at no cost to
   Apostra. The sandbox account that serves through your ad server is the test line. You do not
   set up a separate test placement or name a trafficking contact.

A sandbox that only answers the protocol cannot certify a tracked use case. AdCP
says a sandbox account performs no real platform calls, so if yours stops there,
nothing serves and Apostra's tracker sees nothing. The buy goes live, but the
tracked checks never get evidence, so the use case stays inconclusive.

Sellers that set `account.require_operator_auth: true` cannot be certified yet.
They are skipped as `seller_operator_auth_unsupported`.

Channels Apostra's tracker cannot observe, such as linear TV, radio and print,
certify on your reports instead. See [Use cases](/v3/certification#use-cases).

## Use cases

Apostra reads your products and capabilities to decide which use cases apply.
You do not maintain the list. A use case your agent does not declare does not
apply, and one it declares but has not passed cannot take buys yet.

| Use case | Applies when your agent declares | Judged on |
| - | - | - |
| Display with our tag | Display or DOOH products that accept `display_tag`, or that accept `image` or `html5` in a format with a URL impression-tracker slot | The standard checks |
| VAST video or audio with our trackers | Video, audio or CTV products that accept `video_vast` or `audio_daast` | The standard checks, plus quartiles |
| Cost-per-completed-view video | A video or CTV product with CPCV pricing or a `completed_views` optimization goal | The standard checks, plus quartiles |
| CPA and conversion tracking | Conversion tracking on a product and in `media_buy.conversion_tracking` | The standard checks |
| Audience or signal targeting | `signal_targeting_allowed` on a product, or `media_buy.audience_targeting` | The standard checks |
| TMP | A product with `trusted_match`, on a channel where the tracker fires | The standard checks, with `TMPX` also judged |
| Seller-reported sandbox buy | Products on linear TV, radio, print, cinema, out-of-home, influencer or product placement, or only legacy `format_ids` that Apostra cannot track | Buy goes live and reports on time |

The extra checks the spec lists for cost per completed view, conversions,
signals and TMP are being added. Today a run for those use cases is judged on
the standard checks.

A use case is also split by product family. Display on one ad server and display
on another are two families, and each needs its own certification buy.
Programmatic and non-programmatic DOOH are always separate families.

## What each check judges

| Check | What Apostra observes | Applies when |
| - | - | - |
| The buy goes live | Your agent accepts the buy, reports it `active` or `completed`, and starts serving | Always |
| Macros are replaced | Impression hits carry real values where our macros were. `CACHEBUSTER`, `DEVICE_TYPE`, `COUNTRY` and `MEDIA_BUY_VERSION_HASH` must fill on every hit. Display and video hits must fill `DOMAIN` or `APP_BUNDLE`. Consent-dependent macros such as `DEVICE_ID` and `IP_ADDRESS` may arrive empty but never literal | Our tracker can fire |
| Clicks fire | At least one click reaches our click tracker with its macros filled. It fails on 1,000 impressions with no click | Display and video formats that take clicks |
| Reports arrive on time | A report arrives for every period by the time the product promises. More than 60 minutes late counts as a miss. One miss is a warning and two in seven days fail the use case. A `delayed` notice buys one 24-hour extension for that period | Always |
| Reports agree with our tracker | Reported impressions match our tracker within 20% over the flight. Below 1,000 tracker impressions the result is inconclusive and does not block | Our tracker can fire |
| Ad-server delivery | Our tracker's impressions match your ad server's pre-filter count within 20%, once the tracker has 100 impressions. Your agent may report the pre-filter count as `unfiltered_impressions` from Google Ad Manager. Without it the check is inconclusive and does not block | Our tracker can fire |
| Quartiles fire | Start, first quartile, midpoint, third quartile and complete arrive in order, and your `quartile_data` and `completed_views` agree with them | Video and audio |

Stopping and change checks (a paused buy goes quiet, a creative swap takes
effect) are not part of the first buy. Apostra runs them later to keep
certification current.

Seller-reported channels skip the tracker checks. They pass on the buy going
live and on reports arriving on the schedule your agent declares in
`reporting_capabilities`.

## Test buy size

Each certification buy is capped at USD 10, and each run is capped at USD 50.
A buy's budget is the smallest USD pricing option on the product: a flat-rate
option uses its fixed price, any other option uses its minimum spend per
package, and an option with no minimum uses the USD 10 cap. Apostra never rounds
a minimum down. A product whose smallest option is above USD 10 is skipped as
`budget_exceeds_per_buy_cap`.

The buy runs for three days and asks for about 15 impressions an hour, which
gives roughly 1,000 impressions to compare and three daily reports. Your sandbox
decides whether money moves. Apostra does not pay for test impressions.

## When Apostra starts a run

Once `certificationRequested` is `true`, Apostra starts a run on each active
Source that uses your agent, using Apostra's own certification buyer and
creatives. Each Source gets one run at a time, and every run carries the reason
it started.

| Reason | When it applies |
| - | - |
| `certification_requested` | You said the agent is ready and the Source has no passed or failed result since. Apostra also starts again for this reason when an earlier run ended with nothing to judge |
| `evidence_expiring` | The Source's latest passed or failed result is within four days of leaving the seven-day evidence window, and the previous run has finished |

Apostra does not start a run beside one that is still collecting evidence. A
run counts as collecting for 14 days after its buy was planned, so a Source is
re-certified about every two weeks while it stays requested.

A Source whose setup is incomplete gets no run and no test buy. The most
common cause is a sandbox account that your agent does not return; see
[Your sandbox account](/v3/certification#your-sandbox-account) for what to change. Apostra checks
that Source again after 24 hours, and each day after until it works, so a fix
is picked up within a day. Showing the blocked setup step on the Agent page is
rolling out. A run that ends with nothing to judge, for example because every
family was skipped, is also retried no sooner than 24 hours later, never every
hour.

Apostra does not yet start a run when your agent's products or capabilities
change. After a change, the next run starts when the evidence expires, or when
Apostra support starts one.

## How long it takes

Plan for about three days of serving, plus the reports that are due after the
flight. Apostra re-reads the evidence every hour, so tracker hits and reports
that arrive late update the result without anyone starting a new run. Apostra
compares totals against the settled report about 72 hours after each period,
so the final comparison can arrive after the flight ends.

## Results

Each use case and product family has one latest result.

| Result | What it means | What to do |
| - | - | - |
| Passed | Every check that applies passed on live traffic | Nothing. It stays valid while evidence stays fresh |
| Failed | A check failed, for example macros arrived literal, two report periods were missed, or the counts disagreed beyond 20% | Fix the cause named in the reason. The use case recovers when live traffic passes again |
| Inconclusive | No check failed, but Apostra could not finish judging. Either evidence is still arriving, or the family was skipped before a buy | Wait for the next hourly update. If the reason is a skip reason, see below |

A skipped family never produced a buy. It is shown as inconclusive with its skip
reason. It does not fail the use case, stops no running buys and cannot certify.
A later run judges the family again.

### Skip reasons

Skip reasons describe what stopped the buy before it was sent.

#### Your sandbox account

| Skip reason | What happened | What to change |
| - | - | - |
| `seller_sandbox_account_pending` | Your agent held the account for approval (`pending_approval`) or accepted `sync_accounts` as a task that has not finished | Approve the account or finish the task. Apostra checks again on the next run |
| `seller_sandbox_account_failed` | Your agent rejected the account, returned one that is not active or not a sandbox, returned no `account_id`, did not answer in time, or does not offer `sync_accounts` | Accept the test identity and return an active sandbox account with an `account_id` |
| `seller_operator_auth_unsupported` | Your agent declares `account.require_operator_auth: true` | Certification cannot run for these agents yet |
| `sandbox_brand_unverified` | The certification advertiser has no linked brand | Nothing on your side. Apostra fixes its test advertiser |
| `sandbox_buyer_account_unavailable` | Apostra could not finish looking up its test buyer account in time | Nothing on your side. The next run tries again |
| `sandbox_unverified` | Apostra could not verify the sandbox account for another reason | Ask Apostra support |

#### Your agent's response to the buy

| Skip reason | What happened | What to change |
| - | - | - |
| `seller_creative_review_required` | Your agent held the certification creative for review | Let your agent accept certification creatives without a manual review hold. Apostra skips only its own creative review, so your agent and ad server still apply theirs |
| `creative_sync_failed` | The certification creative could not be synced to your agent, or your agent rejected it. The result carries your agent's own message when it sent one | Accept the creative, or fix the error your agent returned. If the message names missing macros, that is Apostra's creative and not yours |
| `seller_format_cannot_carry_tracker` | The ad format Apostra chose for the product has no tracker slot and no VAST or DAAST wrapper path for a buyer impression tracker | Add a URL impression-tracker slot named `impression`, `tracker_pixel` or `imp_pixel`. For hosted video or audio, add a `pixel_tracker` slot |
| `seller_approval_required` | Platform safety review held the buy, or the approval check could not run. Results from earlier runs can show the same reason as `approval_required` | Ask Apostra support. Nothing to change in your agent |
| `seller_<reason>` | Your agent refused the buy. The text after `seller_` is the refusal reason, for example `seller_all_sources_failed`. The result also carries your agent's error code and message when it sent them | Read your agent's refusal and fix it |
| `buy_<code>` | Apostra's buy was rejected with a validation error. `<code>` is the error code in lower case, for example `buy_validation_error` | Read the message in the result, or ask Apostra support |

#### Budget

| Skip reason | What happened | What to change |
| - | - | - |
| `budget_exceeds_per_buy_cap` | The smallest USD pricing option needs more than USD 10 | Offer a pricing option at or below USD 10 on a product Apostra can buy |
| `budget_currency_unsupported` | No pricing option with an id is in USD | Offer a USD pricing option |
| `seller_no_pricing_option` | The product has no pricing option with a `pricing_option_id` | Give each pricing option an id |
| `budget_exceeds_run_cap` | Adding this family would take the run past USD 50 | Nothing. A later run covers it |
| `run_budget_exhausted` | Earlier buys in the run used the USD 50 | Nothing. A later run covers it |

#### Agents that sell only through briefs

An agent that lists no wholesale products and declares
`media_buy.supports_proposals: true` is certified through a brief. Apostra sends
one brief for display with a tag and one for VAST video, each with a USD 10
budget and a three-day flight, and buys the first proposal that serves the use
case.

| Skip reason | What happened | What to change |
| - | - | - |
| `buy_no_matching_proposal` | Your answer to the brief held no product that serves the use case | Return a product with the channel, creative kind and tracker path the brief asked for |
| `buy_brief_unavailable` | Apostra could not get an answer: your agent timed out, returned an error or answered incompletely | Answer briefs within 45 seconds with a complete response |

#### Apostra's side

| Skip reason | What happened | What to change |
| - | - | - |
| `creative_sync_unsupported` | Apostra offered no certification creative for a tracked family | Nothing on your side. Apostra support supplies one |
| `buy_creative_channel_unmatched` | None of the creatives Apostra offered fits the family's channel or format | Nothing on your side. Apostra support supplies a fitting creative |
| `adapter_storefront_unsupported` | The Source sits behind a managed ad-server Storefront, which does not take trusted sandbox buys | Nothing. This does not apply to third-party sales agents |
| `seller_storefront_unavailable` | The Storefront behind the Source is unavailable | Ask Apostra support |

A product with no declared AdCP channels is skipped before any family is built
(`missing_channels`). That is a finding with the products named, not a failure.
Declare `channels` on the product, or `applies_to_channels` on each format
option.

## How certification is kept

Certification is continuous, and evidence has to stay fresh.

* **Fresh evidence.** Most checks need a pass from the past seven days. The
  report check follows your reporting cadence instead, so a product that reports
  monthly stays certified between reports.
* **Real buys count.** A real buy for a use case produces the same observations
  as a certification buy. While one is live, its traffic is the evidence. When
  none is, Apostra runs a synthetic buy on your sandbox account.
* **A failure blocks new buys only.** When a check fails on live traffic, the use
  case cannot take new buys, and Apostra tells you what it saw and how to fix
  it. Buys already running are never paused automatically. The use case recovers
  as soon as live traffic passes again, with no re-registration step. The step
  that keeps new buys away from a failed use case is rolling out.

Certification buys and creatives skip the Storefront's own approval queues. Your
agent and ad server still review them, and Apostra's platform safety review
still holds a buy it flags.

## Related

* [Sales Agent requirements and best practices](/v2/storefront/inventory-sources/sales-agent-requirements)
* [Create an agent in a Developer account](/v3/developer-agents)
* [Storefront readiness](/v2/storefront/tasks/get-readiness)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.