Documentation Index
Fetch the complete documentation index at: https://www.pagent.ai/docs/llms.txt
Use this file to discover all available pages before exploring further.
Server-side conversion tracking lets you record conversions that happen on your backend — payment confirmations, form submissions, CRM updates, or webhook-triggered events. These events are automatically attributed to the visitor’s browser session and any active experiments, with no extra analytics configuration needed.
How It Works
- Create an API key in your pagent dashboard under Settings > API Keys
- Capture session variables on the frontend using
window._pgnt.variables("goal_label")
- Pass the variables to your backend (e.g., in a form submission or AJAX request)
- Send a conversion event from your backend via the REST API — use
POST /v1/conversions or POST /v2/conversions
Both v1 and v2 are fully supported. v2 accepts the output of _pgnt.variables() directly as a session_data object, which is useful when your integration cannot destructure the variables before sending.
Important: The session_id links each server-side event to the visitor’s browser session and active experiments. Without a valid session_id, the conversion cannot be attributed to an experiment.
Prerequisites
- The pagent SDK installed on your website
- At least one goal configured in the Tracking section of your dashboard (see Tracking Revenue for setup instructions)
1. Create an API Key
- Navigate to Settings > API Keys in your pagent dashboard
- Click “Create API Key”
- Give it a descriptive name (e.g., “Production Backend”)
- Copy the API key immediately
Important: The API key is displayed only once at creation. Store it securely as an environment variable. If you lose the key, delete it and create a new one.
2. Capture Variables from the SDK
Use window._pgnt.variables() to get the session identifiers and goal UUID you need for the API call. Pass the goal label as a string argument:
const vars = window._pgnt.variables("purchase");
// {
// session_id: "abc123...",
// user_id: "xyz789...",
// visit_id: "v_456...",
// conversion_id: "goal-uuid-here"
// }
| Property | Description |
|---|
session_id | The current browser session identifier |
user_id | The visitor’s persistent user identifier |
visit_id | The current page view identifier |
conversion_id | UUID of the goal matching the label, or null if no match |
Note: If conversion_id is null, the label does not match any configured goal. Double-check the goal label in your Tracking settings.
Passing Variables to Your Backend
Send the captured variables alongside your existing request data:
const vars = window._pgnt.variables("purchase");
fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
cart_id: "cart_abc123",
pagent: vars // pass the whole object — v2 accepts it directly
})
});
3. v1 API
Endpoint
| |
|---|
| URL | https://ingest.pagent.ai/v1/conversions |
| Method | POST |
| Authorization | Bearer <API_KEY> |
| Content-Type | application/json |
Request Body
The request body contains an events array with 1 to 100 conversion events:
{
"events": [
{
"session_id": "abc123",
"user_id": "xyz789",
"conversion_id": "goal-uuid-here",
"label": "purchase",
"revenue": 2499
}
]
}
| Field | Type | Required | Description |
|---|
session_id | string | Yes | From _pgnt.variables() |
user_id | string | Yes | From _pgnt.variables() |
conversion_id | string | Yes | Goal UUID from _pgnt.variables() |
label | string | No | Human-readable goal tag (useful for debugging) |
revenue | integer | No | Value in cents (e.g., 999 = $9.99) |
properties | object or array | No | Custom metadata about the conversion |
time | number | No | Unix timestamp in milliseconds (defaults to current time) |
Response
| Status | Body | Description |
|---|
200 | { "accepted": 1 } | Events accepted successfully |
400 | { "error": "..." } | Invalid request body |
401 | { "error": "..." } | Missing, invalid, or expired API key |
429 | | Rate limit exceeded |
Code Examples
cURL
curl -X POST https://ingest.pagent.ai/v1/conversions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"events": [{
"session_id": "abc123",
"user_id": "xyz789",
"conversion_id": "goal-uuid-here",
"label": "purchase",
"revenue": 2499
}]
}'
Node.js
const PAGENT_API_KEY = process.env.PAGENT_API_KEY;
async function trackConversion({ session_id, user_id, conversion_id, label, revenue }) {
const response = await fetch("https://ingest.pagent.ai/v1/conversions", {
method: "POST",
headers: {
"Authorization": `Bearer ${PAGENT_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
events: [{ session_id, user_id, conversion_id, label, revenue }]
})
});
if (!response.ok) {
const body = await response.json();
throw new Error(`Pagent API error (${response.status}): ${body.error}`);
}
return response.json(); // { accepted: 1 }
}
// Usage in an Express route handler
app.post("/api/checkout", async (req, res) => {
const { session_id, user_id, conversion_id, cart_total } = req.body;
// Process the checkout...
const order = await processCheckout(req.body);
// Track the conversion
await trackConversion({
session_id,
user_id,
conversion_id,
label: "purchase",
revenue: Math.round(cart_total * 100) // Convert dollars to cents
});
res.json({ order_id: order.id });
});
Python
import os
import requests
PAGENT_API_KEY = os.environ["PAGENT_API_KEY"]
def track_conversion(session_id, user_id, conversion_id, label=None, revenue=None):
response = requests.post(
"https://ingest.pagent.ai/v1/conversions",
headers={
"Authorization": f"Bearer {PAGENT_API_KEY}",
"Content-Type": "application/json",
},
json={
"events": [{
"session_id": session_id,
"user_id": user_id,
"conversion_id": conversion_id,
"label": label,
"revenue": revenue,
}]
},
)
response.raise_for_status()
return response.json() # {"accepted": 1}
Batch Events
You can send up to 100 events in a single request. Events can belong to different sessions and users, making this ideal for processing webhooks or background jobs:
{
"events": [
{
"session_id": "sess_001",
"user_id": "user_a",
"conversion_id": "goal-uuid",
"label": "purchase",
"revenue": 2499
},
{
"session_id": "sess_002",
"user_id": "user_b",
"conversion_id": "goal-uuid",
"label": "purchase",
"revenue": 4999
}
]
}
Custom Properties
Attach custom metadata to your conversion events for additional context. The properties field accepts an object or an array of objects.
{
"events": [{
"session_id": "abc123",
"user_id": "xyz789",
"conversion_id": "goal-uuid",
"label": "purchase",
"revenue": 2499,
"properties": {
"product_id": "prod_123",
"category": "electronics",
"payment_method": "credit_card"
}
}]
}
4. v2 API
v2 is useful when your integration cannot destructure _pgnt.variables() before sending — the session identifiers are grouped in a session_data object that you can pass through directly.
Single Event Endpoint
| |
|---|
| URL | https://ingest.pagent.ai/v2/conversions |
| Method | POST |
| Authorization | Bearer <API_KEY> |
| Content-Type | application/json |
Request Body
{
"session_data": {
"session_id": "abc123",
"user_id": "xyz789",
"visit_id": "v_456",
"conversion_id": "goal-uuid-here"
},
"label": "purchase",
"revenue": 2499
}
| Field | Type | Required | Description |
|---|
session_data | object | Yes | Session identifiers from _pgnt.variables() |
session_data.session_id | string | Yes | The current browser session identifier |
session_data.user_id | string | Yes | The visitor’s persistent user identifier |
session_data.visit_id | string | No | The current page view identifier |
session_data.conversion_id | string | Yes | Goal UUID from _pgnt.variables() |
label | string | No | Human-readable goal tag (useful for debugging) |
revenue | integer | No | Value in cents (e.g., 999 = $9.99) |
properties | object or array | No | Custom metadata about the conversion |
time | number | No | Unix timestamp in milliseconds (defaults to current time) |
Response
| Status | Body | Description |
|---|
200 | { "accepted": 1 } | Event accepted successfully |
400 | { "error": "..." } | Invalid request body |
401 | { "error": "..." } | Missing, invalid, or expired API key |
429 | | Rate limit exceeded |
Bulk Endpoint
| |
|---|
| URL | https://ingest.pagent.ai/v2/conversions/bulk |
| Method | POST |
| Authorization | Bearer <API_KEY> |
| Content-Type | application/json |
Send up to 100 events in a single request. Each event uses the same session_data structure as the single-event endpoint:
{
"events": [
{
"session_data": {
"session_id": "sess_001",
"user_id": "user_a",
"conversion_id": "goal-uuid"
},
"label": "purchase",
"revenue": 2499
},
{
"session_data": {
"session_id": "sess_002",
"user_id": "user_b",
"conversion_id": "goal-uuid"
},
"revenue": 4999
}
]
}
Code Examples
cURL
curl -X POST https://ingest.pagent.ai/v2/conversions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"session_data": {
"session_id": "abc123",
"user_id": "xyz789",
"visit_id": "v_456",
"conversion_id": "goal-uuid-here"
},
"label": "purchase",
"revenue": 2499
}'
Node.js
const PAGENT_API_KEY = process.env.PAGENT_API_KEY;
async function trackConversion(sessionData, { label, revenue } = {}) {
const response = await fetch("https://ingest.pagent.ai/v2/conversions", {
method: "POST",
headers: {
"Authorization": `Bearer ${PAGENT_API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
session_data: sessionData,
label,
revenue
})
});
if (!response.ok) {
const body = await response.json();
throw new Error(`Pagent API error (${response.status}): ${body.error}`);
}
return response.json(); // { accepted: 1 }
}
// Usage in an Express route handler
app.post("/api/checkout", async (req, res) => {
const { pagent, cart_total } = req.body;
// pagent is the raw _pgnt.variables() output passed from the frontend
// Process the checkout...
const order = await processCheckout(req.body);
// Track the conversion — pass session_data directly, no destructuring needed
await trackConversion(pagent, {
label: "purchase",
revenue: Math.round(cart_total * 100) // Convert dollars to cents
});
res.json({ order_id: order.id });
});
Python
import os
import requests
PAGENT_API_KEY = os.environ["PAGENT_API_KEY"]
def track_conversion(session_data, label=None, revenue=None):
response = requests.post(
"https://ingest.pagent.ai/v2/conversions",
headers={
"Authorization": f"Bearer {PAGENT_API_KEY}",
"Content-Type": "application/json",
},
json={
"session_data": session_data,
"label": label,
"revenue": revenue,
},
)
response.raise_for_status()
return response.json() # {"accepted": 1}
Custom Properties
Attach custom metadata using the properties field (accepts an object or an array of objects):
{
"session_data": {
"session_id": "abc123",
"user_id": "xyz789",
"conversion_id": "goal-uuid"
},
"label": "purchase",
"revenue": 2499,
"properties": {
"product_id": "prod_123",
"category": "electronics",
"payment_method": "credit_card"
}
}
Tracking Revenue
Revenue values follow the same format as client-side tracking: integers representing cents, not dollars.
- Correct:
"revenue": 999 (represents $9.99)
- Incorrect:
"revenue": 9.99 (will be treated as 9 cents)
| Dollar Amount | Revenue Value |
|---|
| $1.00 | 100 |
| $9.99 | 999 |
| $25.50 | 2550 |
| $100.00 | 10000 |
Best Practices
- Store API keys securely — use environment variables, never commit keys to source control
- Send events promptly — if there is a delay between the conversion and the API call, use the
time field to record the actual conversion time
- Validate before sending — check that
conversion_id is not null before passing variables to your backend
- Batch when possible — group events into a single request to reduce HTTP overhead
- Handle errors gracefully — retry on
500 responses, do not retry on 400 or 401
- Revenue in cents — always convert dollar amounts to integer cents before sending
Troubleshooting
Conversions Not Appearing
- Verify
session_id is from an active session captured by the SDK
- Check that the goal exists in your Tracking settings and
conversion_id is correct
- Confirm the API key has not expired (keys expire after 1 year by default)
401 Unauthorized
- Confirm the
Authorization: Bearer <key> header is present and correctly formatted
- The API key may be expired or deleted — create a new one in Settings > API Keys
- Make sure there are no extra spaces or newlines in the key value
400 Bad Request
- v2:
session_data must be an object containing session_id, user_id, and conversion_id
- v2 bulk / v1: The
events array must contain 1 to 100 items
- v1:
session_id, user_id, and conversion_id are all required as top-level fields on each event
revenue must be an integer, not a decimal (e.g., 999 not 9.99)
Need Help?
Reach out at support@pagent.ai.