Skip to main content
Version: v2

User-bound offer delivery API

Use this flow for OFFERWALL and OFFER_API slots. It is bound to a publisher placement, user, environment, platform identity, and optional slot. A session token expires after 15 minutes. Use HTTPS and treat the token and any hosted URL containing it as sensitive.

1. Create a session​

POST /sdk/v2/sessions accepts:

FieldRequirement
placementIdExisting verified placement
adSlotIdOptional named slot to bind the session
externalUserIdStable publisher user ID, 1–128 characters
platformandroid, ios, or web, matching the placement
sdkVersionSupported semantic version at or above the placement minimum
packageBundleOrDomainExact package, bundle, or hostname registered on the placement
environmentsandbox, development, or production
consentGRANTED, DENIED, UNKNOWN, or NOT_REQUIRED
attStatusOptional app tracking transparency state
POST /sdk/v2/sessions
Content-Type: application/json

{
"placementId": "PLACEMENT_ID",
"adSlotId": "SLOT_ID",
"externalUserId": "user-123",
"platform": "android",
"sdkVersion": "2.0.0",
"packageBundleOrDomain": "com.example.puzzlequest",
"environment": "development",
"consent": "GRANTED"
}

A 201 response includes sessionId, token, issuedAt, expiresAt, contractVersion, available adSlots, capability flags, and possibly a hostedOfferwallUrl. Production sessions require a released placement. Mismatched identity, platform, SDK version, or required consent are rejected.

The session creation rate limit is 60 requests per minute per placement/IP. It does not use a publisher dashboard bearer token. Protect the resulting bearer token as a user credential; if a session is compromised, revoke it and issue a new one. The configuration endpoint returns the same session shape and checks that the path sessionId matches the bearer token.

2. Use the session​

Send Authorization: Bearer SESSION_TOKEN on subsequent SDK requests:

MethodPathSuccessful responsePurpose
GET/sdk/v2/sessions/{sessionId}/configuration200 sessionRead current configuration
POST/sdk/v2/sessions/{sessionId}/refresh200 replacement sessionRefresh before expiry; requires Idempotency-Key
POST/sdk/v2/sessions/{sessionId}/revoke204Revoke; requires Idempotency-Key
GET/sdk/v2/ad-slots/{adSlotId}/offers200 eligible offer pageUser-specific inventory; private cache up to 60 seconds
GET/sdk/v2/offers/{offerId}200 offerOffer details for this session
POST/sdk/v2/offers/{offerId}/impressions201 receiptRecord a shown impression; requires Idempotency-Key
POST/sdk/v2/offers/{offerId}/launch201 signed launchReserve and return the authorized destination; requires Idempotency-Key
GET/sdk/v2/reward-status200 status collectionCurrent session user's progress
GET/sdk/v2/reward-status/{offerId}200 statusOne offer's progress
GET /sdk/v2/ad-slots/SLOT_ID/offers
Authorization: Bearer SESSION_TOKEN

The returned offer list is filtered by session identity, slot, country, platform, campaign status, caps, and suitability. Handle no-fill as an ordinary empty state. For a launch, use the server-provided signed destination and preserve its click ID. Never construct a destination from the offer object or accept a client-supplied reward amount.

GET /ad-slots/{adSlotId}/offers accepts optional country, stateCode, deviceType, deviceManufacturer, osVersion, browser, browserVersion, cursor, and limit query parameters. The response includes items, page.nextCursor, page.limit, a 60-second cache, contractVersion, and context with placement/slot and currency information. The service still validates these values against the authenticated session and user. Use the cursor returned by the prior page; do not construct one.

Each item contains a campaignId, public title/copy, requirements and disclaimer, image URLs, reward goals, totalReward, currency, attribution window, creative revision ID, and availability. A goal has an external id, name, reward, pending minutes, and completion window. Display the campaign's requirements and any payment condition before launch. Item presence is not a guarantee that a later launch will pass caps and budget checks.

{
"items": [{
"campaignId": "CAMPAIGN_ID",
"title": "Complete the verified action",
"requirements": "Complete the stated steps",
"goals": [{"id": "qualified_purchase", "name": "Verified purchase", "reward": 50, "pendingMinutes": 0}],
"totalReward": 50,
"availability": {"status": "AVAILABLE", "reservationRequired": true}
}],
"page": {"nextCursor": null, "limit": 20},
"cache": {"maxAgeSeconds": 60},
"contractVersion": "2.0.0"
}

For a custom display, record an impression when the offer is actually shown. POST /offers/{offerId}/impressions with Idempotency-Key returns impressionId, recordedAt, expiresAt, and duplicate. The impression expires after ten minutes. Then call POST /offers/{offerId}/launch with a different Idempotency-Key and JSON {"impressionId":"IMPRESSION_ID"}. The launch requires that impression and returns sessionId, traceId, launchUrl, and expiresAt; the signed launch URL expires after five minutes. The launch reserves eligibility, cap, and budget capacity. Repeating a timed-out launch with the same idempotency key returns its existing session rather than reserving another one.

3. Support and reward verification​

GET /sdk/v2/reward-status returns items, totals (pendingMinorUnits, earnedMinorUnits, reversedMinorUnits), generatedAt, and contractVersion. GET /sdk/v2/reward-status/{offerId} filters the same shape for one offer or session. This is a UI progress view; final publisher credit still comes from the signed reward callback.

GET /sdk/v2/support returns the signed-in session user's cases as {items,count}. POST /sdk/v2/support requires Idempotency-Key and JSON sessionId, reasonCode (3–64 uppercase letters, digits, or underscores), and message (10–2,000 characters). It returns 201 with the created or existing case. The offer session must belong to the same user and placement. POST /sdk/v2/support/{supportCaseId}/evidence accepts multipart field evidence and Idempotency-Key only when development upload capability is enabled; it rejects production uploads. The upload limit is 5 MiB. Do not show an upload control in a production experience based on this endpoint.

SDK offer reads are limited to 120 requests per minute per token and writes to 60 per minute per token. A 429 may carry Retry-After. Cache offer reads only for the response's short private window and refresh after a status or session change.

On 401 sdk_session_expired, create a new session for the same user and placement rather than replaying the expired token. See API errors.