Documentation
Everything here works the same way regardless of what you're building — a static site, a React app, or a backend service. Pick a project's API key from the Projects tab and follow the steps below.
Quickstart
Add the SDK to your page, once, near the top of your HTML:
<script src="https://obsvoid.xyz/obsvoid.js"></script>Then initialize it and send your first pageview:
<script>
const obsvoid = new Obsvoid({
endpoint: "https://api.obsvoid.xyz/e",
apiKey: "YOUR_API_KEY",
});
obsvoid.pageview();
window.obsvoid = obsvoid; // so other scripts on the page can reach it
</script>Events are batched and sent every few seconds (or immediately if 20+ pile up, or when the page is closed) — a short delay before something shows up in Analytics is normal, not a bug.
Custom events
Call capture with an event name and any properties you want attached:
obsvoid.capture("signed_up", { plan: "free" });
obsvoid.capture("checkout_completed", { amount: 49.99, currency: "USD" });Single-page apps
Client-side navigation (React Router, Next.js, Vue Router, etc.) never triggers a full page load, so pageview() firing once on the initial load only counts your very first view. Call it again on every route change. Next.js Pages Router example:
import { useEffect } from "react";
import { useRouter } from "next/router";
function MyApp({ Component, pageProps }) {
const router = useRouter();
useEffect(() => {
const trackPageview = () => window.obsvoid?.pageview();
router.events.on("routeChangeComplete", trackPageview);
return () => router.events.off("routeChangeComplete", trackPageview);
}, [router.events]);
return <Component {...pageProps} />;
}For the App Router, React Router, or anything else, hook into whichever navigation/effect fires after a route change completes and call window.obsvoid?.pageview() there — the principle is the same everywhere.
Server-side events
No SDK needed on a backend — just POST directly, in whatever language you're already using:
curl -X POST https://api.obsvoid.xyz/e \
-H "X-Obsvoid-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"event": "order_completed", "distinct_id": "user_123", "properties": {"amount": 49.99}}'SDK options
| Option | Required | Description |
|---|---|---|
endpoint | Yes | Always https://api.obsvoid.xyz/e |
apiKey | Yes | Your project's API key from the Projects tab |
distinctId | No | Override the visitor ID. By default the SDK generates a random one and persists it in localStorage for you |
flushInterval | No | How often queued events are sent, in milliseconds (default 5000) |
A session ID is also generated automatically and stored in sessionStorage, so events from the same browser tab get grouped into one session for bounce rate/duration stats.
API reference
POST https://api.obsvoid.xyz/e
Headers:
X-Obsvoid-Key— required, your project's API keyContent-Type: application/json
Body — a single event:
{
"event": "signed_up",
"distinct_id": "user_123",
"properties": { "plan": "free" }
}Or a batch — this is what the SDK sends under the hood, useful if you're integrating from a backend that wants to send several at once:
{
"events": [
{ "event": "signed_up", "distinct_id": "user_123" },
{ "event": "clicked_upgrade", "distinct_id": "user_123" }
]
}Fields:
| Field | Description |
|---|---|
event | Event name. Required unless message is set instead. |
distinct_id | Stable identifier for the visitor/user sending this event. |
session_id | Optional — groups events into a session. |
url | Optional — the page URL this event happened on. |
type | Defaults to "event". Use "log" for backend log lines. |
level / message | For log-style entries — e.g. level: "error". |
properties | Any custom data as a JSON object. |
timestamp | Optional ISO 8601 string. Defaults to when the server receives it. |
Response on success:
{ "accepted": 1 }