> ## 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.

# Is your ad server in line with the agreed terms?

> What in line, out of line and not comparable mean on a media buy, which fields are compared, why some are not, and what to do about a difference.

After each read of your ad server, Apostra compares what it holds for a media
buy with the terms you and the buyer agreed. The result is on the buy, per
source, so you can see when your ad server has drifted from what was sold: a
budget raised in your ad server, a flight date moved, targeting changed, or a
rate edited.

Apostra only reports the difference. It never changes your ad server to match
the agreed terms, because the change may be intentional. You decide whether to
put the line back or to agree the change with the buyer.

> [!NOTE]
>
> This check is in beta, and it is available to agents and the API for now: `search`
> and `get` with `kind: "media_buy"`, and the data behind the Media Buys list. The
> Media Buys page does not display it yet. If your buys carry no
> `contractConformance` field, your storefront is not on the beta.

## What "in line with the agreed terms" means

The agreed terms are what Apostra last sent your source for the buy and your
source accepted. An accepted update to the buy replaces them. The ad server's
own state is what your source reports from it.

A buy is in line when every agreed field was compared and every one matches.

## The three statuses

Each routed source on a buy has one of these, and the buy shows the combined
result.

| Status | `status` value | Meaning |
| - | - | - |
| In line | `in_line` | Every agreed field was compared, and every one matches. |
| Out of line | `out_of_line` | At least one compared field differs from the agreed terms. Each difference is listed. |
| Not comparable | `null` | Apostra could not say either way. Nothing has been compared since the terms were accepted, or nothing differs but at least one agreed field could not be compared. |

Not comparable is never shown as in line. A source that reports less never
looks more in line than one that reports everything. A buy is out of line as
soon as one source is. It is in line only when every source is.

A buy you booked directly in your own ad server, without Apostra, has no agreed
terms on Apostra's side. It reads not comparable and is never flagged.

## What each difference tells you

Every open difference on an out-of-line source carries:

| Field | Meaning |
| - | - |
| `field` | The kind of field: `budget`, `pricing` (the rate), `flight` or `targeting`. |
| `fieldPath` | The AdCP path that differs, for example `packages[pkg_1].budget`. |
| `agreed` | What was agreed. |
| `reported` | What your ad server reports now. |
| `valuesTruncated` | `true` when `agreed` or `reported` was too large to include (over 1,000 characters of JSON). The large value is `null` and the other side is unchanged. |
| `firstSeenAt` | When a read of your ad server first showed the difference. |
| `financial` | `true` for budget, rate and flight differences, which can change what is billed or what delivers. |
| `note` | Why the values are called different when equality could not be proven. |

Values keep the shape AdCP gives them: amounts as numbers in the buy's currency,
times as ISO 8601, and targeting as an AdCP targeting overlay.

A difference closes when a later read of your ad server no longer shows it. That
happens when you put the line back, and also when you agree the change with the
buyer and the update is accepted.

## Which fields are compared

| Compared | How |
| - | - |
| Total and daily budget | With the currency, to within one goal unit. See below. |
| Rate and pricing option | The rate is compared to the micro. |
| Flight start and end | To the minute. A start that has already passed is not compared, because your ad server sets "start now" itself. |
| Targeting | Each targeting dimension the buy carries: geography, audiences, devices and frequency caps. |

Your seller-side fulfilment is yours to change, so it is not compared: the ad
units and placements that serve a product, exclusions, line-item priority and
creative rotation. A buy's lifecycle status follows your ad server.

## Why some fields are not compared

A field that cannot be compared is listed in `notCompared` with a `fieldPath`
and a `reason`. It keeps the buy from reading in line.

| Reason | What happened |
| - | - |
| `reported_as_booked` | Your source gives back the value that was booked instead of reading it from your ad server. Matching our own booking proves nothing, so the field is not compared. Frequency caps are always reported as booked for Google Ad Manager. |
| `not_declared_read` | Your source did not say it read the field from your ad server. |
| `not_reported` | Your source did not report the field, or did not return the package at all. |
| `package_not_matched` | An agreed package could not be matched to a package your source reports. |
| `contract_terms_not_held` | The agreed terms for the package are not held yet. An update forwards only the packages it changes, so a package that has not been in an update can be missing its terms. |

Creative assignments are not compared yet.

Two rules make the comparison fair rather than strict:

* **Geography is compared by the area it reaches.** Google Ad Manager can store
  a booking of "United States plus these metro areas" as the metro areas alone.
  That reaches the same people, so it is in line. It is not a missing country.
* **Google Ad Manager rounds budgets.** It holds a budget as a whole number of
  goal units priced at the line item's rate. A budget is compared to within one
  goal unit, which costs a thousandth of the rate on a CPM or vCPM and the whole
  rate on a CPC, CPV or CPCV.

## What to do about an out-of-line buy

1. **Check what changed.** Read `agreed` and `reported` for each difference,
   money first. Your ad server's change history shows who made it.
2. **If the change was not intended,** put the line back in your ad server. The
   next read of your ad server closes the difference.
3. **If the change was intended,** agree it with the buyer. When the buyer's
   update is accepted, it becomes the agreed terms and the difference closes.
4. **If a field is only not comparable,** nothing is wrong. Ask your source to
   report it from your ad server if you want it checked.

Billing follows the agreed terms, not your ad server. A rate raised in your ad
server does not raise what the buyer pays. A budget raised there can deliver
more than the buyer agreed to pay for, so settle it with the buyer.

An out-of-line buy gets an attention flag in the Media Buys list data, and
ranks, within the page of buys returned, above buys that are only waiting on you.
A buy with a budget, rate or flight difference ranks first. The flag is a
warning, not a failure: it asks you to look and does not say the buy has failed.

## Read it from an agent

For a seller on the beta, `search` and `get` with `kind: "media_buy"` return
`contractConformance` on each buy. `search` returns the buy-level status and each
source's status, differences and not-compared fields. `get` returns the same,
beside each leg of the buy's timeline.

```json theme={null}
{
  "contractConformance": {
    "status": "out_of_line",
    "legs": [
      {
        "sourceId": "src_gam",
        "status": "out_of_line",
        "observedAt": "2026-10-17T09:30:00.000Z",
        "differences": [
          {
            "field": "budget",
            "fieldPath": "packages[pkg_1].budget",
            "agreed": 6000,
            "reported": 9000,
            "valuesTruncated": false,
            "firstSeenAt": "2026-10-16T14:00:00.000Z",
            "financial": true,
            "note": null
          }
        ],
        "differencesTotal": 1,
        "notCompared": [
          {
            "fieldPath": "packages[pkg_1].frequency_cap",
            "reason": "reported_as_booked"
          }
        ],
        "notComparedTotal": 1
      }
    ]
  }
}
```

A leg returns its first 25 open differences, money first, and its first 50 fields
that could not be compared. `differencesTotal` and `notComparedTotal` give the
full counts, so a leg with more than that tells you how many were left out.

## Related

* [Manage ads](/v3/manage-ads.md) covers the seller workflows around the Media Buys
  page.
* [Media buy lifecycle](/v2/concepts/media-buy-lifecycle.md) explains the statuses a
  buy moves through, which are separate from whether it is in line with the
  agreed terms.


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