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
Asking for offers
PlannedOne 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.
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.
| Parameter | Required | Meaning |
|---|---|---|
| placement_id | Required | Which placement is asking. Determines currency, exchange rate and revenue share. |
| user_id | Required | Your own identifier for the user. |
| country | Optional | ISO 3166-1 alpha-2. |
| platform | Optional | ios, android, web or desktop. |
| limit | Optional | How many offers to return. |
What comes back
PlannedAmounts 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.
{
"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
PlannedOpen `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.
| Macro | Meaning |
|---|---|
| {click_id} | The click. The value the advertiser must return to confirm a goal. |
| {placement_id} | Which publisher placement produced the click. |
| {campaign_id} | Which campaign was opened. |
| {goal_id} | The goal the click was opened for. |
| {external_user_id} | Your user_id, as it arrived on the wall URL. |
| {sub_id1}, {sub_id2}, {sub_id3} | Your sub ids, empty if none. |
| {country} | The visitor's two-letter country, or empty when it is not known. |
| {platform} | 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.
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
×tamp=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.
| Parameter | Meaning |
|---|---|
| amount | 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 | The campaign, and its name as it stood when the conversion was recorded. |
| goal_id, goal_name | The completed goal. |
| placement_id | Your placement. |
| share_usd_cents | Your earnings for this conversion, in US cents — already split. |
| status | credit, or reversal. |
| sub_id1, sub_id2, sub_id3 | As they arrived on the wall URL, empty if they did not. |
| test | 1 for a test postback, 0 for a real one. Never credit a 1. |
| timestamp | Unix seconds when this attempt was signed. |
| transaction_id | Our id for the conversion — identical on every attempt, and on its credit and its reversal. |
| user_id | The user_id from your wall URL. |
| signature | Lower-case hex HMAC of the other fifteen, with your postback secret. |
Keys and limits
PlannedAPI 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.
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.