{"openapi":"3.1.0","info":{"title":"iKame Ads Mediation – SDK API","version":"1.0.0","description":"HTTP contract between the IAM SDK (Android / iOS) and the IAM server, per IAM-10 §5–§7.\n\nFlow: `GET /config/v1/{appKey}` once per TTL (ETag/304) → `POST /openrtb2/auction` per ad request → fire `burl` on the first valid impression → `POST /events/v1/batch` with HMAC-signed batches.\n\nReference implementation: `lib/sdk/iam-sdk.ts` in this repository."},"servers":[{"url":"https://iam.begamob.com"}],"tags":[{"name":"Config","description":"IAM-10 §5 – versioned config with ETag and TTL"},{"name":"Auction","description":"IAM-10 §6 – OpenRTB auction on the IAM Server (Prebid Server)"},{"name":"Events","description":"IAM-10 §7 – SDK telemetry"},{"name":"Ops"}],"paths":{"/config/v1/{appKey}":{"get":{"tags":["Config"],"summary":"Fetch the published config for an app","description":"Send `If-None-Match` with the last ETag; `304` means keep the cached config. Cache for `ttl_seconds`; on `404` or network error keep the cached copy (BR-CFG-07). The `kill` block changes the ETag immediately when a kill switch is set.","parameters":[{"name":"appKey","in":"path","required":true,"schema":{"type":"string"},"example":"ik_puzzle_android"},{"name":"If-None-Match","in":"header","required":false,"schema":{"type":"string"},"example":"\"v14-e300e810\""},{"name":"X-IAM-Device","in":"header","required":false,"schema":{"type":"string"},"description":"Device id; rate limits apply per device and remote-config rollout buckets are stable per device","example":"dev_3f9a1c5d7f2b"},{"name":"app","in":"query","required":false,"schema":{"type":"string"},"description":"App version (remote config targeting)","example":"3.2.1"},{"name":"country","in":"query","required":false,"schema":{"type":"string"},"description":"ISO-3166 alpha-2 (remote config targeting)","example":"VN"},{"name":"platform","in":"query","required":false,"schema":{"type":"string","enum":["android","ios"]}}],"responses":{"200":{"description":"Current config","headers":{"ETag":{"schema":{"type":"string"}},"Cache-Control":{"schema":{"type":"string"},"description":"public, max-age=60"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SdkConfig"},"example":{"schema_version":1,"config_version":14,"app_key":"ik_puzzle_android","ttl_seconds":300,"endpoints":{"auction":"https://iam.example.com/openrtb2/auction","events":"https://iam.example.com/events/v1/batch"},"kill":{"all":false,"formats":[],"ad_units":[],"bidders":["bidder_c"]},"rules":{"min_interval_seconds":20,"min_interval_app_open_seconds":300,"app_open":{"min_background_seconds":30,"show_on_cold_start":false,"cold_start_wait_ms":1500,"cap_per_day":5,"excluded_screens":["checkout"]}},"ad_units":[{"id":"au_1a2b","name":"home_banner","format":"banner","enabled":true,"timeout_ms":1000,"bid_ttl_seconds":300,"max_retries":1,"floor_cpm_usd":0.05,"auto_reload":true,"cap_per_day":0,"bidders":["bidder_a","bidder_b"],"sizes":[[320,50],[320,100]],"refresh_seconds":60,"collapse_on_no_fill":false},{"id":"au_7f3a","name":"revive_rw","format":"rewarded","enabled":true,"timeout_ms":2000,"bid_ttl_seconds":1800,"max_retries":2,"floor_cpm_usd":0.8,"auto_reload":true,"cap_per_day":0,"bidders":["bidder_a"],"video":{"skippable":false,"skip_after_seconds":5,"reward_threshold_percent":100,"confirm_skip":true,"reward_display_seconds":15},"reward":{"type":"coin","amount":50},"exempt_from_interval":true}],"skadnetwork_ids":["cstr6suwn9.skadnetwork"],"event":{"batch_size":20,"flush_interval_seconds":5,"max_queue":500}}}}},"304":{"description":"Not modified – keep the cached config"},"404":{"description":"Unknown appKey or nothing published yet → SDK stays disabled (config_missing)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/openrtb2/auction":{"post":{"tags":["Auction"],"summary":"Run an auction for one ad unit","description":"One `imp` per request. `imp[0].ext.prebid.storedrequest.id` = adUnitId, `ext.prebid.storedrequest.id` = appKey. The server resolves the Stored Imp of the running config version, runs a first-price auction with `tmax` and the floor, and answers within `tmax`. `200` with an empty `seatbid` is NO_FILL (BR-AUC-04).","parameters":[{"$ref":"#/components/parameters/AppKeyHeader"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuctionRequest"},"example":{"id":"7b1f0c3e-2d4a-4f5b-9c6d-8e7f0a1b2c3d","tmax":1350,"imp":[{"id":"1","ext":{"prebid":{"storedrequest":{"id":"au_3c4d"}},"iam":{"placement":"interstitial","ad_unit_id":"au_3c4d"}}}],"device":{"geo":{"country":"VN"},"lmt":0},"regs":{"ext":{"gdpr":0}},"ext":{"prebid":{"storedrequest":{"id":"ik_puzzle_android"}},"iam":{"sdk_version":"1.0.0","config_version":14,"session_id":"ses_k3j2h1g0f9"}}}}}},"responses":{"200":{"description":"Bid response (empty seatbid = no fill)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AuctionResponse"},"examples":{"win":{"value":{"id":"7b1f0c3e-2d4a-4f5b-9c6d-8e7f0a1b2c3d","cur":"USD","seatbid":[{"seat":"bidder_a","bid":[{"id":"bid_1","impid":"1","price":2.31,"adm":"<html>…</html>","crid":"cr_a_9981","w":320,"h":480,"exp":600,"burl":"https://iam.example.com/events/v1/burl?auction=7b1f0c3e…&bidder=bidder_a&price=2.31","adomain":["advertiser.example"],"ext":{"prebid":{"type":"banner"},"iam":{"price":2.31,"bidder":"bidder_a","creative_type":"display"}}}]}],"ext":{"iam":{"auction_id":"7b1f0c3e-2d4a-4f5b-9c6d-8e7f0a1b2c3d","latency_ms":412,"floor_cpm_usd":0.5}}}},"nofill":{"value":{"id":"7b1f0c3e-2d4a-4f5b-9c6d-8e7f0a1b2c3d","seatbid":[],"nbr":0,"ext":{"iam":{"auction_id":"7b1f0c3e-2d4a-4f5b-9c6d-8e7f0a1b2c3d","latency_ms":980}}}}}}}},"400":{"description":"Invalid bid request, unknown ad unit / stored imp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"description":"Blocked: kill switch (`scope`, `reason`), ad unit disabled, or stored request belongs to another app → SDK state DISABLED","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/events/v1/batch":{"post":{"tags":["Events"],"summary":"Send a batch of SDK events","description":"Signature: `X-IAM-Signature = hex(HMAC-SHA256(eventSecret, X-IAM-Timestamp + rawBody))` where `eventSecret` is the per-app secret shown on the Dashboard. Flush every `event.flush_interval_seconds`, when `event.batch_size` is reached, or when the app goes to background. Keep an offline queue (max `event.max_queue`) and retry; the server dedupes on `event_id`.","parameters":[{"$ref":"#/components/parameters/AppKeyHeader"},{"name":"X-IAM-Timestamp","in":"header","required":true,"schema":{"type":"string"},"description":"Epoch ms as string; part of the signed message","example":"1762150004300"},{"name":"X-IAM-Signature","in":"header","required":true,"schema":{"type":"string"},"description":"Lower-case hex HMAC-SHA256"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventBatch"},"example":{"sdk_version":"1.0.0","config_version":14,"platform":"android","app_version":"3.2.0","device_id":"dev_3f9a1c5d7e2b","session_id":"ses_k3j2h1g0f9","events":[{"event_name":"ad_request","event_id":"0d9c8b7a-6f5e-4d3c-b2a1-0f9e8d7c6b5a","client_ts":1762150000000,"ad_unit_id":"au_3c4d","format":"interstitial","request_id":"7b1f0c3e-2d4a-4f5b-9c6d-8e7f0a1b2c3d","country":"VN","extra":{"timeout_ms":1500,"retry_index":0}},{"event_name":"impression","event_id":"1e0d9c8b-7a6f-4e5d-c3b2-1a0f9e8d7c6b","client_ts":1762150004200,"ad_unit_id":"au_3c4d","format":"interstitial","request_id":"7b1f0c3e-2d4a-4f5b-9c6d-8e7f0a1b2c3d","impression_id":"imp_5a4b3c2d","bidder":"bidder_a","price_cpm_usd":2.31,"extra":{"burl_status":200}}]}}}},"responses":{"202":{"description":"Accepted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventBatchResponse"},"example":{"accepted":2}}}},"400":{"description":"Invalid JSON or schema (`issues` lists the Zod errors)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"413":{"description":"Body larger than 256 KB","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/events/v1/burl":{"get":{"tags":["Events"],"summary":"Billing URL","description":"Opaque URL taken from `bid.burl`; fire it exactly once on the first valid impression, then send the `impression` event with `extra.burl_status`.","responses":{"200":{"description":"ok"}}}},"/healthz":{"get":{"tags":["Ops"],"summary":"Liveness / readiness","responses":{"200":{"description":"Healthy","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Health"}}}},"503":{"description":"Database not reachable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"SdkConfig":{"type":"object","properties":{"schema_version":{"type":"integer","description":"SDK rejects configs whose schema_version it does not know (config_rejected)"},"config_version":{"type":"integer","description":"Published version N; reported back on every auction and event batch"},"app_key":{"type":"string"},"ttl_seconds":{"type":"integer","description":"Re-fetch after this many seconds (BR-CFG-06); serve from cache meanwhile"},"endpoints":{"type":"object","properties":{"auction":{"type":"string","format":"uri"},"events":{"type":"string","format":"uri"}},"required":["auction","events"],"description":"Absolute URLs the SDK must call"},"kill":{"type":"object","properties":{"all":{"type":"boolean"},"formats":{"type":"array","items":{"type":"string","enum":["banner","interstitial","rewarded","app_open"],"description":"Ad format"}},"ad_units":{"type":"array","items":{"type":"string"}},"bidders":{"type":"array","items":{"type":"string"}}},"required":["all","formats","ad_units","bidders"],"description":"Kill switch block, recomputed on every request (BR-CFG-05). Any match → the SDK must not request that inventory"},"rules":{"type":"object","properties":{"min_interval_seconds":{"type":"integer"},"min_interval_app_open_seconds":{"type":"integer"},"app_open":{"type":"object","properties":{"min_background_seconds":{"type":"integer"},"show_on_cold_start":{"type":"boolean"},"cold_start_wait_ms":{"type":"integer"},"cap_per_day":{"type":"integer"},"excluded_screens":{"type":"array","items":{"type":"string"}}},"required":["min_background_seconds","show_on_cold_start","cold_start_wait_ms","cap_per_day","excluded_screens"]}},"required":["min_interval_seconds","min_interval_app_open_seconds","app_open"]},"ad_units":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"adUnitId – also the Prebid stored imp id"},"name":{"type":"string"},"format":{"type":"string","enum":["banner","interstitial","rewarded","app_open"],"description":"Ad format"},"enabled":{"type":"boolean"},"timeout_ms":{"type":"integer","description":"SDK-side auction timeout; the server uses tmax = timeout_ms − 150"},"bid_ttl_seconds":{"type":"integer","description":"Bid expiry (BR-AUC-06)"},"max_retries":{"type":"integer"},"floor_cpm_usd":{"type":"number"},"auto_reload":{"type":"boolean"},"cap_per_day":{"type":"integer","description":"0 = no cap"},"bidders":{"type":"array","items":{"type":"string"},"description":"Bidder ids enabled on this ad unit"},"sizes":{"description":"banner only: [w, h] list","type":"array","items":{"type":"array","prefixItems":[{"type":"integer"},{"type":"integer"}],"items":false,"minItems":2,"maxItems":2}},"refresh_seconds":{"description":"banner only","type":"integer"},"collapse_on_no_fill":{"description":"banner only","type":"boolean"},"close_delay_seconds":{"description":"interstitial / app_open","type":"integer"},"video":{"description":"rewarded only","type":"object","properties":{"skippable":{"type":"boolean"},"skip_after_seconds":{"type":"integer"},"reward_threshold_percent":{"type":"integer"},"confirm_skip":{"type":"boolean"},"reward_display_seconds":{"type":"integer"}},"required":["skippable","skip_after_seconds","reward_threshold_percent","confirm_skip","reward_display_seconds"]},"reward":{"description":"rewarded only","type":"object","properties":{"type":{"type":"string"},"amount":{"type":"number"}},"required":["type","amount"]},"exempt_from_interval":{"description":"rewarded only: ignore min_interval_seconds","type":"boolean"},"waterfall":{"type":"object","properties":{"enabled":{"type":"boolean"},"strategy":{"type":"string","const":"bidding_first","description":"Run the OpenRTB auction first; call tiers whose eCPM can beat the auction price"},"max_tiers":{"type":"integer","description":"Max tiers called per load"},"tiers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"network":{"type":"string","description":"Network id; the SDK needs a matching adapter module"},"placement_id":{"type":"string"},"mode":{"type":"string","enum":["fixed","bidding"],"description":"fixed = line item with fixed eCPM; bidding = network returns a price"},"ecpm_usd":{"type":"number","description":"Expected / fixed eCPM, tiers are ordered by it"},"timeout_ms":{"type":"integer"},"countries":{"description":"ISO-3166 alpha-2; empty = all","type":"array","items":{"type":"string"}}},"required":["id","network","placement_id","mode","ecpm_usd","timeout_ms"]}}},"required":["enabled","strategy","max_tiers","tiers"],"description":"Waterfall over SDK networks, executed by the SDK after the auction (Phase 2 §3)"}},"required":["id","name","format","enabled","timeout_ms","bid_ttl_seconds","max_retries","floor_cpm_usd","auto_reload","cap_per_day","bidders"],"description":"Ad unit as served to the SDK"}},"skadnetwork_ids":{"type":"array","items":{"type":"string"},"description":"iOS: union of the active bidders' SKAdNetwork ids"},"event":{"type":"object","properties":{"batch_size":{"type":"integer"},"flush_interval_seconds":{"type":"integer"},"max_queue":{"type":"integer"}},"required":["batch_size","flush_interval_seconds","max_queue"]},"remote_config":{"description":"Remote config resolved per device from ?app=<version>, ?country=<ISO-2> and X-IAM-Device (rollout bucket)","type":"object","properties":{"params":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"key → typed value (string / number / boolean / json) resolved for this device"},"matched":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"key → name of the condition that applied, or \"default\""}},"required":["params","matched"]}},"required":["schema_version","config_version","app_key","ttl_seconds","endpoints","kill","rules","ad_units","skadnetwork_ids","event"],"description":"Response of GET /config/v1/{appKey}"},"AuctionRequest":{"type":"object","properties":{"id":{"type":"string","minLength":8,"description":"request_id generated by the SDK (UUID). Echoed in the response and used to join SDK events"},"tmax":{"default":1000,"description":"ms; = ad unit timeout_ms − 150","type":"integer","minimum":100,"maximum":5000},"imp":{"minItems":1,"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"ext":{"type":"object","properties":{"prebid":{"type":"object","properties":{"storedrequest":{"type":"object","properties":{"id":{"type":"string","description":"adUnitId"}},"required":["id"]}},"required":["storedrequest"]},"iam":{"type":"object","properties":{"placement":{"type":"string"},"ad_unit_id":{"type":"string"}}}}}},"required":["ext"]},"description":"Exactly one imp per request in Phase 1"},"device":{"type":"object","properties":{"geo":{"type":"object","properties":{"country":{"type":"string"}}},"lmt":{"description":"1 = limit ad tracking","type":"number"}}},"user":{"type":"object","properties":{"ext":{"type":"object","properties":{"consent":{"description":"TCF string when GDPR applies","type":"string"}}}}},"regs":{"type":"object","properties":{"ext":{"type":"object","properties":{"gdpr":{"type":"number"}}}}},"ext":{"type":"object","properties":{"prebid":{"type":"object","properties":{"storedrequest":{"type":"object","properties":{"id":{"type":"string","description":"appKey (top-level stored request)"}},"required":["id"]}}},"iam":{"type":"object","properties":{"sdk_version":{"type":"string"},"config_version":{"type":"number"},"session_id":{"type":"string"}}}}}},"required":["id","imp"],"description":"OpenRTB 2.x bid request (subset used by IAM)"},"AuctionResponse":{"type":"object","properties":{"id":{"type":"string","description":"= request id"},"cur":{"type":"string","const":"USD"},"seatbid":{"type":"array","items":{"type":"object","properties":{"seat":{"type":"string","description":"bidder id"},"bid":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"impid":{"type":"string"},"price":{"type":"number","description":"Clearing price (first-price), USD CPM"},"adm":{"type":"string","description":"Creative markup: HTML for display, VAST XML for video"},"crid":{"type":"string"},"w":{"type":"integer"},"h":{"type":"integer"},"exp":{"type":"integer","description":"Seconds the bid stays valid (= bid_ttl_seconds)"},"burl":{"type":"string","description":"Billing URL – fire on the first valid impression (BR-REV-01)"},"adomain":{"type":"array","items":{"type":"string"}},"ext":{"type":"object","properties":{"prebid":{"type":"object","properties":{"type":{"type":"string","enum":["banner","video"]},"targeting":{"description":"Prebid Mobile compatibility: unsuffixed hb_pb / hb_bidder / hb_size / hb_env mark the winning bid","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},"events":{"description":"Prebid Mobile compatibility: absolute URLs fired on win / impression; imp doubles as the IAM billing URL","type":"object","properties":{"win":{"type":"string","format":"uri"},"imp":{"type":"string","format":"uri"}},"required":["win","imp"]}},"required":["type"]},"iam":{"type":"object","properties":{"price":{"type":"number"},"bidder":{"type":"string"},"creative_type":{"type":"string","enum":["display","video"]},"duration_s":{"type":"number"}},"required":["price","bidder","creative_type"]}},"required":["prebid","iam"]}},"required":["id","impid","price","adm","crid","w","h","exp","burl","adomain","ext"]}}},"required":["seat","bid"]},"description":"Empty array = no fill"},"nbr":{"description":"Present on no-bid responses","type":"integer"},"ext":{"type":"object","properties":{"iam":{"type":"object","properties":{"auction_id":{"type":"string"},"latency_ms":{"type":"number"},"floor_cpm_usd":{"type":"number"},"engine":{"description":"Which engine ran the auction","type":"string","enum":["simulated","prebid-server"]}},"required":["auction_id","latency_ms"]}},"required":["iam"]}},"required":["id","cur","seatbid","ext"],"description":"OpenRTB bid response. 200 with empty seatbid = NO_FILL"},"EventBatch":{"type":"object","properties":{"sdk_version":{"type":"string"},"config_version":{"type":"integer"},"platform":{"type":"string","enum":["android","ios"]},"app_version":{"type":"string"},"device_id":{"type":"string","description":"Stable per install (not the advertising id)"},"session_id":{"type":"string","description":"New per cold start"},"events":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"object","properties":{"event_name":{"type":"string","enum":["sdk_init","config_applied","config_stale","config_missing","config_rejected","ad_request","ad_response","bid_won","bid_expired","ad_show_attempt","ad_show_failed","ad_show","impression","ad_click","video_progress","reward","ad_close","render_error","frequency_block","app_open_trigger","sdk_error","waterfall_attempt","waterfall_fill"]},"event_id":{"type":"string","description":"UUID per event – the server dedupes on it"},"client_ts":{"type":"number","description":"Device clock, epoch ms"},"ad_unit_id":{"type":"string"},"format":{"type":"string","enum":["banner","interstitial","rewarded","app_open"],"description":"Ad format"},"placement":{"type":"string","enum":["banner","interstitial","rewarded","app_open"],"description":"Ad format"},"request_id":{"description":"Auction request id – joins the event to the server-side auction log","type":"string"},"impression_id":{"type":"string"},"bidder":{"type":"string"},"price_cpm_usd":{"type":"number"},"error_code":{"description":"IAM-10 §3 error code, e.g. TIMEOUT, NO_FILL, RENDER_ERROR","type":"string"},"latency_ms":{"type":"number"},"country":{"type":"string"},"extra":{"description":"Flat key/value payload per event type","type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":["string","number","boolean","null"]}}},"required":["event_name","event_id","client_ts"],"description":"One SDK event"}}},"required":["sdk_version","config_version","platform","app_version","device_id","session_id","events"],"description":"Request body of POST /events/v1/batch (≤ 100 events, ≤ 256 KB)"},"EventBatchResponse":{"type":"object","properties":{"accepted":{"type":"integer"}},"required":["accepted"]},"Health":{"type":"object","properties":{"ok":{"type":"boolean"},"sdkContract":{"type":"string"},"uptimeSeconds":{"type":"number"},"storage":{"type":"object","properties":{"kind":{"type":"string","enum":["postgres","memory"]},"auctions":{"type":"number"},"events":{"type":"number"},"versions":{"type":"number"}},"required":["kind"]},"apps":{"type":"array","items":{"type":"object","properties":{"appKey":{"type":"string"},"platform":{"type":"string","enum":["android","ios"]}},"required":["appKey","platform"]}},"memory":{"type":"object","properties":{"auctions":{"type":"number"},"events":{"type":"number"},"revision":{"type":"number"}},"required":["auctions","events","revision"]}},"required":["ok","sdkContract","uptimeSeconds","storage","apps","memory"]},"Error":{"type":"object","properties":{"error":{"type":"string"},"detail":{"type":"string"},"issues":{"type":"array","items":{}},"scope":{},"reason":{"type":"string"}},"required":["error"]}},"parameters":{"AppKeyHeader":{"name":"X-IAM-App-Key","in":"header","required":true,"schema":{"type":"string"},"description":"appKey of the calling app (Dashboard → Apps → Thông tin)"}},"responses":{"Unauthorized":{"description":"Unknown app key or bad signature","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}