Skip to content
Offers API

The offer list as JSON, rendered by you.

For teams that want the offers inside their own interface. You ask for the offers available to one user, you draw them however you like, and you send the user to the click URL we return. Clicks, conversions and the postback work exactly as they do on the hosted wall.

Working today

  • Nothing of the API is released. The endpoint below is the specification we are building to.
  • The hosted wall covers the same ground today; the only server work it needs is signing its URL.

Still being built

  • A read endpoint returning the offers a given user may see.
  • Per-placement API keys, sent as a bearer token and rotatable by you.
  • Documented rate limits and a sandbox placement for development.

How to read this page

Sections marked Planned describe a contract we are building to, published early so you can design against it. Sections with no badge are running today, exactly as written. We will tell you in writing before anything marked Planned changes.

Asking for offers

Planned

One request, scoped to a placement and a user. We apply the campaign targeting to what the request tells us about the user, and return only what that user can actually complete.

Planned — not yet released
GET https://opuswall.net/api/v1/offers
  ?placement_id=fbefb48b-df2e-4102-9e05-f48c4083edc2
  &user_id=your-own-user-id
  &country=DE
  &platform=android
  &limit=50

Authorization: Bearer <your placement API key>

Query parameters

  • placement_id

    Required
    Required
    Meaning
    Which placement is asking. Determines currency, exchange rate and revenue share.
  • user_id

    Required
    Required
    Meaning
    Your own identifier for the user.
  • country

    Required
    Optional
    Meaning
    ISO 3166-1 alpha-2.
  • platform

    Required
    Optional
    Meaning
    ios, android, web or desktop.
  • limit

    Required
    Optional
    Meaning
    How many offers to return.

What comes back

Planned

Amounts are integers. `amount_cents` is your share of the payout — what you would earn — and `currency_amount` is that same amount in your own currency at the placement exchange rate. Show your users the second one.

Planned response shape
{
  "offers": [
    {
      "campaign_id": "6c1e4a08-93d7-4b52-8f0a-5d3b72e1c486",
      "name": "Example campaign",
      "category": "games",
      "icon_url": "https://…",
      "platforms": ["android"],
      "click_url": "…",
      "goals": [
        {
          "goal_id": "2a95f07b-4e63-4c81-9d27-0b6ea31d5f42",
          "name": "Reach level 10",
          "description": "Play until your account reaches level 10.",
          "amount_cents": 168,
          "currency_amount": 1680,
          "time_limit_hours": 72
        }
      ]
    }
  ]
}

Sending a user to an offer

Planned

Open `click_url` rather than linking to the advertiser yourself. That URL is what creates the click and its id, and a completion without a click of ours cannot be attributed to you — which means it cannot be paid to you either.

What the advertiser receives

When a user opens an offer, we record a click and send them to the advertiser's tracking URL with our macros filled in and percent-encoded. The advertiser sends the click id back when a goal completes, which is what ties a conversion to one user, one placement and one moment.

Your user_id reaches the advertiser only if their tracking URL asks for it through {external_user_id} — which is one reason it must be an opaque internal id, never a name or an address. Beyond the macros their tracking URL asks for, they receive only what any website sees when the user's browser opens it.

Macros we substitute into the advertiser's tracking URL

  • {click_id}

    Meaning
    The click. The value the advertiser must return to confirm a goal.
  • {placement_id}

    Meaning
    Which publisher placement produced the click.
  • {campaign_id}

    Meaning
    Which campaign was opened.
  • {goal_id}

    Meaning
    The goal the click was opened for.
  • {external_user_id}

    Meaning
    Your user_id, as it arrived on the wall URL.
  • {sub_id1}, {sub_id2}, {sub_id3}

    Meaning
    Your sub ids, empty if none.
  • {country}

    Meaning
    The visitor's two-letter country, or empty when it is not known.
  • {platform}

    Meaning
    ios, android, desktop or web.

The postback you receive

