AdTech

ads.txt, sellers.json and the SupplyChain Object

How publishers, SSPs and buyers verify who is allowed to sell inventory, and how to debug the three files when they disagree.

By the Kensaltensi team · Last updated 27 October 2026 · 6 min read

Programmatic advertising grew up with an openness that fraudsters exploited. One classic scam, domain spoofing, involved selling impressions on a low-quality site while claiming to be a premium publisher. The industry responded with three related transparency standards that together let a buyer check, at bid time, whether the company asking them to spend is genuinely authorised to sell the inventory. This guide explains what each file is, how they connect, and how to troubleshoot when they do not.

Three files, three questions

MechanismPublished byQuestion it answers
ads.txt (apps: app-ads.txt)The publisher or app developer, on their own domainWhich companies are authorised to sell my inventory, and under what account?
sellers.jsonAn SSP or exchange, on its own domainWhich sellers do I represent, and are they the owner or an intermediary?
SupplyChain object (schain)Added to each bid request by every intermediaryThrough which systems has this specific request passed?

ads.txt: the publisher's list

An ads.txt file is a plain text file placed at the root of a domain, for example https://publisher.example/ads.txt. Each non-comment line declares one authorised relationship. Here is a small example:

# ads.txt for publisher.example
# format: <ad system domain>, <account id>, <DIRECT|RESELLER>, <certification authority id (optional)>

ssp-one.example, 12345, DIRECT, a1b2c3d4e5f6a7b8
ssp-one.example, 98765, RESELLER, a1b2c3d4e5f6a7b8
exchange-two.example, pub-0001, DIRECT
google.com, pub-0000000000000000, DIRECT, f08c47fec0942fa0

OWNERDOMAIN=publisher.example
CONTACT=adops@publisher.example

Each record has comma-separated fields:

  1. Advertising system domain: the domain of the SSP or exchange the buyer will transact through.
  2. Publisher account ID: the publisher's account in that system. This must match what the system uses to identify the seller, and is the value that appears as sid in the supply chain.
  3. Relationship: DIRECT if the publisher controls the account, meaning they have a direct contractual relationship. RESELLER if the account belongs to another party authorised to resell the inventory.
  4. Certification authority ID (optional): an identifier, often the TAG registration ID, that helps verify the ad system. Google's line is a well-known example, with the ID f08c47fec0942fa0.

Later versions of the specification added variable lines such as OWNERDOMAIN, MANAGERDOMAIN, CONTACT and SUBDOMAIN, which give buyers a way to identify the publisher and handle subdomains. For mobile and connected-TV apps, the equivalent file is app-ads.txt, which is hosted on the developer website listed in the app store entry.

Typical mistake: adding a new demand partner in the ad server but forgetting to update ads.txt. The partner's requests are then "unauthorised", and many buyers will not bid or will pay less, with no visible error on the publisher's side.

sellers.json: the seller's directory

On the other side, each SSP or exchange publishes a sellers.json file at the root of its domain. It lists the account IDs it holds and states who each one is:

{
  "contact_email": "support@ssp-one.example",
  "version": "1.0",
  "identifiers": [{ "name": "TAG-ID", "value": "a1b2c3d4e5f6a7b8" }],
  "sellers": [
    {
      "seller_id": "12345",
      "name": "Publisher Example Ltd",
      "domain": "publisher.example",
      "seller_type": "PUBLISHER"
    },
    {
      "seller_id": "98765",
      "name": "Reseller Partner Inc",
      "domain": "reseller.example",
      "seller_type": "INTERMEDIARY"
    }
  ]
}

The seller_type field is PUBLISHER when the account belongs to the content owner, INTERMEDIARY when it belongs to a reseller, or BOTH. Some sellers are marked confidential, in which case their name and domain are omitted, which buyers may treat with extra caution. The seller_id is the key that links this file to the account ID in a publisher's ads.txt.

schain: the route a request took

When a bid request passes through several intermediaries, each adds a node to the schain object so the buyer can see the entire path:

"schain": {
  "ver": "1.0",
  "complete": 1,
  "nodes": [
    { "asi": "ssp-one.example",   "sid": "12345", "hp": 1 },
    { "asi": "exchange-two.example", "sid": "pub-0001", "hp": 1 }
  ]
}

