Alertalytix Documentation
This guide walks you through embedding the browser collector, managing consent, and fetching automated decisions. After creating a domain, copy the public key and follow the steps below.
1. Embed the public key
The key appears on the domain detail screen in the dashboard. Add it as a meta tag so the collector can discover it.
<meta name="alx-public-key" content="YOUR_PUBLIC_KEY">
2. Load the browser collector
Include the script below. It uses an iframe bridge to post to /collect/v1 from the same origin and automatically sends a pageview.
<meta name="alx-public-key" content="YOUR_PUBLIC_KEY">
<script src="https://collector.alertalytix.cloud/v1/alertalytix.js"></script>
3. Consent flow
Require opt-in before collection by registering a callback. When the callback resolves true, events are sent; otherwise they are skipped.
<!-- Require consent before sending events -->
<meta name="alx-consent" content="required">
<meta name="alx-consent-callback" content="onAlxConsent">
<script src="https://collector.alertalytix.cloud/v1/alertalytix.js"></script>
<script>
window.onAlxConsent = async () => {
try {
return !!(window.Cookiebot && Cookiebot.consent && Cookiebot.consent.statistics);
} catch (err) {
return false;
}
};
</script>
- No callback or a rejected promise means zero collection (fail safe).
- Manual calls to
ALX.sendEvent(...)also respect the consent requirement. - See /examples/consent_banner.html for a reference implementation.
4. Transport & security
- The collector posts events through
public/collector.htmlusingpostMessage, eliminating CORS configuration. - Alertalytix validates that the domain exists, is
active, and belongs to the authenticated tenant. - Add
<meta name="alx-disable-tracking" content="1">to suppress automatic sends on specific pages.
5. Offline retry
When offline, events stay in a queued buffer inside the iframe. They replay automatically once connectivity returns.
6. Manual events
Send custom events with ALX.sendEvent('signup', { plan: 'pro' }). Consent enforcement, deduplication and retry are automatic.
Server Event API
Send events directly from your backend when browser instrumentation is unavailable or when you need to enrich requests with server-side data. Authenticate using an API key scoped to the domain you want to monitor.
Endpoint
POST https://api.alertalytix.cloud/api/v1/events
Headers:
Content-Type: application/json
X-API-Key: YOUR_API_KEY
Payload
event_type(string, required): Describes the action, e.g.login.succeeded.context.ip_address(string, recommended): Source IP. Uses the request IP if omitted.occurred_at(ISO8601, optional): Timestamp when the event happened. Defaults to now.subject.identity_key/subject.session_token(optional): Hash identifiers to respect PII policy.context.user_agent,traits,metadata,attrs(optional): Any additional fields you want stored for future analysis.
JSON payload schema
The Events API accepts JSON that matches the schema below. Optional objects may be omitted entirely. Unknown keys are preserved under attrs.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["event_type"],
"additionalProperties": false,
"properties": {
"event_type": { "type": "string", "minLength": 1 },
"occurred_at": { "type": "string", "format": "date-time" },
"context": {
"type": "object",
"additionalProperties": true,
"properties": {
"ip_address": { "type": "string" },
"user_agent": { "type": "string" },
"device_fingerprint": { "type": "string" },
"host": { "type": "string" }
}
},
"subject": {
"type": "object",
"additionalProperties": true,
"properties": {
"identity_key": { "type": "string" },
"session_token": { "type": "string" }
}
},
"traits": { "type": "object", "additionalProperties": true },
"metadata": { "type": "object", "additionalProperties": true },
"attrs": {
"type": "object",
"additionalProperties": true,
"properties": {
"subject": { "$ref": "#/properties/subject" },
"context": { "$ref": "#/properties/context" },
"traits": { "$ref": "#/properties/traits" },
"metadata": { "$ref": "#/properties/metadata" }
}
}
}
}
Example request
{
"event_type": "login_attempt",
"occurred_at": "2024-10-01T12:34:56Z",
"context": {
"ip_address": "192.0.2.10",
"user_agent": "BackendService/1.0"
},
"subject": {
"identity_key": "hashed-user-id",
"session_token": "session-uuid"
},
"traits": {
"plan": "pro",
"channel": "support"
},
"metadata": {
"source": "manual-review"
}
}
Response
{
"event_id": "1234567890123"
}
The event flows through the same processing pipeline as browser events: risk signals, scoring and policy decisions are attached asynchronously.
Decision API
Retrieve the automated decision (allow, challenge or deny) for an ingested event. Use an API key with query scope.
Endpoint
GET https://api.alertalytix.cloud/api/v1/events/:event_id/decision
Headers:
X-API-Key: YOUR_API_KEY
Response example
{
"event_id": "12345678-1234-1234-1234-1234567890ab",
"decision": {
"id": "8460e3b3-adfa-4508-aacd-8ba85e02649d",
"decision": "allow",
"policy_version": "v1",
"decided_at": "2024-09-01T12:34:56Z",
"reasons": { "risk": 42 }
}
}
Recommended flow
- Collect any client-side context your backend needs (hashed identifiers, session tokens, etc.) and send it to the server over TLS. If you want the device fingerprint, call
ALX.createHarvester()in the browser to obtain theexact_idvalue and include it in that payload. - Your backend authenticates with an API key and calls
POST https://api.alertalytix.cloud/api/v1/eventsto create the event. Includeevent_type,context.ip_addressand any identifiers or traits (such asexact_id) that you want analysed. Use the collector endpoint (POST /collect/v1) only when sending directly from the browser. - Poll
GET https://api.alertalytix.cloud/api/v1/events/:event_id/decisionuntil a 200 response is returned, backing off between attempts.
Decisions are created asynchronously. Expect a short delay (hundreds of milliseconds) before the decision becomes available.
cURL
# 1) Ingest (server-side)
curl -sS -X POST "https://api.alertalytix.cloud/api/v1/events" \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"event_type": "login_attempt",
"occurred_at": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'",
"context": { "ip_address": "192.0.2.10", "user_agent": "Mozilla/5.0" },
"subject": { "identity_key": "hashed-user-id", "session_token": "SESSION_TOKEN" },
"traits": { "email_sha256": "..." }
}'
# => { "event_id": "..." }
# 2) Decision
curl -sS "https://api.alertalytix.cloud/api/v1/events/EVENT_ID/decision" \
-H "X-API-Key: YOUR_API_KEY"
Node.js
import fetch from "node-fetch";
export async function decide({ apiKey, ipAddress, identityHash, sessionToken, exactId }) {
const ingest = await fetch("https://api.alertalytix.cloud/api/v1/events", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": apiKey
},
body: JSON.stringify({
event_type: "login_attempt",
occurred_at: new Date().toISOString(),
context: { ip_address: ipAddress, user_agent: "Backend/1.0" },
subject: {
identity_key: identityHash,
session_token: sessionToken
},
traits: exactId ? { exact_id: exactId } : undefined
})
});
if (!ingest.ok) throw new Error(`ingest failed: ${ingest.status}`);
const { event_id } = await ingest.json();
for (let attempt = 0; attempt < 5; attempt += 1) {
const res = await fetch(`https://api.alertalytix.cloud/api/v1/events/${event_id}/decision`, {
headers: { "X-API-Key": apiKey }
});
if (res.status === 200) return (await res.json()).decision;
await new Promise(resolve => setTimeout(resolve, 200));
}
throw new Error("decision timeout");
}
Ruby
require "net/http"
require "json"
def decide(api_key:, ip_address:, identity_hash:, session_token: nil)
uri = URI("https://api.alertalytix.cloud/api/v1/events")
req = Net::HTTP::Post.new(uri)
req["Content-Type"] = "application/json"
req["X-API-Key"] = api_key
req.body = {
event_type: "login_attempt",
occurred_at: Time.now.utc.iso8601,
context: { ip_address: ip_address, user_agent: "Backend/1.0" },
subject: { identity_key: identity_hash, session_token: session_token }
}.to_json
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(req)
end
event_id = JSON.parse(res.body).fetch("event_id")
5.times do
decision_uri = URI("https://api.alertalytix.cloud/api/v1/events/#{event_id}/decision")
decision_req = Net::HTTP::Get.new(decision_uri)
decision_req["X-API-Key"] = api_key
res_decision = Net::HTTP.start(decision_uri.host, decision_uri.port, use_ssl: decision_uri.scheme == "https") do |http|
http.request(decision_req)
end
return JSON.parse(res_decision.body)["decision"] if res_decision.code == "200"
sleep 0.2
end
raise "decision timeout"
end
query; other scopes will be introduced later as needed.