Product
Publishers
Advertisers
Networks
Help
Pause animations

Developer docs

Fetch offers, send events, verify every postback.

Request and response samples for publishers, advertisers and networks, checked against the API on , plus Markdown files your coding assistant can read.

GET Request
curl -G "https://affiliateos.xyz/publisher/offers" \
  -d country=US \
  -H "x-api-key: pk_your_key"
200 Response, one offer
{
  "id": "aff_7c2e91b04d3a",
  "name": "Harbor Blocks",
  "platforms": ["ios"],
  "isPaidOnInstall": true,
  "effectivePayoutUsd": 4.2,
  "goals": [
    { "name": "Install",
      "effectivePayoutUsd": 4.2 },
    { "name": "Reach level 10",
      "effectivePayoutUsd": 0,
      "userRewardUsd": 1.5 }
  ]
}
  1. Publisher · Offer API Show offers in your app
    1. 1 Generate a key under My Profile, then API Keys
    2. 2 Call GET /publisher/offers with x-api-key
    3. 3 Send users through trackingUrl with their ID as sub_id
    Open the reference
  2. Publisher · Offerwall Open our wall in your app
    1. 1 Copy the wall snippet from Docs in the portal
    2. 2 Fill in your user's ID as uid, and their country
    3. 3 Credit user_payout from each postback
    Open the reference
  3. Advertiser Send events for a campaign
    1. 1 Mint API key on your campaign
    2. 2 POST each event with "test": true until Verified
    3. 3 Send the stored afos_click_id as clickId on live events
    Open the reference
  4. Network Pull offers for your publishers
    1. 1 Generate a key under Workspace settings, then API Keys
    2. 2 GET /network-api/offers, one page at a time
    3. 3 Add your postback URL and a Webhook Secret
    Open the reference

Two hosts, three keys, two secrets

Hosts and keys

APIhttps://affiliateos.xyz Wallhttps://offerwall.affiliateos.xyz/w/{appId}
KeyStarts withSent asWhere you get it
Publisher API key pk_ x-api-key header My Profile, then API Keys, then Generate Key. Shown once. Up to five at a time.
Network API key ak_ x-api-key header Workspace settings, then API Keys, then Generate Key. Shown once. Each key expires on the date in the key list, 90 days by default. The list shows how many keys you can hold: at one, ask us to raise it so the next key is live before the old one expires.
Advertiser API key ak_ Authorization: Bearer Mint API key on your campaign. Shown once. Server only.
Postback secret ps_ Signs every postback Publishers: Postbacks, then Create postback secret. Networks: Workspace settings, then Generate Webhook Secret. Shown once.
Offerwall app secret ows_ Signs wall launches Only for signed launches. Keep it on your server.

GET /publisher/offers

Offer API

Every offer your account can run, with your payout for each step and a tracking link ready for your user's ID. You decide the order and the screen.

Publisher key in the x-api-key header.

curl -G "https://affiliateos.xyz/publisher/offers" \
  -d country=US -d platform=iOS -d page=1 \
  --data-urlencode "name=harbor blocks" \
  -H "x-api-key: $AFFILIATEOS_KEY"
