adPluga

Integration guide

From your promotions admin to adPluga

If you already run campaigns and banners on your side, this is the whole path through the API — in the order you would do it, using the names you already use.

The shape of it

adPluga separates three things your admin puts on one screen. Worth having them clear before the first request.

  • The campaign holds what is commercial: objective, price, period and who it is for. It has no image.
  • The ad is the creative inside a campaign: the image, the click destination and the alternative text. It inherits the campaign's period.
  • The slot is the position where the ad appears in your app. It is what the app asks for by name — not a field on the banner.

1. The integration key

Create it in the dashboard, under Settings › Keys. Tick only the scopes for this path: the key reaches nothing else, and billing, wallet and payouts are out of reach by construction. The secret is shown once.

# Settings › Keys › New integration key
# Scopes for this path:
#   campaign:read  campaign:write
#   ad:write       asset:read  asset:write
#   audience:read  audience:write
#   property:read  slot:read   slot:write

export ADPLUGA_KEY=ak_test_   # ak_live_ when you go to production

2. The slots, one per position

Your four positions — home top, home middle, category and search — are four slots. Create them once; after that the campaign chooses where it shows.

# One property per app/site, one slot per position.
curl -X POST https://api.adpluga.com/v1/slots \
  -H "Authorization: Bearer $ADPLUGA_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "property_id": "PROPERTY_ID",
    "name": "home-top",
    "format": "display",
    "width": 1200, "height": 400,
    "accepted_sizes": [{"w":1200,"h":400}]
  }'

# Repeat for home-middle, category and search.
# The name is an identifier: /serve accepts the id or the name, within the property.

The size is free: 1200×400, 800×400 and 600×600 are accepted as they are. You do not have to stick to IAB sizes.

3. The audiences

VIP customers, no purchase in 30 days, newly registered: each is an audience you create once and feed from your CRM. Send identifiers already hashed — we do not need to know who they are.

# The "Target audience" in your admin is a first-party audience.
curl -X POST https://api.adpluga.com/v1/audiences \
  -H "Authorization: Bearer $ADPLUGA_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"name": "VIP customers", "key": "vip"}'

# Then push the members from your CRM.
curl -X POST https://api.adpluga.com/v1/audiences/AUDIENCE_ID/members \
  -H "Authorization: Bearer $ADPLUGA_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"subjects": ["CUSTOMER_HASH", "..."]}'

4. The campaign

Everything your admin treats as commercial goes here. The Idempotency-Key can be your internal identifier: repeating the request with the same key returns the same campaign instead of creating another.

curl -X POST https://api.adpluga.com/v1/campaigns \
  -H "Authorization: Bearer $ADPLUGA_KEY" \
  -H 'Idempotency-Key: summer-promo-2026' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Summer Promo 2026",
    "objective": "traffic",
    "pricing_model": "CPM",
    "bid_cents": 500,
    "pacing": "uniforme",
    "currency": "AOA",
    "starts_at": "2026-10-01T09:00:00Z",
    "ends_at":   "2026-10-31T23:59:00Z",
    "targeting": {
      "geo": ["AO"],
      "device": ["mobile"],
      "slots": ["home-top"],
      "audiences": { "first_party_include": ["vip"] },
      "freq_cap": { "per_user_per_day": 3 }
    }
  }'

Your admin's priority is the bid: the auction ranks by eCPM, so High is simply a larger bid_cents. The usage limit maps to freq_cap per person per day; for an absolute ceiling use budget_total_cents.

5. The creative

Two calls: one asks for an upload destination, the other confirms once the file has landed. The resulting URL is what the banner uses.

# 1. Ask for an upload destination
curl -X POST https://api.adpluga.com/v1/assets/presign \
  -H "Authorization: Bearer $ADPLUGA_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"filename":"banner.png","mime":"image/png","size_bytes":184320}'

# 2. PUT the file to the upload_url you got back, then confirm
curl -X POST https://api.adpluga.com/v1/assets/ASSET_ID/commit \
  -H "Authorization: Bearer $ADPLUGA_KEY"

7. Seeing the ad answer

The app asks the data plane for the ad with the property's publishable key. It is the same call our SDKs make underneath.

# The app asks for the ad with the property's publishable key.
curl 'https://edge.adpluga.com/v1/serve?slot=home-top' \
  -H 'X-AdPluga-Key: pk_test_•••'

# The response carries the creative, the alt_text and the signed measurement URLs.
# While you build, use ak_test_ and pk_test_: nothing is charged.

What about stores?

Your store list has no direct equivalent, and that is deliberate — we do not know what one of your stores is. There are two paths: if each store has its own placement in the app, create a slot per store and point the campaign at that store's slots; if stores share the same positions, make each set of customers an audience and target that instead. The first path is by inventory, the second by people.

What is ours and what stays yours

adPluga is the ad server. That deliberately leaves out much of what your admin does:

  • Discount, cashback and points are your promotion's mechanics. We deliver the banner that announces it; redemption is yours.
  • Push, email and SMS are your channels. We serve the banner in the app.
  • The internal identifier and the description stay with you — we do not store them. Use the identifier as the Idempotency-Key and the link is made for you.
  • While you build, use test keys: they write in test mode, charge nothing, use no quota and count towards no metric.