Documentation / Server-Side Tracking
Server-Side Tracking
Send pageviews and custom events directly from your backend without a browser script. Useful for server-rendered pages, API-only backends, and environments where JavaScript cannot run.
Business plan required
Server-side tracking is available on the Business plan and above. Your API token must be created with the collect:write ability.
Privacy-first by design
The privacy guarantees mirror the JavaScript beacon pipeline. The ip field is hashed with a daily-rotating salt into an anonymous visitor fingerprint and used for geo lookup within the request, then discarded. The raw IP is never written to the database. The user_agent string is parsed for browser and device detection within the request, then discarded. The raw string is never stored. URLs are stripped of query strings and fragments before storage. Visitor identity is a daily-rotating SHA-256 hash, making re-identification across days cryptographically infeasible.
Unlike the browser script, the server-side API cannot automatically detect Global Privacy Control or Do Not Track signals from the end user. If your visitors use GPC or DNT, it is your responsibility to check those signals in your own request handling and omit the collect call accordingly.
Authentication
All collect endpoints require a Sanctum API token passed as a Bearer header. The token must be created with the collect:write ability selected.
Authorization: Bearer <YOUR_API_TOKEN>
Generate a token with the correct ability from Settings / API tokens.
Record a pageview
Send this request each time a page is served to a visitor.
https://ghostlyx.com/api/v1/collect/pageview
Request body (JSON)
tracking_id
string
Your site tracking ID (format: gx_XXXXXXXXXXXX). Found in your site settings.
required
url
string
Full URL of the page being viewed. Query strings and fragments are stripped server-side.
required
pathname
string
Path component of the URL, e.g. /blog/my-post.
required
referrer
string
Referrer URL, if known. Query strings and fragments are stripped.
optional
ip
string
Visitor IP. Hashed with a daily salt into an anonymous visitor fingerprint and used for geo lookup, then discarded. The raw IP is never written to the database. Defaults to the token-holder request IP if omitted.
optional
user_agent
string
Visitor User-Agent. Parsed for browser and device detection, then discarded. The raw string is never stored.
optional
utm_source
string
UTM source parameter.
optional
utm_medium
string
UTM medium parameter.
optional
utm_campaign
string
UTM campaign parameter.
optional
utm_content
string
UTM content parameter.
optional
utm_term
string
UTM term parameter.
optional
timestamp
integer
Unix timestamp (seconds) for the pageview. Defaults to current server time if omitted.
optional
Example (curl)
curl -X POST https://ghostlyx.com/api/v1/collect/pageview \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tracking_id": "gx_abc123",
"url": "https://example.com/blog/my-post",
"pathname": "/blog/my-post",
"referrer": "https://google.com",
"ip": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...",
"utm_source": "newsletter"
}'
Example (PHP / Laravel)
use Illuminate\Support\Facades\Http;
Http::withToken(config('services.ghostlyx.token'))
->post('https://ghostlyx.com/api/v1/collect/pageview', [
'tracking_id' => config('services.ghostlyx.tracking_id'),
'url' => $request->url(),
'pathname' => $request->path(),
'referrer' => $request->header('Referer'),
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
]);
Response
200
{"ok": true}
Pageview recorded successfully.
200
{"ok": true, "skipped": "bot"}
Request was identified as bot traffic and silently dropped.
402
{"ok": false, "reason": "cap_exceeded"}
Monthly pageview cap reached.
403
Token missing the collect:write ability, or account is not on the Business plan.
404
{"ok": false}
Tracking ID not found.
422
Validation failed. Check required fields.
Record a custom event
Send this request when a named business event occurs in your backend, such as a signup, a purchase, or a subscription change.
https://ghostlyx.com/api/v1/collect/event
Request body (JSON)
tracking_id
string
Your site tracking ID (format: gx_XXXXXXXXXXXX).
required
name
string
Event name, e.g. "Signup" or "Purchase". Max 255 characters.
required
url
string
Full URL of the page the event occurred on. Accepted for forward compatibility. Only the pathname is stored.
required
pathname
string
Path component of the URL.
required
props
object
Flat key-value metadata. Values must be strings, numbers, or booleans. Max 4 KB encoded. Do not include personal data.
optional
ip
string
Visitor IP. Hashed with a daily salt into an anonymous visitor fingerprint and used for geo lookup, then discarded. The raw IP is never written to the database.
optional
user_agent
string
Visitor User-Agent. Parsed for browser and device detection, then discarded. The raw string is never stored.
optional
timestamp
integer
Unix timestamp (seconds) for when the event occurred. Defaults to current server time.
optional
Example (curl)
curl -X POST https://ghostlyx.com/api/v1/collect/event \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"tracking_id": "gx_abc123",
"name": "Signup",
"url": "https://example.com/register",
"pathname": "/register",
"ip": "203.0.113.42",
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ...",
"props": {
"plan": "growth",
"referral": "product-hunt"
}
}'
Example (PHP / Laravel)
use Illuminate\Support\Facades\Http;
Http::withToken(config('services.ghostlyx.token'))
->post('https://ghostlyx.com/api/v1/collect/event', [
'tracking_id' => config('services.ghostlyx.tracking_id'),
'name' => 'Signup',
'url' => $request->url(),
'pathname' => $request->path(),
'ip' => $request->ip(),
'user_agent' => $request->userAgent(),
'props' => [
'plan' => $user->plan,
],
]);
Response
200
{"ok": true}
Event recorded successfully.
200
{"ok": true, "skipped": "bot"}
Request was identified as bot traffic and silently dropped.
402
{"ok": false, "reason": "cap_exceeded"}
Monthly pageview cap reached.
403
Token missing the collect:write ability, or account is not on the Business plan.
404
{"ok": false}
Tracking ID not found.
422
Validation failed. Check required fields.
Best practices
Always pass the real visitor IP
Pass the IP from the original HTTP request, not your server's outbound IP. In Laravel you can read it via $request->ip(). If you sit behind a proxy, ensure TRUSTED_PROXIES is configured correctly so Laravel resolves the visitor IP rather than the proxy IP.
Pass the visitor User-Agent
The user_agent field enables accurate browser, OS, and device breakdowns in your dashboard. Without it, all server-side hits will show as "Other" in the device report.
Fire events close to the action
Record events synchronously at the point in your code where the action succeeds. Avoid batching or delaying, as this affects real-time data in your dashboard.
Never include personal data in event names or props
Do not pass email addresses, names, database IDs, or any identifier that can be linked to a real person. This keeps your implementation GDPR and CCPA compliant without extra work.
Use Fire-and-forget for pageviews
Pageview collection should not block your response. Wrap HTTP calls in a queued job or use async HTTP (Http::async()) to avoid adding latency to page loads.
Store credentials in environment variables
Keep your API token and tracking ID out of source code. Use GHOSTLYX_TOKEN and GHOSTLYX_TRACKING_ID in your .env file and read them via config().
Ready to start sending server-side events?