# SplitLaunch Agent Skills

SplitLaunch is a package-first A/B testing platform for customer-owned pages.
The user keeps their own website. SplitLaunch provides the package, browser
pixel, experiment API, data collection, conversion tracking, and reporting API.

This file is written for AI agents. Treat it as the operating manual for
installing SplitLaunch, creating projects, allowlisting domains, launching URL
A/B tests, firing custom conversion events, and retrieving results.

## Agent Operating Rules

1. Never put the private API key in browser code.
2. Browser code only receives the public pixel ID, for example `px_abc123`.
3. Use the API key only in terminal, server, CI, or agent-side calls.
4. For agency/client accounts, create or select the correct project before
   creating experiments. Each project has its own pixel ID.
5. Always allowlist the customer's domain before launching a redirecting test.
6. Prefer creating the experiment in `draft`, verifying URLs/domains/goals,
   then setting status to `running`.
7. Use `projectId` on commands when the account has multiple projects.
8. If a command changes project, domain, experiment, or goal configuration,
   explain the change to the user in plain language.

## Plan Gates

- Free includes the first project, all platform features, and 10,000 one-off
  tracked sessions. No credit card is required.
- Go is for additional projects with URL A/B testing, conversion tracking,
  domain whitelisting, MCP access, and API access. Do not use geo targeting,
  traffic-source filters, URL pattern rules, or revenue values on Go.
- Plus adds geo targeting, traffic-source filters, and revenue tracking. Do not
  use URL pattern rules on Plus.
- Pro adds URL pattern rules and custom feature support.
- When revenue tracking is not included, omit monetary `value`, `revenue`,
  `amount`, `price`, and `currency` fields. SplitLaunch records the
  conversion count but ignores revenue metadata.

## Installation Flow

User-facing setup:

1. User creates a SplitLaunch account and opens the dashboard.
2. Free projects can run immediately within the included limits. Go, Plus, and
   Pro projects use Stripe checkout before running as paid additional projects.
3. User copies the account API key from the dashboard.
4. In the user's website or app repository, the user or coding agent runs:

```bash
npm install @splitlaunch/ab
npx splitlaunch init
```

5. The terminal asks for the API key.
6. The CLI validates the key and retrieves the public pixel ID.
7. The CLI writes local setup configuration and prints the pixel install snippet.
8. The agent can read this Skills file and use the API commands below.

Non-interactive setup for CI or agent automation:

```bash
SPLITLAUNCH_API_KEY=sl_live_... npx splitlaunch init
```

For a specific client/project after the account has multiple projects:

```bash
SPLITLAUNCH_API_KEY=sl_live_... npx splitlaunch init --project PROJECT_ID
```

Useful CLI commands:

```bash
npx splitlaunch projects list
npx splitlaunch docs
npx splitlaunch skills
npx splitlaunch pixel status
```

## Auth

Use the API key as a bearer token for server, terminal, or AI-agent calls:

```txt
Authorization: Bearer sl_live_...
```

Use the canonical `www` endpoint for API calls. Do not call the apex
`https://splitlaunch.dev` endpoint from scripts, because redirects can drop
the `Authorization` header.

Main command endpoint:

```txt
POST https://www.splitlaunch.dev/api/external/v1/commands
```

Request shape:

```json
{
  "command": "experiments.create",
  "input": {}
}
```

Response shape:

```json
{
  "command": "experiments.create",
  "data": {}
}
```

## Projects And Pixel IDs

One account can have multiple projects. This is important for agencies and
expert users managing multiple client websites. Each project has:

- its own `projectId`,
- its own public `pixelId`,
- its own domain whitelist,
- its own experiments,
- its own conversion goals,
- its own attribution window and first/last touch model,
- its own usage records.

If an API key is account-level, it can manage multiple projects. If an API key
is project-scoped, it can only access that project and cannot create new
projects.

Create a client project:

```bash
curl -X POST "https://www.splitlaunch.dev/api/external/v1/commands" \
  -H "Authorization: Bearer sl_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "command": "projects.create",
    "input": {
      "name": "Acme Client",
      "allowedDomains": ["acme.com", "www.acme.com"],
      "reportingTimezone": "America/New_York",
      "attributionWindowDays": 30,
      "attributionModel": "last_touch"
    }
  }'
```

Update project attribution:

```json
{
  "command": "projects.update",
  "input": {
    "projectId": "PROJECT_ID",
    "attributionWindowDays": 60,
    "attributionModel": "first_touch"
  }
}
```

Attribution guidance:

- Use `last_touch` when the latest experiment exposure before conversion should get credit.
- Use `first_touch` when the first experiment exposure in the window should get credit.
- Short-cycle ecommerce tests usually use 3-7 days and `last_touch`.
- Lead-gen, local service, SaaS, or B2B projects may need 30-90 days.

