# AffiliateOS Network API: reference for AI coding assistants

> Snapshot, 30 October 2026. Written for www.affiliateos.ai from the AffiliateOS API as it runs today. Human-readable version: https://www.affiliateos.ai/help/developers#network-api. Index of every help article: https://www.affiliateos.ai/help/llms.txt

Give this file to your coding assistant before you ask it to build on AffiliateOS as a network. Use only the endpoints and fields listed here. Ask the developer for keys; never invent them.

## Naming

You run a network: your own publishers, your own walls, your own rates. Some wire names are older and stay as they are: the path `/publisher-api/offers` and the tracking macro `pub_id` / `{PUBLISHER_ID}` both identify **your network**. Do not rename them in links.

## Authentication

```http
x-api-key: ak_your_network_key
```

Network keys start with `ak_`, are created under **Workspace settings**, then **API Keys**, and are shown once. Each key expires on the date shown in the key list (90 days by default). The list also shows how many keys you can hold; with a limit of one, ask AffiliateOS to raise it so a new key is live before the old one expires. Advertiser keys also start with `ak_`; keep them apart. A network key reads offers.

## Fetch offers

```http
GET https://affiliateos.xyz/network-api/offers
x-api-key: ak_your_network_key
```

`/publisher-api/offers` is the same route under its older name.

### Query parameters (all optional)

| Parameter | Example | Meaning |
|---|---|---|
| `country` | `US` | Only offers open to players in that country. |
| `state` | `TX` | A US player's state. Offers that exclude it are left out. |
| `platform` | `ios`, `android` | Only offers for that platform. |
| `device` | `phone`, `tablet` | Only offers that target that device type. |
| `tracking_type` | `CPI` | Filter by tracking type. |
| `category` | `Game` | Filter by category label. |
| `vertical` | `sports_betting`, `real_money_gaming`, `none` | Filter by vertical. |
| `segment` | `install`, `popular` | `install`: paid-on-install offers. `popular`: offers that earned across AffiliateOS in the last 30 days. |
| `name` / `id` | `harbor blocks` | Search by name, or one exact offer ID. |
| `sort`, `sort_dir` | `payout`, `desc` | `payout`, `epc`, `conversion_rate`, `newest`. |
| `page`, `per_page` | `1`, `50` | Paged by default: page 1, 50 per page. |

### Paging all offers (Node)

```js
let page = 1;
const all = [];
for (;;) {
  const res = await fetch(
    `https://affiliateos.xyz/network-api/offers?country=US&page=${page}&per_page=100`,
    { headers: { "x-api-key": process.env.NETWORK_KEY } },
  );
  const { data } = await res.json();
  all.push(...data.offers);
  if (!data.pagination.has_more_pages) break;
  page += 1;
}
```

### The offer object

Each offer carries `id`, `name`, images, `trackingType`, `platforms`, `geoTargeting`, `effectivePayoutUsd` (your payout), `networkEpc` (an estimate of earnings per click, never a guarantee), `trackingUrl` and `goals[]` (each step with its `id`, `name`, `effectivePayoutUsd` and `userRewardUsd`).

Game offers paid per install pay once, at install. Later steps can carry a player reward and post back with `payout=0`.

## Tracking links for your publishers

Use each offer's `trackingUrl`. Put the end user's ID in `sub_id`, one ID per user: it is the value that comes back on the postback. Leave `pub_id` as we send it; it names your network.

## Postbacks

When a conversion happens, AffiliateOS sends a GET request to your postback URL (set up in your network dashboard: a global URL, and optional offer-specific URLs that win over it).

```text
GET https://postback.northfold.example/afos?action_id=act_5Kq2Wc&sub_id=u_48213&offer_id=aff_7c2e91b04d3a&status=completed&payout=4.20&conversion_name=Install&goal_id=1&is_offer_complete=false&clicktimestamp=2026-10-29T19:06:00Z&verifier=9f86d081...
```

| Parameter | Meaning |
|---|---|
| `action_id` | The conversion's ID. Each step of a multi-step offer gets its own. |
| `sub_id` | The end user's ID from the tracking link. |
| `offer_id` | The offer's ID. |
| `status` | `completed` or `reversed`. |
| `payout` | Your earnings for this step in USD. |
| `conversion_name` | The step's name. |
| `goal_id` | The step's ID; match it to `goals[].id`. |
| `is_offer_complete` | `true` when the last step is done. |
| `clicktimestamp` | Click time as the advertiser's side reported it; format varies, may be empty. |
| `verifier` | Present once you have a Webhook Secret. See below. |

Rules:

- Return HTTP 200. Credit on `completed`, deduct on `reversed`.
- Dedupe on `action_id` and `status` together, so a reversal is never dropped as a duplicate.
- A test postback from the dashboard carries `test=true`. Don't credit it.

### Verify postbacks

Generate a Webhook Secret (`ps_...`) under Workspace settings; it is shown once, and creating a new one replaces the old one at once. Every postback then carries `verifier`: HMAC-SHA256 of the full URL without `verifier`, keyed with your secret.

```js
const crypto = require("crypto");

function isFromAffiliateOS(fullUrl, secret) {
  const url = new URL(fullUrl);
  const verifier = url.searchParams.get("verifier");
  url.searchParams.delete("verifier");
  const expected = crypto.createHmac("sha256", secret).update(url.toString()).digest("hex");
  return expected === verifier;
}
```

Verify against the URL exactly as received; don't rebuild it from a fixed list of parameters.

## What a player can see after a tap

| Page title (exact) | Meaning |
|---|---|
| This offer isn't available for your account | Our traffic checks refused the tap. The offer is still live. |
| This offer isn't available in your state | The offer doesn't take players from that US state. |
| This offer has ended | The offer is gone; the link outlived it. |
| Download a game | The game isn't taking new players right now. |
| This offer is no longer available | The advertiser's side would not serve the tap. No brand name on the page. |
| This offer could not be opened | We couldn't reach the offer's link. The page has a Try again button. |

## Things never to do

- Never put an API key or Webhook Secret in an app or a web page.
- Never reuse one `sub_id` for several users.
- Never change `pub_id` in a tracking link.
