Product
Publishers
Advertisers
Networks
Help
Pause animations
Browse help topics
How-to Advertiser

Send events server to server (S2S)

Updated

View as Markdown

When a user does something you pay for, your server sends one POST to https://affiliateos.xyz/v1/advertiser/events with your ak_ key and the click ID you stored for that user. We match it to the tap, charge the event’s price if it’s live and answer with a status.

See also: Setup without an SDK

On this page

Before you start

Steps

  1. Mint API key

    Under Show developer setup, choose Mint API key, or use Mint new key on API keys. The key starts with ak_ and is shown once, so copy it into your server’s settings straight away. It goes in your server only: never in your app or a web page.

  2. Send the event

    Send it as soon as it happens, from your server:

    Shell
    curl -X POST "https://affiliateos.xyz/v1/advertiser/events" \
      -H "Authorization: Bearer $AFOS_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "clickId": "<stored afos_click_id>",
        "advertiserUserId": "tp_7731",
        "eventKey": "level_25",
        "eventId": "level_25_tp_7731",
        "timestamp": "2026-10-30T19:06:00.000Z",
        "appId": "<your appId>",
        "campaignId": "<your campaignId>"
      }'

    Example: Tidewater Putt player tp_7731 reaches level 25 at 2:06 PM in Texas. x-api-key: ak_... works in place of the Authorization header.

    FieldRequiredWhat it is
    clickIdOn live eventsThe afos_click_id you stored. attributionId is accepted as the same thing.
    advertiserUserIdYesYour own ID for the user. Each event is paid once per user.
    eventKeyYesThe event’s key, or its name in your attribution tool. Case doesn’t matter.
    eventIdYesYour unique ID for this one occurrence. A retry sends the same one.
    timestampYesWhen it happened, in ISO 8601. It must be within 48 hours of when we receive it.
    appIdYesYour product’s ID
    campaignIdNoRecommended, so we don’t have to find the campaign from the appId and key
    transactionIdFor purchases and depositsYour order ID. Required when the key contains purchase or deposit, and it stops the same order being charged twice.
    revenue, currencyNoWhat the user spent, for your return on ad spend reporting. It doesn’t change the price you pay.
    platformNoios, android or web
    testNotrue checks the event without charging it

    Send only these fields. A field we don’t know gets the whole request refused with HTTP 400.

  3. Read the answer

    A request we accept gets a 2xx response with a JSON body such as {"status": "approved", "logId": "...", "payoutUsd": 1.5}. The status decides what happened:

    statusWhat it meansCharged?
    approvedAccepted. A live event matched its tap.Yes, its price. A test isn’t charged.
    duplicateWe already have this eventId or transactionId, or this user already had this eventNo. Treat it as success.
    missing_attributionNo click ID, or one we never issuedNo
    invalid_eventNo campaign for this appId has this event keyNo
    outside_windowtimestamp is more than 48 hours from now, or past the event’s attribution window if it has oneNo
    campaign_pausedThe campaign is pausedNo
    insufficient_balanceYour balance can’t cover the priceNo, and your live campaigns pause
    rejectedSomething else is wrong. Event logs gives the reason, such as a missing transactionId or a click ID used for another user.No

    A bad or revoked key gets HTTP 401. A request with a missing or unknown field gets HTTP 400: fix it before you send it again. A burst above our rate limit also gets HTTP 400, with a rate-limit message: wait, then retry with the same eventId.

  4. Retry safely

    If a request times out, gets a 5xx or hits the rate limit, send it again with the same eventId. A repeat of an event we already have comes back as duplicate and is never charged twice. Fix a 400 for a bad field or a 401 before you retry: sending it again unchanged gets the same answer.

  5. Check it worked

    Open the campaign, then Event logs. Each event shows its time, key, user, status and price, with the reason under it when there is one.

    Before your campaign goes live, send each event with "test": true instead: Send test events without being charged. The full reference is in the developer docs.

Support

Still stuck? Write to us.

One address for every question: hello@affiliateos.ai. Put the details from this list in your first message so we can find the click or conversion without asking you for them again.

Put these in your message

  1. 01The email you sign in with
  2. 02Your role: publisher, advertiser or network
  3. 03The offer ID
  4. 04The click ID or action_id
  5. 05The time it happened, with your time zone
  6. 06What you expected to happen

Looking for a reward in an app? Ask that app's support. It credits your reward, so only it can see your balance.

Search help