List available projects:

```json
{
  "command": "projects.list",
  "input": {
    "perPage": 25
  }
}
```

Retrieve one project and its pixel ID:

```json
{
  "command": "projects.get",
  "input": {
    "projectId": "PROJECT_ID"
  }
}
```

Archive a project:

```json
{
  "command": "projects.archive",
  "input": {
    "projectId": "PROJECT_ID"
  }
}
```

Archiving a project stops its pixel from serving experiments or accepting new
events, but keeps historical usage and reports.

Agent guidance: when the user says "create a test for Client A", first map
Client A to the right `projectId`. If no project exists, create one and use
the returned `pixelId` for installation.

## Domain Allowlisting

SplitLaunch should only run pixels and redirects on domains the user controls.
Before launching an experiment, add the control and variation hostnames.

Add a domain:

```json
{
  "command": "domains.add",
  "input": {
    "projectId": "PROJECT_ID",
    "hostname": "example.com"
  }
}
```

List whitelisted domains:

```json
{
  "command": "domains.list",
  "input": {
    "projectId": "PROJECT_ID"
  }
}
```

Remove a domain:

```json
{
  "command": "domains.remove",
  "input": {
    "projectId": "PROJECT_ID",
    "hostname": "example.com"
  }
}
```

Hostname rules:

- Use hostnames, not full page URLs: `example.com`, not
  `https://example.com/pricing`.
- Add both apex and subdomain if both are used: `example.com` and
  `www.example.com`.
- Add every hostname used by the control URL and variation URL.
- If the customer tests across subdomains, allowlist each participating
  hostname.

## Pixel Installation

The pixel is loaded with a public pixel ID. Example:

```html
<!-- Start of SplitLaunch Pixel -->
<script>
  window.splitlaunch = window.splitlaunch || function(){(window.splitlaunch.q=window.splitlaunch.q||[]).push(arguments)};
</script>
<script async src="https://www.splitlaunch.dev/pixel/px_your_pixel_id.js"></script>
<!-- End of SplitLaunch Pixel -->
```

The pixel collects page/session/event/exposure metadata including:

- page URL and path,
- referrer URL and referrer host,
- visitor ID and session ID,
- visitor type,
- device type,
- browser language,
- timezone,
- viewport bucket,
- UTMs,
- ad click IDs such as gclid, fbclid, msclkid, ttclid, li_fat_id,
- query snapshot,
- country and region when available,
- in-app browser detection,
- experiment ID,
- assigned variant,
- custom event data after sanitisation.

## Creating An A/B Test

Minimum experiment creation:

```json
{
  "command": "experiments.create",
  "input": {
    "projectId": "PROJECT_ID",
    "name": "Homepage headline test",
    "controlUrl": "https://example.com/",
    "variationUrl": "https://example.com/homepage-v2",
    "trafficAllocation": 50
  }
}
```

`trafficAllocation` means the percentage of eligible visitors assigned to the
variation URL. Agents may send `50` or `0.5`; both represent 50%.

Recommended agent checklist before creating:

1. Confirm the project.
2. Confirm the control URL.
3. Confirm the variation URL.
4. Add/verify whitelisted domains.
5. Decide traffic allocation.
6. Decide targeting filters.
7. Decide conversion goal event names.
8. Create the experiment.
9. Keep it in draft until the user approves launch.
10. Set status to running.

## Targeting Examples

Desktop-only experiment:

```json
{
  "command": "experiments.create",
  "input": {
    "projectId": "PROJECT_ID",
    "name": "Desktop pricing layout",
    "controlUrl": "https://example.com/pricing",
    "variationUrl": "https://example.com/pricing-v2",
    "trafficAllocation": 25,
    "targeting": {
      "deviceType": "desktop-only"
    }
  }
}
```

New visitors only:

```json
{
  "command": "experiments.create",
  "input": {
    "projectId": "PROJECT_ID",
    "name": "New visitor offer",
    "controlUrl": "https://example.com/",
    "variationUrl": "https://example.com/new-offer",
    "trafficAllocation": 50,
    "targeting": {
      "visitorType": "new-visitors"
    }
  }
}
```

UTM/source-specific traffic:

Requires Plus or Pro.

```json
{
  "command": "experiments.create",
  "input": {
    "projectId": "PROJECT_ID",
    "name": "Paid search landing test",
    "controlUrl": "https://example.com/landing",
    "variationUrl": "https://example.com/landing-v2",
    "trafficAllocation": 50,
    "targeting": {
      "trafficFilters": {
        "utm_source": "google",
        "utm_medium": "cpc"
      }
    }
  }
}
```

Country targeting:

Requires Plus or Pro.

