> ## Documentation Index
> Fetch the complete documentation index at: https://doc.trackrev.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Bio pages

> Create and manage link-in-bio pages with /api/v1/bio-pages. Every button gets a tracked short link, so you see clicks, visitors and revenue per button.

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST "https://app.trackrev.io/api/v1/bio-pages" \
    -H "Authorization: Bearer <your secret key>" \
    -H "Content-Type: application/json" \
    -d '{
      "slug": "acme",
      "title": "Acme",
      "bio": "Tools for small teams.",
      "links": [
        { "label": "Start free", "url": "https://acme.com/signup" },
        { "label": "Latest video", "url": "https://youtube.com/watch?v=abc" }
      ]
    }'
  ```
</RequestExample>

A bio page is a public list of links at `www.trackrev.io/<slug>`. Pages made through the API belong
to the workspace of the key that made them; they never appear in (or can be edited from) the
dashboard's free link-in-bio builder.

**Scope:** secret key (`lk_`). Any plan, except `/stats`, which needs a paid plan.

## Tracked buttons

Each link button is given a TrackRev short link on the `bio` channel, and the public page sends
visitors through it. That is what counts a click, and, with the pixel on the destination, the
revenue it earned. Sections (headings) have no link.

* A button keeps its short link, and its history, while its `id` stays the same, even if you change
  its `url` or `label`. **Send back the `id`s you received.**
* A button you leave out of a `links` update is removed, and its short link and click history with it.
* Tracking never blocks a save. If a short link can't be made (a free workspace at its link cap, for
  example) the button is saved and links straight to its URL; `tracked_link_id` and `short_url` are
  `null`. Save again to retry.

## Endpoints

| | |
| - | - |
| `POST /api/v1/bio-pages` | Create a page. `201`, or `200` for a known `external_id`. |
| `GET /api/v1/bio-pages` | List pages, newest first. `?limit` (max 200), `?cursor`, `?external_id`. |
| `GET /api/v1/bio-pages/:id` | One page, by id **or slug**. |
| `PATCH /api/v1/bio-pages/:id` | Update any subset of fields. |
| `DELETE /api/v1/bio-pages/:id` | Delete the page and its buttons' short links. |
| `GET /api/v1/bio-pages/availability?slug=` | Is this name free? |
| `GET /api/v1/bio-pages/:id/stats` | Per-button clicks, visitors, conversions, revenue. `?days` or `?from&to`. |

## Body

<ParamField body="slug" type="string" required>
  Create only; can't change later. 3–40 characters, lowercase letters, digits and hyphens (spaces and
  capitals are normalised). Reserved names, and names used by another page, a short link or a
  program code, are refused: `400` for reserved, `409` for taken.
</ParamField>

<ParamField body="title" type="string">Up to 60 characters.</ParamField>
<ParamField body="bio" type="string">Up to 280 characters.</ParamField>
<ParamField body="avatar_url" type="string | null">An `http(s)` image URL.</ParamField>

<ParamField body="links" type="array">
  Up to 50 buttons, replacing the whole list. Each: `id` (optional, 1–64 of `A-Z a-z 0-9 _ -`; one
  is generated if omitted), `type` (`link` or `section`), `label` (up to 80), `url` (`http(s)`, for
  links), `emoji`, `image_url`. A `tracked` field is ignored.
</ParamField>

<ParamField body="social_links" type="array">
  Up to 12 `{ platform, url }` icons: `instagram`, `tiktok`, `x`, `youtube`, `spotify`,
  `apple-music`, `facebook`, `linkedin`, `github`, `twitch`, `discord`, `email`.
</ParamField>

<ParamField body="theme" type="object">
  Colours, fonts and button style (`canvasMode`, `canvasValue`, `cardMode`, `cardBg`, `textColor`,
  `buttonStyle`, `buttonColor`, `buttonTextColor`, `buttonRadius`, `buttonShadow`,
  `buttonShadowColor`, `font`, `footerImage`). On update it is **merged** onto the current theme.
  A value outside its allowed set falls back to the default.
</ParamField>

<ParamField body="is_published" type="boolean" default="true">Unpublished pages return 404 publicly.</ParamField>
<ParamField body="show_branding" type="boolean" default="true">Show the "Made with TrackRev" footer.</ParamField>

<ParamField body="external_id" type="string">
  Create only. Your own id for the page (1–200 characters), unique per workspace. A create with a
  known `external_id` returns that page with `200` and changes nothing, so a retry never makes a
  second page.
</ParamField>

<ParamField body="metadata" type="object">A JSON object of up to 4 KB, stored with the page.</ParamField>

Create also honours an `Idempotency-Key` header, as `POST /links` does.

## Response

<ResponseExample>
  ```json Response theme={null}
  {
    "id": "3f8a92c1-0000-4000-8000-000000000001",
    "slug": "acme",
    "url": "https://www.trackrev.io/acme",
    "title": "Acme",
    "bio": "Tools for small teams.",
    "avatar_url": null,
    "theme": { "canvasMode": "solid", "canvasValue": "#f1f1ef", "…": "…" },
    "links": [
      {
        "id": "lnk_a1B2c3D4e5F6",
        "type": "link",
        "label": "Start free",
        "url": "https://acme.com/signup",
        "emoji": "",
        "image_url": null,
        "tracked_link_id": "6d0e…",
        "short_url": "https://app.trackrev.io/r/k7x2mfp"
      }
    ],
    "social_links": [],
    "is_published": true,
    "show_branding": true,
    "external_id": null,
    "metadata": null,
    "created_at": "2026-10-11T10:00:00Z",
    "updated_at": "2026-10-11T10:00:00Z"
  }
  ```
</ResponseExample>

## Stats

`GET /api/v1/bio-pages/:id/stats` returns `{ window, totals, links }`. Each entry in `links` is
`{ id, label, url, tracked_link_id, clicks, visitors, conversions, revenue }`, bots excluded.
`totals.visitors` adds the per-button counts, so someone who clicked two buttons counts twice. For
daily series or geography, pass the buttons' `tracked_link_id`s to [`/clicks?link_ids=`](/developers/endpoints/clicks).

Page views are not counted; only clicks on tracked buttons are.

## Limits

* Custom domains for bio pages aren't available through the API. Pages are served at
  `www.trackrev.io/<slug>`.
* The workspace's link cap applies to buttons (one link each). A free workspace past its cap saves
  untracked buttons.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.