TEASEDocs
Guides

Landing pages

Build a per-domain link-in-bio funnel — geo-segments, button stacks, a notification banner, and a background — then preview and publish it.

A landing page is the public funnel TEASE serves on one of your domains. You compose it from ordered geo-segments, a stack of buttons per segment, an optional notification banner, and a background — all edited as a draft, previewed live, then published in one atomic step.

Every change you make through the API writes to the draft state only. The live page never moves until you call /publish. That gives you a safe edit-preview-ship loop with a single global undo (/discard).

Concepts

ConceptWhat it is
DomainA host your public page is served on (e.g. links.creator.com). Every other resource on this page is scoped to a domain.
Geo-segmentAn ordered audience bucket matched by country (and, optionally, device, OS, time, or language). Each segment owns its own button stack. Exactly one segment is the default catch-all.
ButtonA labelled link inside a segment, with visible text, an icon, and a destination URL. Buttons are ordered within a segment.
Teaser popupAn optional per-button card (photo + short text) that slides out under its button after a delay.
Notification bannerA push-style banner (avatar, title, body, call-to-action) that surfaces on the page after a short delay.
BackgroundThe page backdrop — a mobile image, a desktop image, and a solid canvas colour behind the card on desktop.
Page UI styleLanding-wide button appearance, the profile-card look, and an optional 18+ confirmation gate — applies while the domain uses a built-in (platform) design.
Draft / PublishedTwo snapshots of the whole page. You always edit the draft; /publish copies draft → published; /discard copies published → draft (undo).

Authentication

Every endpoint on this page lives under https://app.tease.link and requires a Bearer key with the write scope (reads accept any key). See Authentication. A few owner-only endpoints are called out inline.

Domains

A domain row is the anchor for an entire landing page. List the domains you own, create new ones, and copy a finished page onto another domain.

List domains

curl https://app.tease.link/api/admin/domains \
  -H "Authorization: Bearer $TEASE_API_KEY"
const res = await fetch('https://app.tease.link/api/admin/domains', {
  headers: { Authorization: `Bearer ${process.env.TEASE_API_KEY}` },
});
const { items } = await res.json();
import os, requests

res = requests.get(
    "https://app.tease.link/api/admin/domains",
    headers={"Authorization": f"Bearer {os.environ['TEASE_API_KEY']}"},
)
items = res.json()["items"]
{
  "items": [
    {
      "hostname": "links.creator.com",
      "display_name": "Instagram bio",
      "created_at": 1751280000,
      "button_count": 4,
      "infra_state": "ssl_active",
      "provisioning_error": null,
      "infra_attempted_at": 1751280000
    }
  ]
}

Each row carries an infra_state that drives the status badge in the dashboard:

infra_stateMeaning
unmanagedA bare domain row with no managed certificate.
provisioningSetup is running (TLS issuance) right after creation.
pending_dnsWaiting on DNS to point at TEASE — see custom domains.
ssl_activeCertificate issued and the page is serving over HTTPS.
failedThe last setup attempt failed; provisioning_error carries a message.

Create a domain

curl -X POST https://app.tease.link/api/admin/domains \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "hostname": "links.creator.com", "display_name": "Instagram bio" }'
const res = await fetch('https://app.tease.link/api/admin/domains', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TEASE_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ hostname: 'links.creator.com', display_name: 'Instagram bio' }),
});
const domain = await res.json();
res = requests.post(
    "https://app.tease.link/api/admin/domains",
    headers={"Authorization": f"Bearer {os.environ['TEASE_API_KEY']}"},
    json={"hostname": "links.creator.com", "display_name": "Instagram bio"},
)
domain = res.json()

Parameters

FieldTypeRequiredDescription
hostnamestringyesA valid host (my-site.com), 1–253 chars, lowercased. No scheme, no path.
display_namestringnoA friendly "traffic source" label shown in reports (e.g. FB Agency). Blank clears it.

A new domain is created with infra_state: "provisioning" and seeded with three default geo-segments plus empty notification/background rows, so it is editable immediately. Re-creating a hostname you already own within a minute returns the existing row (idempotent retry); otherwise a duplicate returns 409 domain_taken.

Plan limits

Each plan caps how many domains you can own. Creating one past your cap returns 409 domain_cap_reached.

Rename or delete a domain

PATCH updates the display_name (it is reporting metadata only and takes effect without a publish). DELETE removes the domain and cascades its buttons, segments, banner, and background.

# Rename
curl -X PATCH https://app.tease.link/api/admin/domains/links.creator.com \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "display_name": "TikTok bio" }'

# Delete
curl -X DELETE https://app.tease.link/api/admin/domains/links.creator.com \
  -H "Authorization: Bearer $TEASE_API_KEY"
await fetch('https://app.tease.link/api/admin/domains/links.creator.com', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.TEASE_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ display_name: 'TikTok bio' }),
});
requests.patch(
    "https://app.tease.link/api/admin/domains/links.creator.com",
    headers={"Authorization": f"Bearer {os.environ['TEASE_API_KEY']}"},
    json={"display_name": "TikTok bio"},
)

Copy a page onto another domain

copy-from replaces the target's draft buttons and segments with copies of a source domain's — a full draft clone you then publish. The page is rebuilt with fresh button identities so the live page refreshes cleanly.

curl -X POST https://app.tease.link/api/admin/domains/new-site.com/copy-from \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "source": "links.creator.com" }'
FieldTypeRequiredDescription
sourcestringyesThe hostname to copy the draft page from. Must differ from the target.

Companion endpoints copy a single part across domains: copy-notification-from (banner) and copy-bg-from (background). POST /api/admin/bulk/copy-content copies selected parts (buttons_segments, bg, notification, canvas_color) from one source onto many targets in a single call.

Custom domains

To serve a page on your own domain, connect it and verify DNS. Connect is a small state machine built on infra_state: a connect request opens the domain in pending_dns, and a successful verify flips it to active.

Start the connect