When a conversion from your wall settles — the advertiser confirmed it and the money moved — we call the postback URL on your placement, https only. When a settled conversion is reversed, we call it again so you can take the reward back. GET appends the parameters to your URL's query; POST sends them as a form body. Your placement's page shows every attempt with your server's answer, and sends test postbacks.

All fifteen parameters are always present, empty when there is nothing to send, followed by signature: the HMAC of the fifteen, in the order above, with your placement's postback secret. Verify it, and refuse a timestamp more than an hour old — we sign every attempt as it leaves.

  • Acknowledge with any 2xx status and a body of exactly OK or 1. Anything else — another body, a redirect, an error, no answer within 10 seconds — is a failed attempt.
  • A failed attempt is retried after 1 minute, 5 and 15 minutes, 1, 3, 6 and 12 hours, then 24 hours twice: ten attempts over about three days. An abandoned notice can be re-queued from your panel for 30 days.
  • Credit once per (transaction_id, status), under a unique constraint in the same transaction as the credit. On a reversal, take back only what you credited; if you never did, answer OK and do nothing.
  • We never send a credit after its reversal, or a reversal for a credit that never left us.
  • We call only public addresses: your hostname is resolved once per attempt and refused if any address it resolves to is private, loopback, link-local or reserved. We connect to the address we checked, follow no redirect and read at most 16 KiB.
Example request (an example secret and illustrative values)
GET https://example.com/opuswall/postback?site=main
  &amount=1750
  &campaign_id=4a3a4905-900d-4fb1-b719-432d3e292085
  &campaign_name=Example%20campaign
  &goal_id=0f5b8d2e-6c1a-4e9b-b3d7-5a2c8e4f1b90
  &goal_name=Example%20goal
  &placement_id=fbefb48b-df2e-4102-9e05-f48c4083edc2
  &share_usd_cents=175
  &status=credit
  &sub_id1=summer-2026&sub_id2=&sub_id3=
  &test=0
  &timestamp=1790000000
  &transaction_id=c8e1b0d4-7a2f-4c6e-9b3d-1e5f7a9c2b4d
  &user_id=player%2048213
  &signature=60996fe5750f91e727d8d1acbb1afc765f4284d6fecf377f941e900d8eab91ab

Secret e1d2c3b4a5968778695a4b3c2d1e0f1a2b3c4d5e6f708192a3b4c5d6e7f80912 (an example).
Broken over lines for reading; sent as one. Your own site=main is not signed.

Postback parameters

  • amount

    Meaning
    What to credit, or take back, in whole units of your currency: floor(share_usd_cents × coins_per_usd ÷ 100). It can be 0.
  • campaign_id, campaign_name

    Meaning
    The campaign, and its name as it stood when the conversion was recorded.
  • goal_id, goal_name

    Meaning
    The completed goal.
  • placement_id

    Meaning
    Your placement.
  • share_usd_cents

    Meaning
    Your earnings for this conversion, in US cents — already split.
  • status

    Meaning
    credit, or reversal.
  • sub_id1, sub_id2, sub_id3

    Meaning
    As they arrived on the wall URL, empty if they did not.
  • test

    Meaning
    1 for a test postback, 0 for a real one. Never credit a 1.
  • timestamp

    Meaning
    Unix seconds when this attempt was signed.
  • transaction_id

    Meaning
    Our id for the conversion — identical on every attempt, and on its credit and its reversal.
  • user_id

    Meaning
    The user_id from your wall URL.
  • signature

    Meaning
    Lower-case hex HMAC of the other fifteen, with your postback secret.

Keys and limits

Planned

API keys are issued per placement, sent as a bearer token, and rotatable without downtime. A key is a server credential: it must never appear in a mobile app, a browser bundle or a repository.

Rate limits will be published with the release, along with the headers that report your remaining budget.

Offers API

Want the wall inside your own design?

Say so in your application. Publishers planning an API integration help us decide what the first release has to cover.