```json
{
  "command": "experiments.create",
  "input": {
    "projectId": "PROJECT_ID",
    "name": "US/AU offer test",
    "controlUrl": "https://example.com/",
    "variationUrl": "https://example.com/us-au-offer",
    "trafficAllocation": 50,
    "targeting": {
      "geoFilters": {
        "countries": {
          "type": "include",
          "countryCodes": ["US", "AU"],
          "includeUnknown": false
        }
      }
    }
  }
}
```

URL pattern rules for matching multiple pages:

Requires Pro.

```json
{
  "command": "experiments.create",
  "input": {
    "projectId": "PROJECT_ID",
    "name": "All product pages CTA",
    "controlUrl": "https://example.com/products/widget",
    "variationUrl": "https://example.com/products/widget-v2",
    "trafficAllocation": 50,
    "urlPatternRules": [
      {
        "conditions": [
          {
            "operator": "starts_with",
            "value": "/products/"
          }
        ]
      }
    ]
  }
}
```

Supported URL pattern operators:

- `is`
- `contains`
- `starts_with`
- `ends_with`

## Launching, Pausing, Ending

Create experiments as draft, then launch:

```json
{
  "command": "experiments.set_status",
  "input": {
    "projectId": "PROJECT_ID",
    "experimentId": "EXPERIMENT_ID",
    "status": "running"
  }
}
```

Pause:

```json
{
  "command": "experiments.set_status",
  "input": {
    "projectId": "PROJECT_ID",
    "experimentId": "EXPERIMENT_ID",
    "status": "paused"
  }
}
```

End:

```json
{
  "command": "experiments.set_status",
  "input": {
    "projectId": "PROJECT_ID",
    "experimentId": "EXPERIMENT_ID",
    "status": "ended"
  }
}
```

Archive an experiment:

```json
{
  "command": "experiments.archive",
  "input": {
    "projectId": "PROJECT_ID",
    "experimentId": "EXPERIMENT_ID"
  }
}
```

Get one experiment:

```json
{
  "command": "experiments.get",
  "input": {
    "projectId": "PROJECT_ID",
    "experimentId": "EXPERIMENT_ID"
  }
}
```

## Conversion Goals And Custom Events

Create a goal:

Revenue values require Free, Plus, or Pro. On Go, omit `value` and track the
conversion count only.

```json
{
  "command": "goals.create",
  "input": {
    "projectId": "PROJECT_ID",
    "name": "Lead",
    "eventName": "Lead",
    "value": 100
  }
}
```

Fire the conversion in browser code after the desired user action:

```js
window.splitlaunch("track", "Lead", {
  value: 100,
  form: "Demo request",
  plan: "Pro"
});
```

If the visitor was previously exposed to a running experiment, SplitLaunch
persists that active experiment/variant context and attaches it to later
conversion events. The ingestion API also falls back to the visitor's latest
eligible exposure when an attributable conversion event arrives without explicit
experiment fields.

Common event names:

- `Lead`
- `Signup`
- `TrialStarted`
- `Purchase`
- `DemoBooked`
- `Subscribe`
- `ContactFormSubmitted`

Agent guidance: if the user asks to track a form, add the event after a
successful submit, not on initial click. If the user asks to track purchases,
fire the event only after confirmed checkout/payment success.

Deactivate a goal:

```json
{
  "command": "goals.deactivate",
  "input": {
    "projectId": "PROJECT_ID",
    "eventName": "Lead"
  }
}
```

## Reporting

List experiments:

```json
{
  "command": "experiments.list",
  "input": {
    "projectId": "PROJECT_ID",
    "status": "running",
    "perPage": 20
  }
}
```

Get experiment details:

```json
{
  "command": "reports.experiment.details",
  "input": {
    "projectId": "PROJECT_ID",
    "experimentId": "EXPERIMENT_ID",
    "goal": "Lead",
    "from": "2026-07-01",
    "to": "2026-07-14"
  }
}
```

Get chart/time-series data:

```json
{
  "command": "reports.experiment.chart",
  "input": {
    "projectId": "PROJECT_ID",
    "experimentId": "EXPERIMENT_ID",
    "interval": "day",
    "goal": "Lead"
  }
}
```

Get channel/acquisition breakdown:

```json
{
  "command": "reports.experiment.channels",
  "input": {
    "projectId": "PROJECT_ID",
    "experimentId": "EXPERIMENT_ID",
    "goal": "Lead"
  }
}
```

Channel reports use the UXON-derived grouping model. Agents should expect
proper channel labels such as Paid Search, Paid Social, Other Paid, Owned
Messaging, Organic Search, Organic Social, Referral, Direct, AI Referral, App
Store, and Unknown. Each row includes the channel, source/medium, optional
campaign/content hierarchy, variant, exposures, visitors, conversions, revenue
when available, and conversion rate.

When explaining results, agents should mention:

