Web / JavaScript Integration
The simplest integration. Use fetch() to call the Scrya API directly from your browser game.
Setup
// scrya.js — minimal client
const SCRYA_API = 'https://api.scrya.com/api/ads';
const API_KEY = 'sk_live_your_key_here';
async function scryaPost(endpoint, body) {
const res = await fetch(SCRYA_API + endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + API_KEY,
},
body: JSON.stringify(body),
});
if (res.status === 204) return null; // No ad matched
if (!res.ok) throw new Error('Scrya API error: ' + res.status);
return res.json();
}
async function scryaGet(endpoint) {
const res = await fetch(SCRYA_API + endpoint, {
headers: { 'Authorization': 'Bearer ' + API_KEY },
});
if (!res.ok) throw new Error('Scrya API error: ' + res.status);
return res.json();
} Step 1: Match an Ad
async function matchAd(gameId, playerId, narrative, moodTags) {
return scryaPost('/match', {
game_id: gameId,
user_id: playerId,
player_geo: { country_code: 'US' },
game_context: {
theme_id: '3',
category: 'simulation',
narrative: narrative,
mood_tags: moodTags,
},
session_id: crypto.randomUUID(),
});
} Step 2: Display the Hook
async function generateScene(gameId, playerId) {
const narrative = 'The president faces a difficult decision...';
const match = await matchAd(gameId, playerId, narrative, ['tense']);
if (match) {
// Inject hook into dialogue
dialogueBox.addLine(match.narrative_hook);
// Apply visual bias if your renderer supports it
if (match.biased_pipeline) {
renderer.setLighting(match.biased_pipeline.lighting);
renderer.setEffects(match.biased_pipeline.effects);
}
// Record the impression
await recordImpression(match, gameId);
}
} Step 3: Record the Impression
async function recordImpression(match, gameId) {
if (!match) return;
await scryaPost('/impressions', {
campaign_id: match.campaign_id,
creative_id: match.creative_id,
game_id: gameId,
});
} TypeScript Types
interface AdMatchRequest {
game_id: string;
user_id: string;
player_geo: { country_code: string; state_code?: string; city_name?: string };
game_context: {
theme_id?: string;
category?: string;
narrative?: string;
mood_tags?: string[];
};
session_id?: string;
}
interface AdMatchResponse {
campaign_id: string;
creative_id: string;
narrative_hook: string;
creative_type: 'narrative_hook' | 'product_mention' | 'location_brand' | 'character_reference';
brand_name: string;
cpm_bid_cents: number;
biased_pipeline?: {
lighting?: string;
effects?: string;
location?: string;
camera?: string;
};
} Check Your Revenue
// Check total earnings
const revenue = await scryaGet('/creator-revenue');
console.log('Total earnings:', revenue);
// Per-game breakdown
const gameRevenue = await scryaGet('/creator-revenue/' + gameId);
console.log('Game earnings:', gameRevenue); React Example
function useScryaAd(gameId, playerId, narrative, moods) {
const [match, setMatch] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
matchAd(gameId, playerId, narrative, moods)
.then(setMatch)
.catch(() => setMatch(null))
.finally(() => setLoading(false));
}, [gameId, playerId, narrative]);
useEffect(() => {
if (match) recordImpression(match, gameId);
}, [match]);
return { match, loading };
}