Integration guide ยท For newsrooms and news agencies
Send your articles to a single secure endpoint and they reach readers in the Fanvaiy mobile app within seconds, on your own agency page, in the discovery feed, and in the stories strip. This page explains exactly how to connect.
Pushing news is invite-only. We review every newsroom before enabling access, which keeps the app trustworthy for readers. You can't self-register; reach out and we'll get you set up.
Once approved, we give you two things: an agency key
(e.g. partner_yourname)
and a private signing secret. Keep the secret safe, as it's how we know a push is really from you.
Your CMS makes a signed POST request to our endpoint whenever you publish (or update) an article.
We verify the signature and store the article for 15 days, then it auto-expires. Nothing is kept long-term.
Your article reaches readers in the app feed, stories strip, and on your dedicated agency page, branded as yours.
Articles are retained for 15 days. To keep your news visible, simply keep pushing new stories, and older ones roll off automatically.
POST https://api.fanvaiy.com/api/v1/partner/news
Every request must carry these three headers:
| Header | Value |
|---|---|
| X-Partner-Agency | Your agency key (e.g. partner_yourname) |
| X-Partner-Timestamp | Current Unix time in seconds (must be within 5 minutes of now) |
| X-Partner-Signature | HMAC-SHA256 of "{timestamp}.{rawBody}" using your secret (hex) |
{
"externalId": "12345", // your own id (required), re-send to update
"title": "Breaking: ...", // required
"publishedAt": "2026-06-11T09:00:00Z", // ISO 8601 (required)
"summary": "Short standfirst",
"body": "<p>Full article HTML</p>", // body OR redirectUrl required
"redirectUrl": "https://youragency.mv/12345",
"image": "https://.../cover.jpg",
"imageCaption": "Photo: ...",
"videoUrl": "https://.../clip.mp4",
"isFeatured": false,
"author": "Reporter name",
"audio": { "url": "https://.../tts.mp3", "duration": 120 }
}
Compute the signature over the exact raw request body you send. Here it is in Node.js and PHP.
Node.js
const crypto = require("crypto");
const secret = process.env.FANVAIY_SECRET; // given to you on approval
const body = JSON.stringify({ externalId: "12345", title: "Breaking: ...",
publishedAt: new Date().toISOString(),
redirectUrl: "https://youragency.mv/12345" });
const ts = Math.floor(Date.now() / 1000).toString();
const sig = crypto.createHmac("sha256", secret).update(`${ts}.${body}`).digest("hex");
await fetch("https://api.fanvaiy.com/api/v1/partner/news", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Partner-Agency": "partner_yourname",
"X-Partner-Timestamp": ts,
"X-Partner-Signature": sig,
},
body,
});
PHP
$secret = getenv("FANVAIY_SECRET");
$body = json_encode([
"externalId" => "12345",
"title" => "Breaking: ...",
"publishedAt" => date("c"),
"redirectUrl" => "https://youragency.mv/12345",
]);
$ts = (string) time();
$sig = hash_hmac("sha256", "$ts.$body", $secret);
$ch = curl_init("https://api.fanvaiy.com/api/v1/partner/news");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Partner-Agency: partner_yourname",
"X-Partner-Timestamp: $ts",
"X-Partner-Signature: $sig",
],
]);
curl_exec($ch);
| 204 | Accepted, the article is live |
| 400 | Missing or stale timestamp |
| 401 | Signature did not match |
| 404 | Unknown or not-yet-approved agency |
| 422 | Payload failed validation |
To pull a story before its 15 days are up, send the same signed request with the article's
externalId and an event of "deleted".
{
"externalId": "12345",
"event": "deleted"
}