Response (Example, trimmed)
{
  "status": "success",
  "data": {
    "offers": [
      {
        "id": "aff_7c2e91b04d3a",
        "name": "Harbor Blocks",
        "trackingType": "CPI",
        "platforms": ["ios"],
        "isPaidOnInstall": true,
        "effectivePayoutUsd": 4.2,
        "trackingUrl":
          "https://click.affiliateos.ai/click?off_id=…",
        "goals": [
          {
            "id": "1",
            "name": "Install",
            "effectivePayoutUsd": 4.2,
            "userRewardUsd": 1.5,
            "userRewardIsAdvertiserSet": true
          },
          {
            "id": "6",
            "name": "Reach level 10",
            "effectivePayoutUsd": 0,
            "userRewardUsd": 1.5,
            "userRewardIsAdvertiserSet": true
          }
        ]
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page": 50,
      "total": 1,
      "has_more_pages": false
    }
  }
}

Things that trip people up

  • Paging is off unless you send page or per_page. Without them, one response holds every offer.
  • Put one ID per user in sub_id on the tracking link. It comes back on the postback and decides whose balance you credit.
  • Keys are shown once. Keep up to five, so you can deploy a new key before you revoke the old one.
  • networkEpc is an estimate of earnings per click, never a guarantee.
  • When the same app is listed more than once, you get at most one install-paid and one event-paid listing per app and platform.
  • Cards you saved earlier: GET /publisher/offers/availability?ids=aff_...,aff_... checks 1 to 100 offer IDs. Each row returns id, available, cap and capResetsAt, plus checkedAt on the response. Check at most every 60 seconds for cards on screen, and drop any card with available: false.
ParameterExampleWhat it does
countryUSOnly offers open to players in that country. Send it every time.
stateTXA US player's state. Leaves out offers whose state rules we enforce when they exclude it.
platformiOSOnly offers for that platform.
os_version17.4With one platform: drops offers the phone is too old or too new for.
segmenttopThe Top Offers Feed, in our order. See the next section.
page, per_page1, 100Turns paging on. per_page defaults to 50 and stops at 200.
sort, sort_dirpayout, descAlso epc, install_payout, first_event_payout, newest, popular.

GET /publisher/offers?segment=top

Top Offers Feed

The same call, with the list chosen by us: our team's picks, plus offers that pay $1+, converted 10+ times in the last 30 days and had 15% or fewer reversed. Rebuilt every day.

Publisher key in the x-api-key header.

curl -G "https://affiliateos.xyz/publisher/offers" \
  -d segment=top -d country=US \
  -H "x-api-key: $AFFILIATEOS_KEY"

Things that trip people up

  • Rows come back in a fixed order: install-paid games by highest install payout, then event-paid offers with our picks first. sort is ignored.
  • Each row is your own row: your payout, your tracking link, your filters.
  • The order is the same for every publisher. It is not personalized.
  • If the Top Offers Feed is switched off, segment=top returns an empty list rather than an error.

GET offerwall.affiliateos.xyz/w/{appId}

Hosted Offerwall

A complete offerwall we host. Open it with your user's ID in a link, an iframe or a webview, and conversions arrive on your usual postback.

Simple launch: the uid in the URL. Signed launch: an HMAC signature made on your server, required once the wall is set to Signed.

<iframe
  src="https://offerwall.affiliateos.xyz/w/owapp_your_app
?uid=u_48213&source=iframe&country=US"
  style="width:100%;height:720px;border:0"
  allow="clipboard-write"
></iframe>

Things that trip people up

  • New walls start on Simple launches: the uid alone opens the wall. Older walls may require a signature on every launch. We change this setting for you: email hello@affiliateos.ai with your wall ID (owapp_...), and switch your links over first.
  • A signed URL works once and for 5 minutes. Sign a fresh one per launch; a reused one shows “This offerwall link is invalid or expired.”
  • Always send country. The wall only filters by location when you do.
  • Credit user_payout from each postback as sent. Don't recompute it from the payout. On wall postbacks, your earnings are in revenue.
  • Inside an iframe or webview the wall posts messages prefixed affiliateos.offerwall.: ready, height_changed, offer_started, offer_redirect (iframes only), close_requested, error. Never credit from these: conversions reach you as postbacks.
  • On accounts with Properties & Apps, Test launch on the property's Integrations tab opens your wall in test mode, before you write signing code.

GET your URL

Postbacks

When a user completes a step, we send a GET to your URL with the conversion as query parameters, signed with your secret. Credit on completed, deduct on reversed.

We sign; you verify. verifier is HMAC-SHA256 of the full URL without verifier, keyed with your ps_ secret.

GET https://api.pebble.example/afos
  ?action_id=act_5Kq2Wc
  &sub_id=u_48213
  &offer_id=aff_7c2e91b04d3a
  &status=completed
  &payout=4.20
  &user_payout=1.50
  &conversion_name=Install
  &offer_name=Harbor+Blocks
  &goal_id=1
  &is_offer_complete=false
  &clicktimestamp=2026-10-29T19:06:00Z
  &verifier=9f86d081884c7d659a2feaa0c55ad015…

Things that trip people up

  • Publisher postbacks are never sent unsigned. Without a postback secret, nothing is sent, even for real conversions.
  • A reversal repeats the action_id with status=reversed. Dedupe on action_id and status together, or you drop the reversal.
  • Send test postback on a saved postback adds test=true. It proves your URL answers, not that live conversions route there.
  • Install-paid games pay you once, at install. Later steps still post back with user_payout set to the player's reward: payout=0 on Offer API, feed and link postbacks; on the Hosted Offerwall, revenue=0 and payout equals the player's reward.
  • Verify against the URL exactly as you received it. Rebuilding it from a list of parameters breaks when an optional one appears.
  • Every parameter, including placement, manual_credit and the _numeric IDs, is in Postback parameters.
  • Once you save any postback row, the account-level fallback URL stops being used. If you add event rows, add a conversion row too.
ParameterSentMeaning
action_idAlwaysThe conversion's ID. A reversal repeats it.
sub_idAlwaysThe user ID you put on the tracking link, or the wall's uid.
offer_idAlwaysOur offer ID, as in the Offer API.
statusAlwayscompleted or reversed.
payoutAlwaysOffer API, feed and links: what we pay you for this step, in USD, 0 on later steps of install-paid games. Hosted Offerwall: the player's reward.
user_payoutWith a rewardThe player's reward in USD. Credit this number as sent.
revenueWallYour earnings on Hosted Offerwall postbacks. Track revenue from it when present.
conversion_nameAlwaysThe step's name, such as Install or Reach level 10.
offer_nameUsuallyThe offer's title. Store it: a pulled offer can't be looked up later.
clicktimestampUsuallyClick time as the advertiser's side reported it. The format varies; parse it defensively.
goal_idPer stepMatches goals[].id in the Offer API.
is_offer_completeAlwaystrue when the last step is done.
sub1 to sub10When the link had themYour own values from the tracking link, returned on every postback for that conversion.
reasonSome reversalsWhy it was taken back, in plain words, when we have one.
testTests onlytrue on a test postback. Don't credit it.
verifierPublishers: alwaysThe signature. Strip it, hash the rest of the URL, compare.

POST /v1/advertiser/events

Advertiser events

Tell us from your server when a player completes a step you pay for. We match it to the tap, charge your balance the event's price and pay the publisher.

Advertiser key as Authorization: Bearer ak_... (x-api-key also works). Server only.

curl -X POST \
  "https://affiliateos.xyz/v1/advertiser/events" \
  -H "Authorization: Bearer $AFOS_ADVERTISER_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "clickId": "clk_7Q2f81",
    "advertiserUserId": "tp_player_7731",
    "eventKey": "level_25",
    "eventId": "level_25_tp_player_7731",
    "timestamp": "2026-10-29T19:31:00.000Z",
    "appId": "your_app_id",
    "test": true
  }'
