LinkStacked

Scheduling & A/B tests

Two adjacent surfaces that drive most growth integrations. Both work exclusively at the link level — there's no profile-wide schedule or test.

scheduleLink accepts a ScheduleConfig with go-live and expire timestamps. Either is optional, but you must supply at least one:

mutation Schedule($linkId: ID!, $config: ScheduleConfig!) {
  scheduleLink(linkId: $linkId, config: $config) {
    link { _id scheduleConfig { liveAt expiresAt timezone } }
  }
}
{
  "linkId": "65f...",
  "config": {
    "liveAt": "2026-08-01T18:00:00.000Z",
    "expiresAt": "2026-08-08T18:00:00.000Z",
    "timezone": "Europe/London"
  }
}

scheduledLinks returns every link with an active or upcoming schedule for the authenticated user — handy for "Coming up" admin views.

Timezones matter at the boundary

We store liveAt/expiresAt as UTC, but the editor records the authoring timezone in timezone. If you change a user's timezone in your app, refetch and re-render before letting them edit a schedule — otherwise an 18:00 launch slips by an hour.

Create an A/B test

A/B tests on Linkstacked compare two link variants and route traffic 50/50 with deterministic, sticky bucketing per visitor.

mutation CreateABTest($input: CreateABTestInput!) {
  createABTest(input: $input) {
    test { _id status variants { label url ctr } }
  }
}
{
  "input": {
    "linkId": "65f...",
    "variants": [
      { "label": "Variant A", "url": "https://...", "title": "Stream now" },
      { "label": "Variant B", "url": "https://...", "title": "Listen here" }
    ]
  }
}

The same surface exposes pauseABTest, resumeABTest, and endABTestWithWinner(testId, winnerLabel) so an automated pipeline can end a test as soon as significance is reached.

Read live test results

abTestReport(testId) returns variant-level click-through rates, significance, and a flag for the leading variant. Poll it on whatever cadence makes sense for your dashboard — the API caches per-second aggregations server-side, so a 5–10s poll is friendly.