- sample size,
- exposure count,
- conversion count,
- conversion rate by variant,
- absolute difference,
- relative lift,
- whether the result looks directional or needs more data,
- any obvious channel/device skew.

## Usage And Billing

Usage summary:

```json
{
  "command": "usage.summary",
  "input": {
    "projectId": "PROJECT_ID",
    "from": "2026-07-01",
    "to": "2026-07-31"
  }
}
```

Billing portal:

```json
{
  "command": "billing.portal",
  "input": {
    "returnUrl": "https://www.splitlaunch.dev/dashboard"
  }
}
```

## Commands

### projects.create

- Category: Project
- Mode: write
- Input: name, allowedDomains?, reportingTimezone?, attributionWindowDays?, attributionModel?
- Use: Create a new client/project workspace and allocate a unique public pixel ID.

### projects.list

- Category: Project
- Mode: read-only
- Input: page?, perPage?
- Use: List projects available to the API key's account, including each public pixel ID.

### projects.get

- Category: Project
- Mode: read-only
- Input: projectId?
- Use: Return the current project, public pixel ID, domain whitelist, attribution settings, and plan limits.

### projects.update

- Category: Project
- Mode: write
- Input: projectId?, name?, allowedDomains?, attributionWindowDays?, attributionModel?
- Use: Update project name, domain whitelist, or project attribution settings.

### projects.archive

- Category: Project
- Mode: write
- Input: projectId
- Use: Archive a project so its pixel stops serving experiments while history is kept.

### experiments.create

- Category: Experiments
- Mode: write
- Input: projectId?, name, controlUrl, variationUrl, trafficAllocation, targeting?, urlPatternRules?, preserveRedirectParams?
- Use: Create an external URL A/B test with control URL, variation URL, traffic split, and targeting filters.

### experiments.get

- Category: Experiments
- Mode: read-only
- Input: projectId?, experimentId
- Use: Return one experiment by ID, including URLs, status, targeting, and URL pattern rules.

### experiments.update

- Category: Experiments
- Mode: write
- Input: projectId?, experimentId, name?, controlUrl?, variationUrl?, trafficAllocation?, targeting?, urlPatternRules?, preserveRedirectParams?
- Use: Change an experiment's URLs, allocation, filters, URL pattern rules, or selected conversion goals.

### experiments.set_status

- Category: Experiments
- Mode: write
- Input: projectId?, experimentId, status
- Use: Move an experiment through draft, running, paused, and ended states.

### experiments.archive

- Category: Experiments
- Mode: write
- Input: projectId?, experimentId
- Use: Archive an experiment by ending it while keeping exposures, events, and reports available.

### experiments.list

- Category: Experiments
- Mode: read-only
- Input: projectId?, status?, page?, perPage?
- Use: List experiments for the project with status and URL filters.

### reports.experiment.details

- Category: Reports
- Mode: read-only
- Input: projectId?, experimentId, from?, to?, goal?
- Use: Return experiment totals, variants, visitors, exposures, conversions, rates, lift, and selected goal data.

### reports.experiment.chart

- Category: Reports
- Mode: read-only
- Input: projectId?, experimentId, from?, to?, interval?, goal?
- Use: Return time-series experiment performance for agent analysis.

### reports.experiment.channels

- Category: Reports
- Mode: read-only
- Input: projectId?, experimentId, from?, to?, goal?
- Use: Return acquisition channel and metadata performance using the pixel's collected UTMs, click IDs, referrers, and browser context.

### goals.create

- Category: Goals
- Mode: write
- Input: projectId?, name, eventName, value?
- Use: Create a conversion goal that can be fired by the browser pixel or documented for an AI agent.

### goals.list

- Category: Goals
- Mode: read-only
- Input: projectId?, active?
- Use: List conversion goals available for experiments and reporting.

### goals.deactivate

- Category: Goals
- Mode: write
- Input: projectId?, goalId?, eventName?
- Use: Deactivate a conversion goal by goal ID or event name while keeping historical reporting data.

### domains.list

- Category: Domains
- Mode: read-only
- Input: projectId?
- Use: List allowed customer-owned domains for pixel and redirect safety.

### domains.add

- Category: Domains
- Mode: write
- Input: projectId?, hostname
- Use: Add a customer-owned domain that may load the pixel or participate in redirects.

### domains.remove

- Category: Domains
- Mode: write
- Input: projectId?, hostname
- Use: Remove a whitelisted hostname from a project so the pixel stops accepting that domain.

### usage.summary

- Category: Usage
- Mode: read-only
- Input: projectId?, from?, to?
- Use: Return billing usage such as events, sessions, exposures, active experiments, and plan limits.

### billing.portal

- Category: Billing
- Mode: write
- Input: returnUrl?
- Use: Create or return a billing portal link for subscription management.
