Send events from your server
Some things happen on your server, not on the phone: a subscription renews, a webhook arrives, a job finishes. Send them to Apparent over HTTP with the same batch the SDKs use. Nothing to install.
The write key is safe on your server
The key (k) only lets you add events. It already ships inside your app, so it is not a secret, and keeping it in your server's settings is fine. Find it on the Connect page.
Send an event
import { randomUUID } from "node:crypto";
export async function track(userId, name, props = {}) {
const now = Date.now();
const res = await fetch("https://in.apparent.fyi/i", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
a: process.env.APPARENT_APP,
k: process.env.APPARENT_KEY,
v: "server-0.1.0",
sa: now,
d: { platform: "ios", os: "18.0", app: "1.0.6", locale: "en-US",
anon: userId, user: userId },
e: [{ n: name, t: now, s: "server",
i: randomUUID().replaceAll("-", ""), p: props }],
}),
});
if (!res.ok) throw new Error(`Apparent answered ${res.status}`);
}import os, time, uuid, requests
def track(user_id, name, props=None):
now = int(time.time() * 1000)
r = requests.post("https://in.apparent.fyi/i", timeout=10, json={
"a": os.environ["APPARENT_APP"],
"k": os.environ["APPARENT_KEY"],
"v": "server-0.1.0",
"sa": now,
"d": {"platform": "ios", "os": "18.0", "app": "1.0.6",
"locale": "en-US", "anon": user_id, "user": user_id},
"e": [{"n": name, "t": now, "s": "server",
"i": uuid.uuid4().hex, "p": props or {}}],
})
r.raise_for_status()curl https://in.apparent.fyi/i -H 'content-type: application/json' -d '{
"a": "<app id>", "k": "<write key>", "v": "server-0.1.0",
"sa": '"$(date +%s)000"',
"d": { "platform": "ios", "os": "18.0", "app": "1.0.6",
"locale": "en-US", "anon": "user_8812", "user": "user_8812" },
"e": [{ "n": "subscription_renewed", "t": '"$(date +%s)000"',
"s": "server", "i": "'"$(uuidgen | tr -d - | tr A-Z a-z)"'",
"p": { "plan": "yearly" } }]
}'A good answer is {"ok":true,"accepted":1,...}. 401 means the app id or key is wrong. On 408, 429 or 5xx, wait and send the same batch again (same i). On 401 or other 4xx, stop.
Who it was
- A signed-in person: put your own account id in
user(oruon one event). Apparent counts everyone with the same user id as one person, across all their phones and reinstalls, so this event joins the same person as their app events. Use the same id you pass toidentify()in the app. - Send the user id as
anontoo. People are found throughanon, so one shared value for every user (like"server") would merge them all into the first person. Withanonequal touserthe event lands on that person, and on the same person as their phone events. - An event with no user id counts as its own person, one per
anon. - A server event is not someone opening the app, so it doesn't make a person "active" on its own. Their app visits do.
The event id
i is a random id for each event (32 hex characters, up to 80). Send the same one when you retry: the server keeps one copy per id, so sending twice is safe. For a webhook, a hash of the webhook's own id works well, so a repeated delivery never counts twice.
All fields and limits are in the Intake API.