PilotFoxDocs

Server-side ingest

Send events and identify users from your own server with an API key that has the ingest:write permission.

The tracking script records what happens in the browser. Some things happen elsewhere: a payment webhook arrives, a subscription renews in a background job, a user signs up through your API. Server-side ingest lets your backend send those events to PilotFox, and tell it who a visitor is, through two endpoints:

EndpointWhat it does
POST /api/v1/ingest/eventsRecords up to 500 events per request.
POST /api/v1/ingest/identifyLinks a visitor to your user id, with traits. The way to give PilotFox the email it uses to link payments.

Server events go through the same processing as the browser's: the site's exclusions (IP addresses, countries and the like) and bot blocking apply, the location comes from the IP address, and the browser, system and device from the user agent. They appear in the same reports.

When to send from the server

  • Events the browser never sees: webhooks from your payment provider, renewals and cancellations, background jobs, emails opened, actions taken through your API or a mobile backend.
  • Events that must not be lost or faked: a browser can block the script or send anything it likes; your server sends what really happened.
  • Identifying users with an email you trust: only an email trait sent from your server links payments; one set in the browser does not.

For everything that happens on your pages (pageviews, clicks, custom events triggered by the visitor), keep using the tracking script: it needs no key and knows the visitor without your help.

The API key

Create an API key with the ingest:write permission. It is one of the two key-only permissions: no role holds it, only organization owners and admins can grant it, and the ingest endpoints refuse every credential but an API key (401), so no browser can call them.

  • A key without a site list can send to every site of its organization. A key limited to some sites can send only to those: events for another site are rejected one by one (forbidden_site), an identify is 404.
  • Give ingest its own key, with ingest:write only, and keep it in your server's secrets. Never put it in a web page or an app.
curl -X POST "https://pilotfox.app/api/v1/ingest/events" \
  -H "Authorization: Bearer $PILOTFOX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"events": [{"site": "SITE_ID", "type": "event", "name": "subscription_renewed", "anonymous_id": "customer_8412", "uid": "user_8412", "props": {"plan": "pro"}}]}'

Limits

LimitValue
Requests60 a minute per key (the write budget), then 429 with Retry-After
Events per request1 to 500
Request body1 MiB
Event time (ts)from 7 days ago to 5 minutes ahead

That is up to 30 000 events a minute per key. Send events in batches rather than one request each.

Matching server events to visitors

Each event belongs to a visitor, and PilotFox has no cookie to recognise one: in the browser, it derives the visitor from the IP address and user agent (see Concepts). On the server, you choose with optional fields of each event. An event needs none of them; only an identify must name its visitor.

You sendThe event belongs toUse it for
ip and user_agent of the visitor's browserThe same visitor as that browser's events, and their current session if the browser was active in the last 30 minutes.Events caused by a visitor's request to your backend: take the address and User-Agent of that request.
anonymous_idA visitor of its own, the same for every event with this id on the site (and on any day), never one of the browser's.Server-side activity with your own stable id, such as a customer or account id, when there is no browser request to take an address from.
neitherA visitor made of your server's own address and no user agent: every such event of that server falls under one visitor, located where your server is.Rarely what you want.

anonymous_id wins when both are sent (an empty anonymous_id counts as none). An ip without a user_agent names the visitor of that address with no user agent, and a user_agent without an ip uses your server's address. Whatever you choose, add uid (your user id) when you know the user: reports then count the event for that user, whichever visitor it belongs to.

To pass the visitor's address, take it from the connection of the browser's request, or, behind a load balancer or CDN, from the header it sets for the client address (such as X-Forwarded-For as your proxy writes it, or CF-Connecting-IP), never from a header the visitor can forge. Pass the User-Agent header unchanged.

On a site whose visitor identity is Daily rotating (Site settings → Privacy & identity), the visitor named by an IP address and user agent changes every day at midnight UTC, as in the browser; one named by anonymous_id does not.

Privacy

The ip and user_agent you send are used as the browser's are: to derive the visitor, the location and the device. PilotFox does not store the IP address, and keeps neither the ip, the user_agent nor the uid of a server request in its logs.

The tracking script honours a visitor's opt-out in the browser, and the Global Privacy Control signal unless the site turns that off. Your server's requests carry no such signal: if you send events about visitors who opted out, respect their choice in your own code. See Opt-out.

On this page