POST /api/admin/domains/connect registers the host and returns the exact DNS record to add. A subdomain (e.g. links.creator.com) gets a CNAME instruction; a bare apex (e.g. creator.com) gets an A record instruction. The response also includes the URL to call to verify.

curl -X POST https://app.tease.link/api/admin/domains/connect \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "hostname": "links.creator.com" }'
{
  "domain_id": 42,
  "hostname": "links.creator.com",
  "is_apex": false,
  "state": "pending_dns",
  "instructions": { "record_type": "CNAME", "name": "links", "value": "…" },
  "verify_url": "/api/admin/domains/42/verify"
}
FieldTypeRequiredDescription
hostnamestringyesThe custom host to connect. A host already taken returns 409 domain_taken.
providerstringnoA DNS provider hint so the returned instructions match your registrar's UI.

Add the DNS record

In your DNS provider, create the record exactly as returned in instructions. CNAME records can take a few minutes to propagate; apex A records can take longer.

Verify

Call the verify_url (or POST /api/admin/domains/{domain_id}/verify). While DNS has not landed yet, the response stays in pending_dns and lists what is still missing in issues:

{
  "domain_id": 42,
  "hostname": "links.creator.com",
  "state": "pending_dns",
  "issues": ["CNAME not found"]
}

Once the record resolves correctly, verify flips the domain to active and returns the live URL:

{
  "domain_id": 42,
  "hostname": "links.creator.com",
  "state": "active",
  "url": "https://links.creator.com"
}

The state machine is intentionally small — there are only two outcomes from pending_dns:

StateHow you get thereWhat it means
pending_dnsAfter connect, and after any verify while DNS is not yet correct.TEASE is waiting on your DNS record.
activeAfter a verify where the DNS record resolves correctly.The page serves over HTTPS on your domain.

Re-run verify as often as you like — it is idempotent and safe to poll while DNS propagates.

Segments

A geo-segment is an audience bucket. Visitors are matched to the first segment whose conditions fit, falling through to the one default catch-all. Each segment owns its own ordered button stack, so you can show a different funnel to (say) US mobile traffic than to everyone else.

A segment matches primarily by country. You can optionally narrow it with display conditions on device, OS, or time of day, and tag it with a target language for auto-translation.

Segments have their own «Сегменты» tab in the domain hub (/domains/<domain>/segments), next to «Кнопки». It lists every segment as a card in priority order (top matches first) — its countries and conditions, the default / block flags, the button-set A/B split, and up/down priority arrows. The segment's actual buttons are still edited in the «Кнопки» tab.

Country tiers

Your account has three reusable country tiers, shared across every domain:

  • Tier 1 — your top-paying markets.
  • Tier 2 — your mid markets.
  • Blocklist — countries you turn away with a 403 (they never enter the funnel).

They power two things: the T1 / T2 preset chips in the segment editor add the countries from your Tier 1 / Tier 2 to the segment you're building, and traffic is automatically A/B-weighted by tier (Tier 1 leans more aggressively toward the paid offer than Tier 2).

The tiers stay behind the scenes — they seed the preset chips and the A/B split, not a settings screen of their own. Every account starts with sensible defaults; to customise them, use the API/MCP get_config / set_config (the geo_classify key — { "tier1": [...], "tier2": [...], "blocklist": [...] } of ISO-3166 alpha-2 codes). Your changes apply only to your account.

List segments

curl https://app.tease.link/api/admin/segments/links.creator.com \
  -H "Authorization: Bearer $TEASE_API_KEY"
{
  "domain": "links.creator.com",
  "items": [
    {
      "segment_id": "s_1a2b3c4d",
      "domain": "links.creator.com",
      "name": "US mobile",
      "countries": ["US"],
      "position": 0,
      "is_default": false,
      "match": [{ "type": "device", "op": "is", "values": ["mobile"] }],
      "ab": null,
      "lang": ""
    },
    {
      "segment_id": "s_default",
      "domain": "links.creator.com",
      "name": "Everyone",
      "countries": [],
      "position": 1,
      "is_default": true,
      "match": null,
      "ab": null,
      "lang": ""
    }
  ]
}
FieldTypeDescription
segment_idstringStable id (s_<hex>). Used as the tier when managing this segment's buttons.
namestringA label you choose.
countriesstring[]ISO-3166 alpha-2 codes this segment targets. Empty = no country restriction.
positionintOrder in which segments are considered.
is_defaultboolWhether this is the catch-all. Exactly one segment per domain is the default.
matchobject[] | nullOptional display conditions (device / OS / time). null = country only.
abobject | nullOptional button-set A/B configuration for the segment.
langstringOptional target language (ISO code) for auto-translation, or "".

Create a segment

curl -X POST https://app.tease.link/api/admin/segments/links.creator.com \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "US mobile",
    "countries": ["US"],
    "match": { "all": [{ "type": "device", "op": "is", "values": ["mobile"] }] }
  }'

Parameters

FieldTypeRequiredDescription
namestringyes1–64 chars.
countriesstring[]noISO-3166 alpha-2 codes (US, GB). De-duplicated and upper-cased. Empty = no country restriction.
is_defaultboolnoMake this the catch-all. The first segment on a domain always becomes the default.
matchobjectnoDisplay conditions, { "all": [<clause>, …] }. Clauses are AND-ed. Omit for country-only matching.
langstringnoTarget language code for auto-translation (see translations).
Match clauses

A match clause narrows a segment beyond country. Each clause is one of:

ClauseShapeAllowed values
device{ "type": "device", "op": "is" | "is_not", "values": [...] }mobile, tablet, desktop
os{ "type": "os", "op": "is" | "is_not", "values": [...] }ios, android, windows, macos, other
time{ "type": "time", "tz": "<IANA tz>", "days": [0–6], "hours": [[start, end]] }days 0=Mon…6=Sun; hours are half-open [start, end) ranges with start under end, both 0–24

Within a clause, values are OR-ed; across clauses in all, conditions are AND-ed.

The default is the catch-all

