Skip to content New SourceLoop MCP: chat with your attribution data in Claude, ChatGPT & Cursor
SourceLoop
API reference

Events

Send server-side events for anything the browser cannot see: OAuth callbacks, payment webhooks, queue workers. Endpoints, parameters, and schemas.

Endpoint Method Path Permission
Send events Send server-side events POST /v1/events events:write

Base URL https://app.sourceloop.ai/api/v1

Send events

Send server-side events

POST /v1/events

Requires events:write

For anything the browser cannot see: OAuth callbacks, payment webhooks, queue workers. Events are validated and classified with exactly the same rules the browser tracker applies, so a server-side conversion attributes identically.

Each event needs an email, a phone, or the visitor id to be attributable. The visitor id is the strongest match: it ties the event to the exact visit and its marketing. Read it from the `_sl_aid` cookie, or from the hidden `sl_aid` field the script fills in your forms, and send it as `anonymous_id` (`sl_aid` and `visitor_id` are accepted too). Email and phone match the person when there is no visitor id.

Send your own id for the event (a CRM lead id, an order number) as `external_id`: sending the same one again never counts twice, so retries are safe. The response lists the `event_id` each accepted event got.

Events up to 90 days old. For older history or offline sales, use an offline data source (`POST /imports/{id}/rows`). Channels are never taken from the request: the visit decides them, exactly as in the browser.

Partial success: one malformed event does not discard the batch.

curl -s -X POST "https://app.sourceloop.ai/api/v1/events" \
  -H "Authorization: Bearer $SOURCELOOP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "events": [
      {
        "event_type": "conversion",
        "event_name": "demo_booked",
        "external_id": "crm-lead-8812",
        "email": "jane@acme.com",
        "sl_aid": "3f1c2a9e-7b1d-4c55-9d1e-0a1b2c3d4e5f",
        "occurred_at": "2026-10-08T14:03:00Z",
        "revenue": 0
      }
    ]
  }'
const res = await fetch(
  "https://app.sourceloop.ai/api/v1/events",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SOURCELOOP_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      "events": [
        {
          "event_type": "conversion",
          "event_name": "demo_booked",
          "external_id": "crm-lead-8812",
          "email": "jane@acme.com",
          "sl_aid": "3f1c2a9e-7b1d-4c55-9d1e-0a1b2c3d4e5f",
          "occurred_at": "2026-10-08T14:03:00Z",
          "revenue": 0
        }
      ]
    }),
  },
);

const data = await res.json();
import os, requests

res = requests.post(
    "https://app.sourceloop.ai/api/v1/events",
    headers={"Authorization": f"Bearer {os.environ['SOURCELOOP_API_KEY']}"},
    json={
      "events": [
        {
          "event_type": "conversion",
          "event_name": "demo_booked",
          "external_id": "crm-lead-8812",
          "email": "jane@acme.com",
          "sl_aid": "3f1c2a9e-7b1d-4c55-9d1e-0a1b2c3d4e5f",
          "occurred_at": "2026-10-08T14:03:00Z",
          "revenue": 0
        }
      ]
    },
)
data = res.json()

Headers

Idempotency-Keystringheader

Request body

websitestring

eventsobject[]required

Event

events[].event_typestring

Example custom

events[].event_namestring

Example signup_completed

events[].occurred_atstring (date-time)

When it happened (default now). Up to 90 days back.

events[].external_idstring

Your id for this event. The same one again never counts twice.

Identity

events[].emailstring

events[].phonestring

events[].anonymous_idstring

The visitor id: the _sl_aid cookie, or the sl_aid hidden form field. Also accepted as sl_aid or visitor_id.

Value

events[].revenuenumber

events[].currencystring

events[].propertiesobject

Responses

  • 200 Accepted and rejected counts, the event_id of each accepted event by index, and the reason for each rejected one
  • 400 Something was wrong with the request or the credential.
  • 429 Too many requests for this workspace in the current minute (`rate-limit-exceeded`), the monthly allowance used (`quota-exceeded`), or too many analytics queries running at once (`too-many-concurrent`). Wait Retry-After seconds and repeat the request unchanged.

Track every conversion to its true source

Capture and send full attribution data from every signup, lead, booking, and sale to your CRM and ad platforms, so you know exactly what's driving revenue.

Without SourceLoop

Untagged

Kayden Floyd

kayden@abc.com

  • SourceUnknown
  • MediumUnknown
  • CampaignUnknown
  • Landing pageUnknown
Journey
No touchpoints captured

With SourceLoop

Auto-tagged

Kayden Floyd

kayden@abc.com · Acme Co.

  • Channel Paid Social
  • CampaignFree_demo
  • Landing page/pricing
Journey
Synced to HubSpot Google Ads Meta