Response (Example)
{
  "status": "approved",
  "logId": "evl_3Hc9Qm",
  "payoutUsd": 1.5
}

Things that trip people up

  • Test events ("test": true) are free. Send one for each required event until it reads Verified.
  • Store afos_click_id when the player arrives, and send it as clickId on every live event. Without it the event is logged and never charged.
  • Use a stable eventId per event, so a retry comes back as duplicate instead of a second charge. Purchases also need transactionId.
  • Unknown fields in the body get a 400. Send only the fields listed here.
  • Send events from your server. An API key in an app can be read by anyone.
statusWhat happenedCharged
approvedMatched and accepted. On a test event, verified and logged.Live: the event's price. Test: no.
duplicateSame eventId or transactionId as before, or the player already reached this event's limit (such as once per player). Safe to treat as success.No
missing_attributionNo click ID came with a live event, or the one sent matches no tap of ours.No
invalid_eventNo event with that eventKey on that appId.No
outside_windowtimestamp isn't within 48 hours of now, or is later than the event's attribution window after the tap.No
campaign_pausedThe campaign is paused.No
insufficient_balanceYour balance can't cover the event. Your live campaigns pause until you add funds.No
rejectedSomething else failed. The reason is in Event logs on the campaign.No

GET /network-api/offers

Network API

Every offer your network can run, for your own walls and your publishers, in the same offer shape as the Offer API. Paged by default, 50 per page.

Network key in the x-api-key header. Your network's ak_ key, not an advertiser key.

curl -G "https://affiliateos.xyz/network-api/offers" \
  -d country=US -d platform=iOS -d page=1 -d per_page=50 \
  -H "x-api-key: $NORTHFOLD_NETWORK_KEY"

Things that trip people up

  • /publisher-api/offers is the same route under its older name. Both answer.
  • pub_id in a tracking link names your network. Leave it as we send it.
  • Network keys and advertiser keys both start with ak_. Keep them apart: a network key reads offers, an advertiser key sends events.
  • Network keys expire, 90 days after you generate them by default. The key list shows each date and how many keys you can hold. To switch without a gap you need room for two: if your limit is one, ask us to raise it, then generate the next key before the old one expires.
  • Generate a Webhook Secret under Workspace settings (it starts with ps_) and every postback carries a verifier you can check, exactly as above.

Guides

Step by step, for the parts of an integration that need more than a sample.

AI context files

Files for your coding assistant.

One plain Markdown file per role: endpoints, fields and rules, checked against the API on . Paste one in before you ask for code.

  • Publisher

    /ai/publisher.md

    Publisher reference

    Keys, the Offer API and Top Offers Feed, tracking links, postbacks and signatures, the Hosted Offerwall.

    17 KB Checked 30 Oct 2026

  • Advertiser

    /ai/advertiser.md

    Advertiser reference

    Products, campaigns and events, afos_click_id, server events, test events and every response status.

    6 KB Checked 30 Oct 2026

  • Network

    /ai/network.md

    Network reference

    The Network API, paging, tracking links for your publishers, postbacks and verifying them.

    6 KB Checked 30 Oct 2026

Then ask it this

Read the attached AffiliateOS reference before writing any code. Use only the endpoints and fields it lists, and ask me for keys instead of inventing them. Keep every key on the server.

AI tools can also start from the site index at /llms.txt, or read every help article as Markdown from /help/llms.txt.

Changelog

Latest changes for integrations

  1. Publishers

    Set your own API user reward, as a percent of your payout, under My Profile. Where the advertiser set no reward, the Offer API's userRewardUsd and your postbacks' user_payout follow it. Your payout doesn't change, and install steps keep a $0 user reward unless the advertiser set one.

  2. Publishers

    Add sub1 to sub10 to a tracking link and get them back on that conversion's postbacks, as params or as {sub1} to {sub10} macros.

  3. Publishers

    The Postbacks screen lists 15 macros, up from 8. Two are new: {offer_name} and {conversion_name}.

Every change, by date

Get a key.

Search help