Every domain keeps exactly one default segment. Promoting another segment to default (is_default: true) clears the flag on the previous one automatically; you cannot clear the only default directly.

Edit, reorder, delete

PATCH accepts any subset of the create fields. Omitting a field leaves it unchanged; sending an explicit empty conditions payload clears it.

# Edit
curl -X PATCH https://app.tease.link/api/admin/segments/links.creator.com/s_1a2b3c4d \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "countries": ["US", "CA"] }'

# Reorder — the list must be a permutation of the domain's current segment ids
curl -X PUT https://app.tease.link/api/admin/segments/links.creator.com/order \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "order": ["s_1a2b3c4d", "s_default"] }'

# Delete — pass ?force=true to also remove the segment's buttons
curl -X DELETE "https://app.tease.link/api/admin/segments/links.creator.com/s_1a2b3c4d" \
  -H "Authorization: Bearer $TEASE_API_KEY"

Order matters: segments are considered top-to-bottom, so place your most specific segments above the catch-all. Reorder validates that your order array is exactly the current set of segment ids (order_mismatch otherwise). Deleting a segment that still has buttons returns 409 segment_has_buttons unless you pass ?force=true; deleting the default returns 400 segment_default_required until you promote another segment first.

Direct redirects

Sometimes the landing itself is the wrong layer for an audience — you want the visitor on the destination instantly. Two levels of direct 307 redirects cover that, and both keep every hop logged and attributed like any other click.

Redirect segments

A segment can be flagged as a redirect segment: a visitor it matches never sees the landing — they are 307-redirected straight to that segment's primary button destination (the bottom-most one, the same button the landing paints as its main CTA). The hop is recorded as a click with its own source label, so redirected countries stay visible in your traffic reports and feed Telegram attribution. A block segment always wins over a redirect — a blocked country still gets its 403. The flag itself is provisioned by the platform for messenger-bound geo routes; it is not yet self-serve in the segment editor.

The destination lives in the BUTTON (fixed 2026-07-27)

Until 27 Jul 2026 the destination came from the TG_DIRECT_ACCOUNT_USERNAME server setting — one Telegram account for the whole platform, and only for the platform owner's domains: a tenant's redirect segment did not work at all, and the segment's buttons took no part whatsoever (editing a button changed nothing on the site). The target is now the same primary button the per-domain direct-redirect uses: each creator's own destination, visible and editable from the panel. No visible button with a destination → the landing is served (fail-safe).

In the panel such an audience carries a «→ редирект» badge, a notice above the list spells out the real destination, the target button is labelled, and the rest are dimmed.

Direct-redirect mode (per domain)

A whole domain can skip its landing: with direct-redirect mode on, TEASE geo-matches the visitor to a segment as usual and then 307s them straight to that segment's button destination — for a smart-link button that is the domain's /r/<slug> smart link, so click logging, click-id binding, and revenue attribution keep working end-to-end. Built for placements where an interstitial page only loses clicks (e.g. adult X/Twitter traffic).

curl -X POST https://app.tease.link/api/admin/domains/links.creator.com/direct-redirect \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true, "ab_pct": 0 }'
{ "domain": "links.creator.com", "direct_redirect": true, "ab_pct": 0 }
FieldTypeRequiredDescription
enabledboolyesTurn the mode on or off. Turning it off also resets the stored ab_pct to 0, so a later re-enable starts clean (everyone redirected).
ab_pctintnoA/B share, 0–100 — see below. Omit to keep the stored value; ignored while enabled is false.

The guardrails you'd expect all stay on:

  • The redirect target comes from the published page, so the toggle needs no re-publish and takes effect within about a minute. In the panel it is the «Прямой редирект» button next to «Импорт», above the button list.
  • The target is the audience's primary button — the bottom-most one, the same button the landing paints as its main CTA. The setting is per DOMAIN: it applies in EVERY audience, and each one sends its visitors to its own primary button.
  • Redirect and block segments still win: a matched redirect segment sends its own 307 (e.g. to Telegram), and a blocklisted country still gets 403.
  • Bots and scanners never see the destination — they get nothing, exactly like the cloaking layer.
  • In-app webviews (Instagram / Facebook / TikTok embedded browsers) still receive the landing: the destination won't open inside a hardened webview, so the landing's client-side escape must run there.
  • If the matched segment has nothing to redirect to, the landing is served — a fail-safe, never a dead redirect.

The panel shows each audience's fate (since 2026-07-27)

While the mode is on, the button list no longer looks like a normal page: the audience carries a «→ редирект» badge (or «→ N%» under A/B), a notice above the list says «visitors do NOT see the page, they go straight to …» with the real destination, the target button is labelled «сюда уходит редирект», and the rest are dimmed with «посетитель не увидит». Before this the panel drew every button the same way, hiding the fact that a visitor only ever reaches one.

Changing the target happens in the same dialog: the chosen button is moved to primary. The other buttons stay put — under A/B the landing arm still shows them.

A/B: landing vs. straight-to-offer

ab_pct turns the on/off into a live experiment on an enabled host: N% of visitors get the direct redirect (arm B), the rest get the normal landing funnel (arm A). 0 means everyone is redirected (no A/B); 100 states the same thing explicitly. The split is sticky per visitor and cookieless, and entry clicks carry the arm tag, so conversions attribute per arm — you measure which converts better instead of committing 100%. Domain rows in GET /api/admin/domains report direct_redirect and direct_redirect_ab_pct.

Buttons

Buttons live inside a segment. When managing buttons, the segment's segment_id is the tier path parameter — so buttons/{domain}/{segment_id} is "the buttons shown to this audience".

List a segment's buttons

