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

# Seller buyer instructions

> Save seller notes, a country scope and a discount for one brand and operator pair with save_advertiser_instructions, and read them back.

`save_advertiser_instructions` records what your Seller Account wants applied
to one specific buying operator working for one specific brand: free-form
notes, an optional country scope and an optional discount. It is a Seller
Account tool. A Buyer Account uses `save_advertiser` and its `privateNotes`
field instead; see [Private advertiser notes](/v3/manage-ads#private-advertiser-notes).

<Note>
  In a Seller Account, `advertiser` means a **brand domain and operator domain
  pair** that you wrote instructions for. It is not a registry-verified
  identity, a buyer's own Advertiser, or an ad-server advertiser. Every domain
  you save carries `verified: false` on the way back.
</Note>

## What you can change

One instruction row is identified by its exact pair, `operator.domain` plus
`brand.domain`. You can write three things on it:

| Field | What it holds |
| - | - |
| `instructions.notes` | Free text for this pair, up to 2,000 characters. `null` clears it. |
| `instructions.countries` | A list of unique uppercase two-letter country codes the row applies to. `null` removes the scope, so the row applies in every country. |
| `discountPercent` | A number from 0 to 100, or `null` to clear it. This is the discount your row contributes. The discount that finally applies is read-only and appears when you read the row. |

The pair itself is fixed once the row exists. To target a different operator or
brand, save a new row.

<Warning>
  This tool writes only the exact-pair row. Rows that name only a brand, only an
  operator, or neither are broader selling policy, and they belong to the
  playbook and its house-discount rules. See
  [Listing, Playbook and Business Rules](/v2/setup/seller-pages#discounts-are-per-rule-not-a-whole-list).
  Reading a row like that through `get` returns `NON_EXACT_ADVERTISER` and
  names `save_playbook` as the next step.
</Warning>

## Create an instruction

Send both domains and a non-null `instructions.notes` or `discountPercent`.
A create without either, including one that carries only a country scope, is
refused with `VALIDATION_FAILED` and lists the missing fields.

```json theme={null}
{
  "operator": { "domain": "agency.example" },
  "brand": { "domain": "brand.example" },
  "instructions": {
    "notes": "Always lead with the sports bundle. Net-30 only.",
    "countries": ["US", "CA"]
  },
  "discountPercent": 8
}
```

The result reports `action: "created"` with the saved projection as `object`.
If the write lands but the read-back is unavailable, the result reports
`action: "created"` with `warning: "created_but_readback_unavailable"`, the row
`id` when known, and the `pair`. Read the row with `get` before you save again.

## Change an instruction

Address the row by `id`, or by sending the same two domains again. Send only
the fields you want to change. Anything you omit keeps its stored value.

```json theme={null}
{
  "id": "INSTRUCTION_ID",
  "instructions": { "notes": "Lead with the CTV bundle instead." }
}
```

* A change that matches what is already stored returns `action: "unchanged"`
  with the current projection and writes nothing.
* A change returns `action: "updated"`. As with a create, a successful write
  whose read-back is unavailable returns `updated_but_readback_unavailable`.
* If you send an `id` and also domains that do not match that row, the call is
  refused with `PAIR_MISMATCH`, naming the requested and the stored pair. Omit
  `id` to create the pair you named, or correct the domains.
* If two stored rows match the same pair, the call is refused with
  `AMBIGUOUS_MATCH` and the matching `ids`. Send the `id` of the one you mean.
* An `id` that does not exist returns `NOT_FOUND`. Omit `id` and send both
  domains to create the row instead.

V3 has no tool to delete an instruction row. To stop a row applying, clear its
fields with `null`.

## Read an instruction

List the rows with `search`, then read one with `get`:

```json theme={null}
{ "kind": "advertiser", "query": "brand.example" }
```

```json theme={null}
{ "kind": "advertiser", "id": "INSTRUCTION_ID" }
```

`query` matches the operator domain, the brand domain and the notes. Search
results omit the discount, routing, access and trust projections on purpose.
`get` adds them, each with a `provenance` sentence that says where the value
comes from:

| Projection | What it tells you |
| - | - |
| `effectiveDiscount` | The discount that applies to this pair. It is the largest of this row's discount, any matching broader instruction row's discount and any matching house-discount rule. Instruction rows that are scoped to countries are left out of the value, and `countryScope` names the countries and row ids that would add to it once a buy country is known. If a source could not be read, `unavailable` says which. |
| `routing` | Not a value. Advertiser routing belongs to the inventory source, so this projection points you to the source. |
| `sponsoredAccess` | Whether a sponsored buyer can act for the pair. It is a seller relationship Page, not row state. |
| `accountTrust` | Standing auto-approve trust. It is a buyer-account Page and cannot be written here. |

You cannot change the effective discount, routing, sponsored access or account
trust with this tool. Author discounts as house-discount rules in the playbook,
and open the inventory source or the relationship Page for the others.

## Limits

* The tool needs a Seller Account. A Buyer Account is refused with a
  wrong-account error.
* Sending the same state again returns `unchanged` rather than writing twice.
* The rows you save are the instruction rows your storefront reads when it
  composes products for a matching buyer. Keep notes to selling guidance. They
  are not a place for credentials.

## Related

* [Seller workflows](/v2/setup/v3/seller-workflows)
* [Seller tools in the tool reference](/v2/setup/v3/tool-reference#seller-tools)
* [Listing, Playbook and Business Rules](/v2/setup/seller-pages)


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