Siyaqi
One script. One placement. Verify the serve call.
Paste the hosted script. It fetches the ad, renders the text, and handles click tracking. Copy it from the publisher portal after you create a placement.
If no campaigns are live, the unit stays empty — that is expected.
Quickstart
Before the script
- Sign up as a publisher.
- Create an application.
- Create a placement and add UI context.
- Copy the snippet the portal emits.
If you skip the placement id, nothing useful can serve.
Recommended embed (hosted script)
Shape matches the portal snippet. src is this site’s /sdk/siyaqi.js. Replace data-placement with the portal id. data-serve is the serve API base with no trailing slash.
<!-- Siyaqi text ad unit — docs: /docs --> <div id="siyaqi-ad"></div> <script src="https://www.siyaqi.app/sdk/siyaqi.js" data-serve="https://siyaqi-prod.fly.dev" data-placement="plc_YOUR_ID" data-target="siyaqi-ad" async></script>
| Attribute | Required | Meaning |
|---|---|---|
| data-serve | yes | Serve API base URL, no trailing slash |
| data-placement | yes | Placement id from the publisher portal |
| data-target | no | DOM id to fill (default siyaqi-ad) |
Verify it worked
- Load the page that contains the node.
- In the network panel, find
GET https://siyaqi-prod.fly.dev/v1/ads/serve?placementId=… - Ad JSON → placement is live and a campaign was eligible.
- 404 or empty → if no campaigns are live, the unit stays empty. The snippet can still be correct. The serve API may return
404 No eligible creatives. - Optional liveness:
GET https://siyaqi-prod.fly.dev/health
No API key is required for serve. API keys are for the portal API — listing, pausing, and inspecting campaigns.
Localhost
data-serve="http://localhost:8080" src="http://localhost:3000/sdk/siyaqi.js"
CORS: serve must allow the web origin. Script file: /sdk/siyaqi.js.
Contract
| Method | Path | Role |
|---|---|---|
| GET | /v1/ads/serve?placementId= | Pick creative + buffer impression |
| POST | /v1/ads/click | Buffer click |
| POST | /v1/ads/impression | Optional explicit impression |
| GET | /health | Liveness |
| GET | /v1/openapi.json | OpenAPI document (YAML body) |
Shared types: @siyaqi/shared (ServedAd, ClickEventInput, API). Serve OpenAPI.
Troubleshooting
| Symptom | Check |
|---|---|
| No network call | Script blocked, wrong page, or snippet not shipped. |
| Call fires, empty or 404 | If no campaigns are live, the unit stays empty. Keep the placement live. The serve API may return 404 No eligible creatives. |
| Wrong placement | data-placement does not match the portal id. |
| CORS / local vs prod | data-serve host mismatch (localhost vs production). |
| Layout shift | Reserve min-height on #siyaqi-ad in your CSS. |
Optional: vanilla fetch / React
After the hosted script works, you can call serve yourself. This is optional — not the recommended integrate path.
Vanilla JS
async function loadSiyaqiAd(targetId, placementId) {
const res = await fetch(
`https://siyaqi-prod.fly.dev/v1/ads/serve?placementId=${placementId}`
);
if (!res.ok) return;
const { ad } = await res.json();
if (!ad) return;
const el = document.getElementById(targetId);
el.innerHTML = `<span>${ad.text}</span> <a href="#" data-trk="${ad.trackingId}">${ad.cta}</a>`;
}React
import { useEffect, useState } from "react";
function useSiyaqiAd(placementId) {
const [ad, setAd] = useState(null);
useEffect(() => {
fetch(`https://siyaqi-prod.fly.dev/v1/ads/serve?placementId=${placementId}`)
.then((r) => r.ok ? r.json() : null)
.then((data) => data?.ad && setAd(data.ad));
}, []);
return ad;
}
export function SiyaqiAd({ placementId }) {
const ad = useSiyaqiAd(placementId);
if (!ad) return null;
return (
<span>
{ad.text}{" "}
<a href="#" data-trk={ad.trackingId}>{ad.cta}</a>
</span>
);
}Sign in to the publisher portal, create an app and placement, then copy the snippet (language tabs included) from Applications.
Further surfaces
React Native, MCP, CPA pixels, and the portal API are optional next steps. Start with the hosted script.
CPA conversions
Fire a conversion pixel after a purchase or signup. Start with CPM or CPC. Conversion tracking is available when you run CPA.
<!-- Siyaqi conversion pixel — fire after purchase --> <script src="https://www.siyaqi.app/sdk/convert.js" data-serve="https://siyaqi-prod.fly.dev" data-campaign="CAMPAIGN_ID" data-value="2999" async></script>
The data-value attribute is the conversion value in minor currency units (cents). Attribution window defaults to 7 days (configurable via CPA_ATTRIBUTION_DAYS).
Portal API
Org-scoped REST API for managing campaigns programmatically. Create an API key in the Developers hub. For advertisers and agents that manage campaigns.
# List campaigns
curl https://www.siyaqi.app/api/v1/campaigns \
-H "Authorization: Bearer sk_live_YOUR_SECRET"
# Pause a campaign
curl -X POST https://www.siyaqi.app/api/v1/campaigns/CAMPAIGN_ID \
-H "Authorization: Bearer sk_live_YOUR_SECRET" \
-H "Content-Type: application/json" \
-d '{"action":"pause"}'
# Wallet balance
curl https://www.siyaqi.app/api/v1/wallet \
-H "Authorization: Bearer sk_live_YOUR_SECRET"See the full OpenAPI spec for all endpoints. Keys are hashed at creation — copy the secret immediately.
MCP (Model Context Protocol)
Connect AI agents (Cursor, Claude Desktop, etc.) to manage campaigns via natural language.
// .cursor/mcp.json
{
"mcpServers": {
"siyaqi": {
"command": "npx",
"args": ["-y", "@siyaqi/mcp"],
"env": {
"SIYAQI_API_KEY": "sk_live_YOUR_SECRET",
"SIYAQI_API_BASE": "https://www.siyaqi.app"
}
}
}
}Once connected, your agent can list, pause, activate, and inspect campaigns — the same actions as the portal API.
Agent skills
Three SKILL.md files for Cursor, Claude Code, and Codex: estimate publisher revenue, score a UI placement, then integrate the unit. No API key for the first two.
npx skills add AmjedMVP/Siyaqi # or one file curl -o SKILL.md https://www.siyaqi.app/skills/integrate-siyaqi-ads/SKILL.md
Full list: /skills. If your agent should manage campaigns, use MCP above — not the publisher integrate skill.
React Native / Expo
Use the @siyaqi/react-native package when you call the same /v1 serve contract. Start on web with the snippet; native can use the same endpoint.
npm install @siyaqi/react-native
import { useSiyaqiAd } from "@siyaqi/react-native";
import { Text } from "react-native";
export function SiyaqiAdUnit({ placementId }) {
const { ad, isLoading } = useSiyaqiAd({
placementId,
serveUrl: "https://siyaqi-prod.fly.dev",
});
if (isLoading || !ad) return null;
return <Text>{ad.text} — {ad.cta}</Text>;
}Swift (iOS) and Kotlin (Android) native SDKs are on the roadmap — use the REST endpoint directly for now: GET /v1/ads/serve?placementId=ID.
OpenAPI
Two OpenAPI specs are available:
- /api/v1/openapi — Portal API (campaigns, wallet, keys). Requires Bearer token.
- https://siyaqi-prod.fly.dev/v1/openapi.json — Ad serve API (no auth). Includes
/v1/ads/serve,/v1/ads/click, and conversion endpoints. Response body is YAML.