curl https://app.tease.link/api/admin/buttons/links.creator.com/s_1a2b3c4d \
  -H "Authorization: Bearer $TEASE_API_KEY"
{
  "domain": "links.creator.com",
  "tier": "s_1a2b3c4d",
  "items": [
    {
      "button_id": "main",
      "domain": "links.creator.com",
      "tier": "s_1a2b3c4d",
      "position": 0,
      "label": "Subscribe",
      "text_value": "Subscribe",
      "url": "https://example.com/offer",
      "icon": "heart.svg",
      "hidden": false,
      "match": null,
      "variants": null,
      "popup": null
    }
  ]
}
FieldTypeDescription
button_idstringStable id within the segment. Server-generated when you omit it on create.
positionintOrder within the segment.
text_valuestringThe visible button text.
labelstringAn internal alias for logs/reports; defaults to text_value.
urlstringThe destination the button points to.
iconstringThe icon asset filename (name.svg) — see Icons.
hiddenboolWhether the button is hidden on the live page.
matchobject[] | nullOptional per-button display conditions (same clause shapes as segments).
variantsobject[] | nullOptional URL A/B variants for the button.
popupobject | nullThe button's teaser popup, or null when off.

Create a button

curl -X POST https://app.tease.link/api/admin/buttons/links.creator.com/s_1a2b3c4d \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text_value": "Subscribe",
    "url": "https://example.com/offer",
    "icon": "heart.svg"
  }'

Parameters

FieldTypeRequiredDescription
text_valuestringyesVisible button text, 1–64 chars.
urlstringyesDestination, http(s)://, up to 2048 chars.
iconstringyesIcon filename matching [A-Za-z0-9_-].svg.
labelstringnoInternal alias for reports. Defaults to text_value.
button_idstringnoProvide your own id, else one is generated.
matchobjectnoPer-button display conditions, { "all": [<clause>, …] }.
variantsobjectnoURL A/B set, { "variants": [{ "id", "weight", "url" }, …] } (2 or more variants).
popupobjectnoA teaser popup for the button.

Editing and ordering mirror segments:

# Edit any subset of fields
curl -X PATCH https://app.tease.link/api/admin/buttons/links.creator.com/s_1a2b3c4d/main \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "text_value": "Join now" }'

# Reorder within the segment
curl -X PUT https://app.tease.link/api/admin/buttons/links.creator.com/s_1a2b3c4d/order \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "order": ["main", "secondary"] }'

# Delete
curl -X DELETE https://app.tease.link/api/admin/buttons/links.creator.com/s_1a2b3c4d/main \
  -H "Authorization: Bearer $TEASE_API_KEY"

A/B in brief

Supplying two or more variants turns a button into an A/B test across destination URLs, weighted by weight. TEASE handles serving and keeping a given visitor on a consistent variant — you only declare the variants and read the results in your reports.

Teaser popup

Any button can carry a teaser popup — a small card with a photo and up to 160 characters of text that slides out under the button after a delay. Tapping the card clicks the button itself. A popup shows at most once per pageview, and a visitor who dismisses it won't see it again for the rest of their session.

Configure it in two steps: upload the photo (auto-cropped to a 480×360 WebP card), then set the popup object on the button via create or PATCH.

# 1. Upload the card photo (multipart, max 5 MB)
curl -X POST https://app.tease.link/api/admin/buttons/links.creator.com/s_1a2b3c4d/main/popup-photo \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -F "file=@teaser.jpg"
{ "filename": "popup_9f2c1a7b3d4e.webp", "url": "/notification/links.creator.com/popup_9f2c1a7b3d4e.webp" }
# 2. Attach the popup to the button
curl -X PATCH https://app.tease.link/api/admin/buttons/links.creator.com/s_1a2b3c4d/main \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "popup": {
      "enabled": true,
      "text": "Tonight’s set is 50% off — 2 hours only",
      "delay_ms": 4000,
      "photo": "popup_9f2c1a7b3d4e.webp"
    }
  }'
FieldTypeRequiredDescription
enabledboolyesfalse (or empty text) clears the popup.
textstringyesThe card text, up to 160 chars.
delay_msintnoDelay before the card slides out, 500–30000 ms (default 4000).
photostringnoA filename returned by the popup-photo upload.

The popup object replaces the stored popup wholesale. Re-uploading identical photo bytes is idempotent (same content → same filename), so retries are safe.

Icons

Every button references an icon by filename. The icon library ships ~290 built-in icons — brand marks plus generic glyphs, searchable and categorised in the panel's icon picker — and holds your own uploads next to them. Built-ins are shared; anything you upload or generate is private to your workspace.

# List everything you can use (built-ins + your own)
curl https://app.tease.link/api/admin/icons \
  -H "Authorization: Bearer $TEASE_API_KEY"

# Upload your own SVG (sanitized, max 1 MB)
curl -X POST https://app.tease.link/api/admin/icons \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -F "file=@custom.svg"

Generate an icon with AI

Can't find the right glyph? POST /api/admin/icons/generate draws a new icon in the house style — white on a transparent background, so it sits next to the built-ins seamlessly — and saves it straight to your private library.

curl -X POST https://app.tease.link/api/admin/icons/generate \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "prompt": "paper plane", "style": "line" }'
{ "filename": "gen_7c2f91d3a4b5.svg", "builtin": false, "uploaded_at": 1752537600 }
FieldTypeRequiredDescription
promptstringyesThe icon concept, in any language, up to 200 chars.
stylestringnoline (stroke outline, default) or solid (filled glyph).

Use the returned filename as a button's icon. Generation is rate-limited to 6 per minute (429 icon_gen_rate_limited); a failed attempt returns 502 icon_ai_failed — just retry with a reworded prompt.

Notification banner

The banner is a per-domain, push-style card (avatar, title, body, and a call-to-action link) that appears on the page after a short delay. There is one banner per domain — you can edit it by hand, or drive it from the reusable notification library (where two or more enabled presets turn it into a creative A/B test).

Read and edit

curl https://app.tease.link/api/admin/notification/links.creator.com \
  -H "Authorization: Bearer $TEASE_API_KEY"
{
  "domain": "links.creator.com",
  "enabled": true,
  "avatar_filename": "av_9f.png",
  "sound_filename": null,
  "title": "New drop",
  "body": "Tap to see today's set",
  "link_text": "Open",
  "target_url": "https://example.com/offer",
  "delay_ms": 2500,
  "translations": {},
  "style": null,
  "preset_id": null,
  "assignment": null
}