complete: 1 states that the chain is complete from the originating seller to the buyer. Each node includes the ad system (asi), the seller account (sid), and hp, which indicates whether that node participates in payment. A shorter chain generally means fewer fees taken and fewer places for something to go wrong, which is why buyers often favour direct paths.

How a buyer verifies a request

  1. Read the requestThe buyer sees the site domain and the schain nodes, such as ssp-one.example with seller ID 12345.
  2. Fetch ads.txtThe buyer's system retrieves publisher.example/ads.txt (usually crawled and cached) and looks for a line naming ssp-one.example with account 12345.
  3. Check the relationshipA DIRECT line confirms the publisher's own account. A RESELLER line confirms an authorised partner.
  4. Fetch sellers.jsonThe buyer checks ssp-one.example/sellers.json for seller ID 12345 and compares its type and domain with the claim.
  5. Validate each hopFor multi-node chains, each node's ad system must have authorised the next, and every sid should appear in the relevant sellers.json.
  6. DecideA buyer's policy may bid, bid lower, or skip the request when something does not match.

Common failures and how to find them

ProblemWhy it happensFix
File not found, or served as HTMLPlaced in the wrong directory, or a 404 page returned with status 200Serve as text/plain at the root of the exact domain, with a real 200
Redirect problemsRedirect to a different domain, or chains of several redirectsKeep it to the same registrable domain and minimise hops
Typos in account IDExtra spaces, wrong characters, or ID from the wrong accountCopy from the partner's dashboard; trim whitespace
Wrong relationshipMarked DIRECT for an account the publisher does not own, or the reverseFollow the partner's instructions exactly
Missing line after onboardingPartner added in the ad server onlyAdd the process step: new partner means ads.txt update, then verify
Subdomain not coveredTraffic on news.publisher.example but file only on the rootPublish per subdomain or use the SUBDOMAIN directive appropriately
sellers.json mismatchSeller ID not present, or listed under a different domainAsk the SSP to correct their file; check you use the correct account ID
Incomplete schainAn intermediary drops or fails to append its nodeRaise with the partner; check the request payload for the nodes
Encoding and size problemsByte-order marks, strange characters, or very large filesSave as UTF-8 without BOM; remove duplicates and unused lines

A debugging routine

Start from the outside and work in. These commands use standard tools, and only need read access to public files:

# Check status, content type and redirects
curl -sSIL https://publisher.example/ads.txt

# Fetch the file and look for a specific partner
curl -sS https://publisher.example/ads.txt | grep -i "ssp-one.example"

# Fetch an SSP's sellers.json and find a seller id (requires jq)
curl -sS https://ssp-one.example/sellers.json | jq '.sellers[] | select(.seller_id=="12345")'
  1. Confirm the ads.txt URL returns a 200 status with a plain-text content type and the expected lines.
  2. For each demand partner, find the line, and check the account ID against the partner's dashboard.
  3. Look up that ID in the partner's sellers.json. It should exist, with the type you expect.
  4. Capture a real bid request and confirm the schain nodes and sid values match steps 2 and 3.
  5. If everything lines up and bids are still low, the problem is elsewhere: timeouts, floors or creative quality. Return to the staged troubleshooting sequence.

Keep it maintainable

Treat ads.txt as a managed asset rather than a file someone edits by hand. Store it in version control, review changes, run a validator in your deployment pipeline, schedule a periodic audit that removes defunct partners, and keep a record of who requested each line. Removing stale lines matters as well: each unused entry is an authorisation you no longer control.

Key takeaways

  • ads.txt is the publisher saying who may sell; sellers.json is the seller saying who they are; schain is the path this request took.
  • Each ads.txt line needs the right domain, account ID, relationship and, where applicable, certification authority ID.
  • Buyers cross-check all three, and a mismatch can reduce bids or exclude the inventory entirely.
  • The file must be served at the root as plain text with a 200 status. A friendly 404 page served with a 200 is a classic failure.
  • Authorisation does not equal performance. Valid files will not fix timeouts, floors or broken creatives.

Related guides

Further reading

About this guide. Written by the Kensaltensi engineering and QA team for educational purposes. Examples are illustrative, and standards evolve, so check the primary sources above before relying on a detail in production. If you spot an error or an out-of-date statement, email hello@kensaltensi.org and we will review and correct it.