Integration guides
Build a real public campaign directory
Read the existing discovery endpoint from your backend, expose a narrow response and link to the real participant journey.
Before you start
- A server runtime: Node 20+ with fetch, Python 3 with urllib, or PHP with cURL and JSON support.
- The public Server origin and an actually supported campaign country. No secret account key is needed for this read.
- A frontend able to render text safely and handle empty or unavailable results.
Read one explicit campaign type
GET /api/public/homepage/discovery supports type=all|ticketing|voting|donation, country, q, location, status, date, sort, page and limit. This example requests Ghana ticketing; it does not enable Ghana payments for other campaigns.
Use the HTTPS origin as a fixed backend destination, not a URL supplied by the visitor. Keep timeouts and inspect both HTTP status and success.
export async function readPublicCampaigns() {
const url = new URL('/api/public/homepage/discovery', 'https://api.sparkglim.net');
url.search = new URLSearchParams({ type: 'ticketing', country: 'GH', sort: 'latest', page: '1', limit: '12' }).toString();
const response = await fetch(url, { headers: { accept: 'application/json' }, signal: AbortSignal.timeout(12000) });
const payload = await response.json();
if (!response.ok || payload.success !== true || !payload.data) throw new Error(payload.error || 'Discovery unavailable');
return payload.data;
}import json
import urllib.parse
import urllib.request
def read_public_campaigns():
query = urllib.parse.urlencode({"type": "ticketing", "country": "GH", "sort": "latest", "page": "1", "limit": "12"})
request = urllib.request.Request("https://api.sparkglim.net/api/public/homepage/discovery?" + query, headers={"Accept": "application/json"})
with urllib.request.urlopen(request, timeout=12) as response:
payload = json.load(response)
if payload.get("success") is not True or not isinstance(payload.get("data"), dict):
raise RuntimeError("Discovery unavailable")
return payload["data"]<?php
function read_public_campaigns(): array {
$query = http_build_query(['type'=>'ticketing','country'=>'GH','sort'=>'latest','page'=>1,'limit'=>12], '', '&', PHP_QUERY_RFC3986);
$handle = curl_init('https://api.sparkglim.net/api/public/homepage/discovery?' . $query);
curl_setopt_array($handle, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>12, CURLOPT_HTTPHEADER=>['Accept: application/json']]);
$raw = curl_exec($handle);
$status = curl_getinfo($handle, CURLINFO_RESPONSE_CODE);
$error = curl_error($handle);
curl_close($handle);
if ($raw === false || $status < 200 || $status >= 300) throw new RuntimeException($error ?: 'Discovery unavailable');
$payload = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
if (($payload['success'] ?? false) !== true || !is_array($payload['data'] ?? null)) throw new RuntimeException('Discovery unavailable');
return $payload['data'];
}Add your own narrow read proxy
Save the TypeScript read function in read-public-campaigns.ts and call it from a server route. The following example is a Next.js route handler; place it in your own application’s intended API folder.
Use short caching for successful public data and no-store on errors. A backend proxy is not an open forwarding proxy and must not accept arbitrary upstream origins.
import { readPublicCampaigns } from './read-public-campaigns';
export async function GET() {
try {
const data = await readPublicCampaigns();
return Response.json({ success: true, data }, { headers: { 'Cache-Control': 'public, max-age=60' } });
} catch {
return Response.json({ success: false, error: 'Discovery unavailable' }, { status: 503, headers: { 'Cache-Control': 'no-store' } });
}
}Render actual records and preserve destination rules
Read data.entries; each record contains a campaign projection plus relevant lifecycle and availability fields. Treat data.available=false, zero entries and service failure as different states; never substitute invented events.
Render titles as text and use each actual campaign type and slug to link to the corresponding supported ticketing, voting or donation route. Preserve country and language; do not iframe checkout or private pages.
Check your integration
- Verify 200 data, empty results and 503 handling without creating any transaction.
- Change the type filter and confirm voting or donation campaigns never appear as ticketing events.
- Paginate with the returned data.pagination and render original currency, dates and permitted visibility.