PATCH accepts any subset of the editable fields:

curl -X PATCH https://app.tease.link/api/admin/notification/links.creator.com \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "enabled": true,
    "title": "New drop",
    "body": "Tap to see today’s set",
    "link_text": "Open",
    "target_url": "https://example.com/offer",
    "delay_ms": 2500
  }'

Parameters

FieldTypeRequiredDescription
enabledboolnoWhether the banner shows on the live page.
titlestringnoBanner headline, up to 128 chars.
bodystringnoBanner text, up to 256 chars.
link_textstringnoCall-to-action label, up to 64 chars.
target_urlstringnoWhere the call-to-action goes (http(s)://). Required to enable only in the manual link mode; in smart-link mode it's just the last-resort fallback. Empty clears it.
link_modestringnoWhere a click goes: "" (default) = smart link — mirror the matched segment's primary button; "manual" = own linktarget_url wins. See below.
delay_msintnoDelay before the banner appears, 500–10000 ms.
translationsobjectnoPer-locale overrides of the public text — see translations.
assignmentobjectnoWhich library presets this banner serves, { "items": [...] } — see Notification library & A/B. { "items": [] } clears it.
styleobjectnoBanner appearance overrides — see Appearance. {} resets to the stock look.

Where the click goes

The banner's call-to-action has two modes, switched by link_mode:

  • Smart link ("", the default) — a click mirrors the primary button of the segment the visitor matched, following the exact tier-aware routing (and A/B arm) the buttons themselves use. No URL is needed to enable the banner in this mode; a stored target_url serves only as a last-resort fallback when the matched segment has no button to mirror.
  • Own link ("manual") — the banner goes exactly where you point it: target_url is required (enabling without it returns 400 notification_incomplete) and wins over the segment mirror.

Appearance

The banner's look is styled independently of its content: the style object on the same PATCH carries the appearance knobs. Only the keys you set are stored; send {} to reset the banner to the stock look. The current value comes back in the style field on reads.

curl -X PATCH https://app.tease.link/api/admin/notification/links.creator.com \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "style": {
      "theme": "glass",
      "opacity": 0.9,
      "radius": 18,
      "avatar_shape": "circle",
      "position": "top",
      "accent": "#e0426e"
    }
  }'
KeyTypeDescription
themestringlight, dark, or glass.
opacityfloatCard opacity, 0.3–1.
radiusintCorner radius in px, 0–32.
avatar_shapestringcircle, rounded, or square.
positionstringtop or bottom — the edge the banner slides in from.
accentstringAccent colour for the call-to-action, #rrggbb.
dimfloatReadability scrim behind the banner text, 0–0.8. Omitted = an automatic 0.22 scrim on the glass themes; an explicit 0 turns it off.
text_sizefloatBanner text size multiplier, 0.85–1.3.
fontstringBanner typeface — same font list as the button font.

Avatar and sound

The avatar image and optional notification sound are uploaded as files and can be removed:

# Upload an avatar (multipart)
curl -X POST https://app.tease.link/api/admin/notification/links.creator.com/avatar \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -F "file=@avatar.png"

# Remove the sound
curl -X DELETE https://app.tease.link/api/admin/notification/links.creator.com/sound \
  -H "Authorization: Bearer $TEASE_API_KEY"

Notification library & A/B

Instead of hand-editing every domain, keep your banner creatives as reusable presets in the notification library and assign them to domains. In the panel, the «Уведомление» tab is a wall of preset cards with checkboxes: a checked card is what the domain shows, two or more checked cards run a creative A/B test, and each card's weight sets its share of impressions. A given visitor sticks to one variant (rotation is sticky per subnet), so the test stays clean.

A preset carries the full creative — texts, the call-to-action link, delay, translations, plus its own master files (avatar/sound), stored under notification/_preset_<id>/ and served at /notification/_preset_<id>/<filename>. Whenever a preset is put on a domain, those masters are copied into the domain's folder, so the live banner never references a file that isn't there.

# List the library
curl https://app.tease.link/api/admin/library/notifications \
  -H "Authorization: Bearer $TEASE_API_KEY"

# Save a domain's current banner into the library — files included
curl -X POST https://app.tease.link/api/admin/library/notifications/from-domain \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "domain": "links.creator.com", "name": "July drop" }'

from-domain snapshots the domain's draft banner into a new preset together with its avatar/sound files and stamps the new preset_id back onto the domain, so the card shows its library binding. Errors: 400 name_required / 400 notification_empty (nothing to save), 409 preset_name_taken, 404 domain_not_found.

Assign presets to a domain

The assignment object on the banner PATCH is the API behind the checkboxes. It replaces the stored assignment wholesale; { "items": [] } clears it (back to the hand-edited single banner).

curl -X PATCH https://app.tease.link/api/admin/notification/links.creator.com \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "assignment": {
      "items": [
        { "preset_id": 7, "weight": 2, "enabled": true },
        { "preset_id": 9, "weight": 1, "enabled": true },
        { "preset_id": 4, "enabled": false }
      ]
    }
  }'
FieldTypeRequiredDescription
preset_idintyesA preset id from GET /api/admin/library/notifications.
weightintnoRelative share of impressions in a 2+ preset A/B (default 1). Here preset 7 serves ~2/3 of visitors.
enabledboolnoThe checkbox (default true). A disabled item stays in the list but never serves.

Every enabled preset is validated on save: an unknown id returns 404 preset_not_found, and a preset that is missing its texts, avatar, or — in the own-link mode — its link returns 400 preset_incomplete — a broken arm can never silently eat a share of your traffic. The first enabled preset is stamped into the domain banner (texts, files, and preset_id) and every enabled preset's master files are copied into the domain folder, so a single-preset assignment serves its creative immediately.

Preset avatars

