Ad Management
Create and serve targeted promotional content, onboarding cards, announcements, and feature spotlights directly inside your app — managed from the Swake portal without a code deployment.
Overview
Swake Ad Management lets product and growth teams publish in-app content — banners, cards, interstitials, and more — from a central portal. Targeting rules, scheduling, and A/B variants are all configured without touching your app's code.
Highlight new features to the right users at the right time.
Guide new users with sequential in-app cards and tips.
Promote upgrades to free-tier users with conversion-focused copy.
Run time-limited offers with start and end date scheduling.
Key capabilities:
| Rich ad definitions | Image, text, CTA, and full HTML ad types. |
| Targeting rules | Device type, user segment, and A/B variant matching. |
| Priority-based serving | Highest-priority non-expired ad wins for a placement. |
| Version history | Every definition change is snapshotted; roll back at any time. |
| Real-time toggling | Activate or deactivate ads instantly — no app deploy required. |
| Impression tracking | SDK reports impressions for analytics. |
Creating Ads in the Portal
All ad management happens inside your project in the Swake portal. No API calls or code changes are needed to create or update ads.
| Step | Action |
|---|---|
| 1 | Go to your project in the Swake portal. |
| 2 | Navigate to Ads in the project sidebar. |
| 3 | Click New Ad. |
| 4 | Fill in: Name, Ad Type, Campaign (optional), Priority, Start/End dates. |
| 5 | Design your ad content in the Definition JSON editor or use the visual editor. |
| 6 | Set targeting rules: device type, user segment, and A/B variant. |
| 7 | Click Save — the ad is created as inactive. |
| 8 | Toggle Active to start serving the ad immediately. |
Ads can be duplicated from the overflow menu — the copy gets a "(copy)" name suffix and starts as inactive. Full version history is preserved for every ad.
Ad Types & Formats
Choose the format that best fits your content and placement. Each type has its own definition_json schema.
Horizontal banner displayed at the top or bottom of the screen. Slim footprint, always visible.
Full-width card with image, title, body text, and a CTA button. Great for rich content.
Full-screen overlay shown at natural breakpoints. High impact, use sparingly.
Embedded in a list or feed at a specified index. Feels native to the surrounding content.
Custom HTML/CSS with full design control. Ideal for rich, pixel-perfect branded content.
Ad Definition JSON
The ad content is stored as a definition_json blob validated by Zod on the API. Different ad types have different schemas.
Card
{
"type": "card",
"title": "New Feature: Dark Mode",
"body": "You can now switch to dark mode in Settings → Appearance.",
"imageUrl": "https://cdn.example.com/dark-mode-preview.png",
"imageAlt": "Dark mode preview",
"cta": {
"label": "Try it now",
"action": "navigate",
"url": "settings/appearance"
},
"dismissible": true,
"campaign": "q1-feature-launch"
}
Banner
{
"type": "banner",
"text": "🎉 Version 2.0 is here — see what's new",
"cta": {
"label": "Learn more",
"action": "open_url",
"url": "https://yourapp.com/changelog"
},
"backgroundColor": "#0f172a",
"textColor": "#ffffff",
"position": "top"
}
HTML
{
"type": "html",
"html": "<div style='padding: 16px; background: #f0fdf4;'>...</div>",
"height": 120
}
CTA action types
| Action | Behaviour |
|---|---|
navigate | Push a route inside the app (relative path). |
open_url | Open an external URL in the system browser. |
dismiss | Close the ad without navigating anywhere. |
custom_event | Fire a named custom event for your analytics layer. |
Targeting & Scheduling
Ads are only served to users that match all configured targeting rules. When multiple ads match the same placement, the one with the highest priority wins.
Target mobile, tablet, or desktop. Pass device_type in the SDK request.
mobiletabletdesktopComma-separated segment IDs created in Users → Segments. Only matched users see the ad.
ISO 8601 start_date and end_date. The ad is only served within this window.
Integer — higher number wins. When multiple ads match, the highest-priority active ad is returned.
Global on/off toggle. Inactive ads never serve regardless of all other targeting rules.
Optional string key passed by the SDK to select a specific variant of a placement.
The SDK endpoint returns HTTP 204 when no ad matches the targeting rules. Always handle the no-ad case gracefully in your UI.
A/B Testing
Run controlled experiments by creating two ads with the same placement ID but different ab_variant values. The SDK passes the variant key at fetch time and receives only the matching ad.
| Step | Action |
|---|---|
| 1 | Create Ad A with placement "home-banner" and ab_variant "control". |
| 2 | Create Ad B with placement "home-banner" and ab_variant "treatment". |
| 3 | Assign your users to variants in your app (e.g., via a feature flag service). |
| 4 | Pass abVariant when fetching the ad from the SDK. |
| 5 | Compare impression and conversion rates per variant in your analytics. |
// SDK fetches ad for a specific A/B variant
const ad = await Swake.getAd('home-banner', {
abVariant: userVariant, // 'control' | 'treatment'
});
Swake does not assign users to variants — that is handled by your own experimentation layer. Swake only serves the ad matching the variant you pass.
Version History
Every time definition_json changes, Swake automatically creates a new version snapshot. Metadata-only changes (name, active toggle, targeting) do not create a new version.
| Capability | Details |
|---|---|
| View history | Ads → select ad → History tab in the portal. |
| Rollback | Click any past version to restore its definition_json as the current content. |
| Versions available | Last 10 version snapshots stored per ad. |
| API access | GET /v1/portal/ads/:id/versions/:version returns the full snapshot JSON. |
SDK Integration
The SDK fetches and renders ads automatically using placement components, or you can fetch them manually and render with your own UI. Both patterns are supported across the React Native and Web SDKs.
Declare placements in Swake.init() and drop <AdPlacement> components anywhere in your UI. The SDK handles fetching, caching, and rendering.
Call Swake.getAd(placementId) directly and render ad.definitionJson yourself for full UI control.
React Native
Install the React Native SDK and declare your ad placements during initialisation.
Auto-serving (recommended)
import Swake from '@swake/react-native';
// In Swake.init() — enable ad auto-serving
Swake.init({
apiKey: 'ep_live_...',
ads: {
enabled: true,
// Define placements
placements: ['home-banner', 'dashboard-card'],
},
});
Drop <AdPlacement> anywhere in your component tree. It renders the highest-priority active ad for the given placement ID.
import { AdPlacement } from '@swake/react-native';
// Renders the highest-priority active ad for this placement
<AdPlacement
id="home-banner"
onImpression={(ad) => console.log('Ad shown:', ad.id)}
onCtaTap={(ad, cta) => console.log('CTA tapped:', cta.action)}
onDismiss={(ad) => console.log('Ad dismissed:', ad.id)}
/>
Manual fetch
Fetch the ad imperatively and render the definitionJson with your own components.
const ad = await Swake.getAd('home-banner');
if (ad) {
// Render ad.definitionJson yourself
console.log(ad.definitionJson.type); // 'card'
await Swake.trackImpression(ad.id);
}
Web SDK
Use @swake/web to mount placements in any container element or fetch ads manually.
import Swake from '@swake/web';
// Mount an ad placement
Swake.mountAdPlacement('home-banner', {
container: document.getElementById('ad-container'),
onImpression: (ad) => analytics.track('ad_shown', { adId: ad.id }),
onCtaClick: (ad, cta) => {
if (cta.action === 'navigate') router.push(cta.url);
},
});
// Or fetch manually
const ad = await Swake.getAd('home-banner', {
deviceType: 'desktop',
userSegment: ['premium-users'],
});
The Web SDK renders ads inside a Shadow DOM to prevent CSS leakage. HTML ads have access to the full DOM inside their shadow root.
API Reference
SDK endpoints authenticate via X-API-Key. Portal endpoints require a valid JWT session cookie.
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /v1/sdk/ads/:placementId | Serve highest-priority ad for placement | SDK |
| GET | /v1/portal/projects/:projectId/ads | List project ads | Portal |
| POST | /v1/portal/projects/:projectId/ads | Create ad | Portal |
| GET | /v1/portal/ads/:id | Ad detail with definition + version history | Portal |
| PATCH | /v1/portal/ads/:id | Update ad or toggle active | Portal |
| POST | /v1/portal/ads/:id/duplicate | Duplicate ad (inactive copy) | Portal |
| DELETE | /v1/portal/ads/:id | Soft-delete ad | Portal |
| GET | /v1/portal/ads/:id/versions/:version | Get specific version snapshot | Portal |
GET /v1/sdk/ads/:placementId — query parameters
| Parameter | Required | Values | Description |
|---|---|---|---|
device_type | Yes | mobile | tablet | desktop | Device type of the requesting client. |
user_segment | No | comma-separated segment IDs | User segment IDs for targeting matching. |
ab_variant | No | any string | A/B variant key to select the matching ad variant. |
HTTP 200 — returns ad definition + metadata with Cache-Control and ETag headers. Supports If-None-Match conditional requests for efficient polling.
HTTP 204 — no ad matched the targeting rules. Render nothing.
Rate limit: 100 requests/min per API key (separate bucket from other SDK endpoints).