Skip to main content
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

  1. Create an API key in your pagent dashboard under Settings > API Keys
  2. Capture session variables on the frontend using window._pgnt.variables("goal_label")
  3. Pass the variables to your backend (e.g., in a form submission or AJAX request)
  4. Send a conversion event from your backend via POST /v1/conversions
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

  1. Navigate to Settings > API Keys in your pagent dashboard
  2. Click “Create API Key”
  3. Give it a descriptive name (e.g., “Production Backend”)
  4. 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"
// }
PropertyDescription
session_idThe current browser session identifier
user_idThe visitor’s persistent user identifier
visit_idThe current page view identifier
conversion_idUUID 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",
        session_id: vars.session_id,
        user_id: vars.user_id,
        conversion_id: vars.conversion_id
    })
});

3. Send Conversion Events

Endpoint

URLhttps://ingest.pagent.ai/v1/conversions
MethodPOST
AuthorizationBearer <API_KEY>
Content-Typeapplication/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
        }
    ]
}
FieldTypeRequiredDescription
session_idstringYesFrom _pgnt.variables()
user_idstringYesFrom _pgnt.variables()
conversion_idstringYesGoal UUID from _pgnt.variables()
labelstringNoHuman-readable goal tag (useful for debugging)
revenueintegerNoValue in cents (e.g., 999 = $9.99)
propertiesobject or arrayNoCustom metadata about the conversion
timenumberNoUnix timestamp in milliseconds (defaults to current time)

Response

StatusBodyDescription
200{ "accepted": 1 }Events accepted successfully
400{ "error": "..." }Invalid request body
401{ "error": "..." }Missing, invalid, or expired API key
429Rate 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}

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 AmountRevenue Value
$1.00100
$9.99999
$25.502550
$100.0010000

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:
{
    "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"
        }
    }]
}
The properties field accepts an object or an array of objects.

Best Practices

  1. Store API keys securely — use environment variables, never commit keys to source control
  2. 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
  3. Validate before sending — check that conversion_id is not null before passing variables to your backend
  4. Batch when possible — group events into a single request to reduce HTTP overhead
  5. Handle errors gracefully — retry on 500 responses, do not retry on 400 or 401
  6. 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

  • The events array must contain 1 to 100 items
  • session_id, user_id, and conversion_id are all required for each event
  • revenue must be an integer, not a decimal (e.g., 999 not 9.99)

Need Help?

Reach out at support@pagent.ai.