Each preset owns a master avatar, managed on the preset itself (multipart image up to 5 MB, center-cropped to a 192 px square WebP with a content-hash filename):

# Upload / replace the master avatar
curl -X POST https://app.tease.link/api/admin/library/notifications/7/avatar \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -F "file=@avatar.jpg"

# Remove it (bound domains lose it too and their banners are disabled)
curl -X DELETE https://app.tease.link/api/admin/library/notifications/7/avatar \
  -H "Authorization: Bearer $TEASE_API_KEY"

In the panel you can pick the avatar straight from your photo gallery or upload a new image — an uploaded photo is saved into the gallery as well, so it's reusable next time.

Editing and applying presets

PATCH /api/admin/library/notifications/{preset_id} edits propagate immediately: every domain whose assignment enables the preset is re-stamped and its draft re-baked, so a text or avatar fix reaches all bound drafts in one call — going live stays an explicit /publish. POST /api/admin/library/notifications/{preset_id}/apply (body { "domains": [...] }) stamps a preset onto whole domains at once: it writes a single-preset assignment, materializes the master files into each domain folder, and enables the banner only when the preset is complete (all texts, an avatar, and — in the own-link mode — the link).

Enabling follows preset readiness. On every re-stamp the bound domains' enabled is derived from the preset itself: the assignment checkbox records the intent to show, and the banner turns on automatically the moment the preset becomes complete (all texts, an avatar, and — in the own-link mode — the link) — say, when the missing avatar finally lands in the card — and honestly turns off when the preset stops being complete.

Background

Each domain has a background made of up to three pieces: a mobile image, a desktop image, and a solid canvas colour painted behind the card on desktop. Images go through an upload step and an edit step; the canvas colour is a single call.

Quick pick: in the «ник модели» card (the «Кнопки» tab), clicking the avatar opens the background library — choose one (→ single bg) or several (→ random rotation) without leaving the buttons editor. Uploading and cropping new backgrounds still lives in the «Дизайн» tab.

Upload and edit an image

bg/upload saves the original image for a (domain, variant) pair, where variant is mobile or desktop. bg/edit applies your crop/transform design state and regenerates the served variants.

# 1. Upload the source image for the mobile background
curl -X POST "https://app.tease.link/api/admin/bg/upload?domain=links.creator.com&variant=mobile" \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -F "file=@bg-mobile.jpg"

# 2. Commit the editor design state
curl -X POST "https://app.tease.link/api/admin/bg/edit?domain=links.creator.com&variant=mobile" \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "designState": { } }'
const form = new FormData();
form.append('file', file); // a Blob/File of the image
await fetch(
  'https://app.tease.link/api/admin/bg/upload?domain=links.creator.com&variant=mobile',
  { method: 'POST', headers: { Authorization: `Bearer ${process.env.TEASE_API_KEY}` }, body: form },
);
Query / bodyTypeRequiredDescription
domainqueryyesThe domain to set the background for.
variantqueryyesmobile or desktop.
filemultipartyes (upload)The source image.
designStatebodyyes (edit)The editor's crop/transform state object. Pass {} to use the image as-is.

Canvas colour

curl -X POST "https://app.tease.link/api/admin/bg/canvas-color?domain=links.creator.com" \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "color": "#0d0604" }'
FieldTypeRequiredDescription
colorstringyesA #rrggbb hex colour.

Button appearance

Built-in (platform) designs expose a landing-wide button style — shape, fill, opacity, colours, label contrast, typography, icon colours, alignment, and an idle animation. It follows the same draft → publish lifecycle as everything else and is set through POST /api/admin/bg/ui, which carries three independent sections: buttons (this one), profile, and gate. In the panel they live in the domain hub («Дизайн» / «Лендинг»), with a live preview of every knob.

A section object replaces the stored section wholesale (send the full style you want); null resets it to the stock look. Only the keys you set are stored — anything omitted keeps the design's default.

curl -X POST "https://app.tease.link/api/admin/bg/ui?domain=links.creator.com" \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "buttons": {
      "shape": "pill",
      "fill": "solid",
      "opacity": 0.95,
      "color": "#e0426e",
      "text": "auto",
      "animation": "breathe",
      "anim_speed": "slow",
      "font": "unbounded",
      "align": "icon_left"
    }
  }'

# Back to the stock look
curl -X POST "https://app.tease.link/api/admin/bg/ui?domain=links.creator.com" \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "buttons": null }'
KeyTypeDescription
shapestringrounded, pill, square, or sharp.
fillstringglass, solid, outline, soft_shadow, or hard_shadow.
opacityfloatButton background opacity, 0.05–1.
colorstringButton colour, #rrggbb. Empty string returns to the default.
shadow_colorstringShadow colour for the shadow fills, #rrggbb.
textstringLabel mode: auto (picked against the button colour), dark, or light.
animationstringnone, pulse, breathe, float, shake, glow, shine, wobble, or heartbeat.
anim_speedstringslow, normal, or fast.
fontstringLabel typeface: wix (default), gilroy, tiktok, satoshi, nunito, onest, clashdisplay, bebas, unbounded, prata, pacifico, or greatvibes — see Fonts.
text_sizefloatLabel size multiplier, 0.7–1.5.
text_colorstringExact label colour, #rrggbb — an explicit hex wins over the text mode. Empty string = auto (light or dark picked against the surface).
icon_colorstringIcon glyph colour, #rrggbb. Empty string returns to the stock white glyph.
icon_bgstringColour of the circle behind the icon, #rrggbb, or none to remove the circle. Empty string returns to the stock black circle.
alignstringcenter (icon + label centered as one group), icon_left (icon pinned to the left edge, label centered), or left (whole row flush left).
fillsarray of stringUp to 6 fill styles (same list as fill); with 2+ entries the button cycles through them instead of one flat fill. Under 2 entries collapses back to a plain fill.
hoverstringDesktop pointer-hover effect: lift, none, ink, glow, shine, tilt, press, or border.
new_tabboolOpen every button's link in a new tab instead of navigating away — some ad networks require this.
reveal_delayfloatSeconds before the whole button row fades in on load, 0–10 (0 = instant). For per-element scroll/delay/touch/exit triggers instead, see Appear rules.
widthintFixed button width in px, 160–360.
pad_yintVertical padding in px, 6–28.

See Style every button on the landing page for a full worked example, and Landing-page builder for the free-canvas layout, teaser stack, sticky bar, spotlight and appear-rules surface that lives alongside this section.

Fonts

The same twelve-face brand pack powers every typography knob on the page — button labels (buttons.font), the profile card (name_font / bio_font), and the notification banner (style.font):

IdFaceNotes
wixWix Madefor DisplayThe brand default — what you get when no font is set.
gilroyGilroyPremium geometric grotesque in the OnlyFans style (the OnlyFans logo itself is custom lettering — this is the closest typeface look).
tiktokTikTok SansTikTok's actual brand typeface (released under the OFL).
satoshiSatoshiModern grotesque. Latin-only.
nunitoNunitoRounded, friendly sans.
onestOnestClean geometric sans.
clashdisplayClash DisplayFashion-forward display face. Latin-only.
bebasBebas NeueTall condensed caps. Latin-only.
unboundedUnboundedWide crypto-modern display.
prataPrataElegant serif.
pacificoPacificoBold brush script.
greatvibesGreat VibesFormal calligraphic script.

Latin-only faces (satoshi, clashdisplay, bebas) have no Cyrillic glyphs — Cyrillic text set in them falls back to the brand default face automatically, so a Russian label never renders as tofu. Font ids from the retired v1 pack keep working on read: they simply render as the default.

Changing font re-renders every button label of the domain in the new face on the server (labels are pre-baked SVGs) — the response reports the count as relabeled. All faces are self-hosted under free licenses and served same-origin from your domain (/fonts/lf_<id>.woff2), so the page never makes a third-party font request.

Platform designs only

The style applies while the domain uses a built-in design. An uploaded custom-HTML design owns its own look and never inherits these settings — your tuning is kept though, so switching back to a platform design restores it.

Free plan badge

Landing pages of free-plan workspaces show a small "tease.link" badge under the buttons (on custom designs too). Upgrading removes it automatically — there is nothing to configure.

Profile card

The same bg/ui endpoint styles the profile card above the buttons — the nickname's typeface and size, the "Active now • Responds in 5 mins" status line, and an optional bio with its own typeface and size — via the second section, profile. Platform designs only, same draft → publish lifecycle, same replace-wholesale semantics: send the full section you want, or { "profile": null } to reset to the stock card.

curl -X POST "https://app.tease.link/api/admin/bg/ui?domain=links.creator.com" \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profile": {
      "name_font": "unbounded",
      "name_size": 1.2,
      "status_text": "Online now",
      "bio": "18+ • daily drops",
      "bio_font": "nunito",
      "bio_size": 0.9
    }
  }'
KeyTypeDescription
name_fontstringNickname typeface — same font list as the button font.
name_sizefloatNickname size multiplier, 0.7–1.6.
statusboolWhether the status line shows. false hides it; omitted = shown.
status_textstringCustom status-line text, up to 48 chars. Empty returns the stock wording.
biostringA short bio under the nickname/status, up to 160 chars. Empty removes it.
bio_fontstringBio typeface — same font list. Omitted = the brand default.
bio_sizefloatBio size multiplier, 0.7–1.6 (1 = stock).

18+ content gate

The same bg/ui endpoint carries an optional 18+ sensitive-content gate — a confirmation step shown before the page: either a full-screen cover (optionally blurring the page behind it) or a compact sheet from the top edge. A visitor confirms once per browser and is never asked again. Platform designs only, same draft → publish lifecycle.

curl -X POST "https://app.tease.link/api/admin/bg/ui?domain=links.creator.com" \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "gate": {
      "enabled": true,
      "mode": "cover",
      "blur": true,
      "title": "Adults only",
      "text": "This page contains sensitive content.",
      "ok": "I’m 18 or older",
      "leave": "Leave",
      "leave_url": "https://google.com"
    }
  }'
KeyTypeDescription
enabledboolWhether the gate shows.
modestringcover — full-screen confirm; drop — a sheet from the top edge.
title / textstringCustom copy (64 / 300 chars). Omit for the stock wording.
ok / leavestringButton labels (32 chars each).
leave_urlstringWhere the leave button sends visitors who decline. Empty = a neutral default.
blurboolBlur the page behind the cover-mode gate.

Send { "gate": null } to remove the gate entirely.

Most creators don't need this

The gate is for human visitors — an extra layer of conservatism for strict traffic sources. Bots, scrapers, and in-app link scanners are already handled: cloaking keeps your content buttons invisible to them without bothering real fans.

Desktop-wide mode, free canvas, stack, sticky bar & appear rules

bg/ui carries several more sections beyond buttons/profile/gate — full walkthroughs live under Landing-page builder, summarised here so the whole bg/ui surface is in one place:

SectionWhat it doesFull guide
desktopFull-monitor layout on wide screens instead of a phone-card. mode (phone/wide), align, width (280–560), dim (0–0.7), blur.Enable full-screen desktop mode
layoutmode: "free" switches the button column to a free-form canvas; pos places each button/object by id as {x, y, w, h}.Place elements freely on a canvas
objectsExtra canvas elements — items[] of type text/image/video/icon/shape/teaser/badge, up to 40.Place elements freely on a canvas
stackA swipeable/pinned stack of up to 8 locked teaser cards.Build a teaser card stack/carousel
stickyA bar that stays visible while scrolling, with its own CTA button style.Add a sticky call-to-action bar
appearPer-element show rules — react to scroll/delay/touch/exit instead of showing instantly, plus a delayed background reveal.Control when an element or background appears
exit / timerAn exit-intent popup and/or an urgency countdown.Add an exit-intent popup and/or an urgency timer

Spotlight — a whole design, not a bg/ui section

POST /api/admin/bg/design {"design": "spotlight"} switches the page to a second built-in design (alongside classic) that frames the hero photo fixed behind the buttons instead of a scrolling column. Its own spotlight bg/ui section (blur 0–40, focus_x/focus_y 0–100, zoom 100–300) tunes the photo's focal point, zoom and bottom blur. The free canvas (layout.mode: "free" + objects) is not available on the Spotlight design.

Languages & auto-translate

Segments carry an optional lang target, and the notification banner carries a translations map, so the public text can be auto-translated per locale. The translations object is keyed by language code and only ever holds the public text fields:

{
  "translations": {
    "es": { "title": "Nuevo drop", "body": "Toca para ver el set de hoy", "link_text": "Abrir" },
    "fr": { "title": "Nouveau drop", "link_text": "Ouvrir" }
  }
}

Supported codes include en, ru, pt, es, de, fr, it, nl, pl, tr, ar, hi, id, ja, ko, zh. The call-to-action destination is never part of a translation — only the visible title, body, and link_text.

QR codes

Every domain can hand out printable QR codes — a flyer, a sticker, a business card — that lead straight to the landing page and are tracked like any other traffic source, live outside the draft/publish cycle. See QR codes for creating, designing, downloading, reading a code's funnel, and renaming or disabling one without devaluing what's already printed.

Draft → preview → publish

Everything above writes to the draft. Nothing is live until you publish. The recommended loop:

Inspect the draft vs. published state

GET /api/admin/config returns both snapshots plus which domains are dirty:

curl https://app.tease.link/api/admin/config \
  -H "Authorization: Bearer $TEASE_API_KEY"
{
  "is_dirty": true,
  "dirty_domains": ["links.creator.com"],
  "draft": { "updated_at": 1751280600, "domains": {}, "segments": {}, "notifications": {}, "backgrounds": {} },
  "published": { "updated_at": 1751280000, "domains": {}, "segments": {}, "notifications": {}, "backgrounds": {} }
}
FieldTypeDescription
is_dirtyboolWhether any domain's draft differs from its published page.
dirty_domainsstring[]The hostnames with unpublished changes.
draft / publishedobjectPer-domain matrices of buttons, segments, notifications, and backgrounds for each state.

Preview a live audience

Request a signed preview token, then open the preview page with it. You can simulate the exact audience — country, device, OS, hour, and language — so you see what a US-mobile visitor at 9pm would see without changing anything live.

In the panel, the landing preview drives the same simulation through a single «Аудитория» selector: pick a segment (each option shows its country flags and button count) and the preview renders that audience. Under the hood the selector pins the segment via force_segment, so even a countryless default catch-all previews exactly. It stays in two-way sync with the segment rail, and the fine-grained country / OS / hour / language overrides live under «ещё…».

curl "https://app.tease.link/api/admin/preview-token?domain=links.creator.com&force_country=US&force_device=mobile" \
  -H "Authorization: Bearer $TEASE_API_KEY"
{
  "token": "…",
  "public_url": "https://links.creator.com",
  "frame_path": "/preview-frame/"
}
QueryTypeDescription
domainstringThe domain to preview.
force_countrystringSimulate a country (ISO alpha-2, upper-case).
force_devicestringmobile or desktop.
force_osstringios, android, windows, macos, or other.
force_hourintSimulate the hour of day, 0–23.
force_langstringSimulate a 2-letter language.
force_segmentstringRender exactly this segment (by segment_id), skipping the geo classifier. Unknown ids are ignored; a block segment still previews its 403.

The returned token is short-lived and authorizes only a preview of your own draft. Open public_url joined with frame_path, carrying the token, to render the simulated page.

Owner-only

preview-token is restricted to the account owner.

Publish (or discard)

When the preview looks right, publish the draft to the live page. Discard throws the draft away and restores it from the published page — your single global undo.

Publish and discard

POST /api/admin/publish copies the draft to the live page atomically. With no body it publishes every dirty domain; pass a domains array to publish only some.

# Publish everything that changed
curl -X POST https://app.tease.link/api/admin/publish \
  -H "Authorization: Bearer $TEASE_API_KEY"

# Publish only one domain
curl -X POST https://app.tease.link/api/admin/publish \
  -H "Authorization: Bearer $TEASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "domains": ["links.creator.com"] }'
await fetch('https://app.tease.link/api/admin/publish', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TEASE_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ domains: ['links.creator.com'] }),
});
requests.post(
    "https://app.tease.link/api/admin/publish",
    headers={"Authorization": f"Bearer {os.environ['TEASE_API_KEY']}"},
    json={"domains": ["links.creator.com"]},
)
FieldTypeRequiredDescription
domainsstring[]noHostnames to publish. Omit (or send no body) to publish all dirty domains. An explicit empty list is rejected.
{ "ok": true, "bg_files_by_domain": {}, "domains": ["links.creator.com"] }

Publishing with nothing changed returns 400 nothing_to_publish. POST /api/admin/discard takes the same optional domains selection and restores the draft from published; with nothing to undo it returns { "ok": true, "noop": true }.

discard is destructive to your draft edits — it replaces the draft with whatever is currently live. Your published page is untouched.

Audit feed

Every change above is journaled. GET /api/admin/audit returns the most recent entries (newest first), useful for an activity feed or change log.

curl "https://app.tease.link/api/admin/audit?limit=50" \
  -H "Authorization: Bearer $TEASE_API_KEY"
{
  "items": [
    {
      "id": 9012,
      "ts": 1751280600,
      "actor": "owner",
      "action": "publish",
      "payload": "{\"tiers_updated\": true}"
    }
  ]
}
FieldTypeDescription
limitqueryNumber of entries to return, 1–500 (default 100).
idintMonotonic entry id.
tsintUnix timestamp.
actorstringWho made the change.
actionstringThe change type (e.g. segment_create, publish).
payloadstring | nullA JSON string with action-specific details.

FAQ

Next steps

On this page