# Share links

> Create and manage PIN-gated public pages where creators can see their own analytics and payouts.

A **share link** is a creator's PIN-gated public page. See the [Shareable creator pages guide](https://www.trackagoat.com/docs/guides/shareable-pages.md) for what a creator actually sees.

## The URL is derived from the creator id

There is no share-link identifier to store or look up. Every page lives at:

```
https://www.trackagoat.com/share/creator/<creator_id>
```

So any creator id from any endpoint can be turned into a link with string concatenation. The `url` field is returned for convenience, but you never need to fetch it.

The corollary is that the URL is **not** a secret: the 4-digit entrance PIN is the access control. Unlock attempts are rate limited and locked out after 10 failures.

## Permissions

Every share-link endpoint requires **two** things:

1. The right API key scope (`read` to fetch, `write` to modify).
2. The member the key acts as must be an **org admin**. Creating a public link is a trust decision, so a `write`-scope key acting as a non-admin still gets `403 insufficient_role`.

## PIN visibility

PINs are omitted by default. This is deliberate: a `read`-scope agent sweeping `/api/v2/creators` should not pull every entrance PIN in the org into its logs.

| Situation | Is `pin` returned? |
|-----------|--------------------|
| Any read, by default | No |
| `GET /creators/{id}/share-link?include_pin=true` with the `write` scope | Yes |
| `GET /creators/{id}/share-link?include_pin=true` with only `read` | No (`403 insufficient_scope`) |
| `GET /creators` or `/creators/{id}` with `?include_pin=true` and the `admin` scope | Yes |
| `GET /creators` or `/creators/{id}` with `?include_pin=true` without `admin` | No (`403 insufficient_scope`) |
| `POST` (create) | Always. This is your one chance to capture it |
| `PATCH` that changed the PIN | Yes |
| `POST /share-links/bulk` | Always, for every creator |

## The share_link object

```json
{
  "url": "https://www.trackagoat.com/share/creator/3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "enabled": true,
  "sections": ["analytics", "payouts"],
  "pin": "4821",
  "created_at": "2026-07-29T14:02:11.482Z"
}
```

| Field | Type | Description |
|-------|------|-------------|
| `url` | string | The public page. Always `{site}/share/creator/{creator_id}`. |
| `enabled` | boolean | When `false`, the page renders a neutral "not available" state. |
| `sections` | array | Some of `"analytics"`, `"payouts"`. Never empty. |
| `pin` | string | The 4-digit entrance PIN. Present only per the table above. |
| `created_at` | string | ISO 8601. |

## Embedded on creator responses

`GET /api/v2/creators` and `GET /api/v2/creators/{id}` both carry a `share_link` field (`null` when the creator has no page). No second call is needed to list every creator's link.

```bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v2/creators?project_id=<uuid>"
```

```json
{
  "data": [
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "display_name": "Holden K",
      "handle": "holden.k",
      "share_link": {
        "url": "https://www.trackagoat.com/share/creator/3fa85f64-5717-4562-b3fc-2c963f66afa6",
        "enabled": true,
        "sections": ["analytics"],
        "created_at": "2026-07-29T14:02:11.482Z"
      }
    }
  ],
  "error": null,
  "meta": { "hasMore": false, "nextCursor": null }
}
```

Add `include_pin=true` with an `admin`-scope key to include PINs:

```bash
curl -H "Authorization: Bearer tga_<admin_key>" \
  "https://www.trackagoat.com/api/v2/creators?include_pin=true"
```

## GET /api/v2/creators/\{id\}/share-link

Returns the creator's share link, or `null` data if they don't have one.

### Query parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `include_pin` | boolean | Include the entrance PIN. Requires the `write` scope (the key's owner is already required to be an org admin here). |

```bash
curl -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v2/creators/<creator_id>/share-link"
```

## POST /api/v2/creators/\{id\}/share-link

Creates the page, or replaces the settings of an existing one (and re-enables it if it was disabled). Requires the `write` scope.

### Body

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `pin` | string | None | Exactly 4 digits. A random PIN is generated when omitted. |
| `show_analytics` | boolean | `true` | Show the analytics section. |
| `show_payouts` | boolean | `false` | Show the payout accruals section. |

At least one of `show_analytics` / `show_payouts` must be `true`, or the request returns `400 no_sections`.

```bash
curl -X POST -H "Authorization: Bearer tga_<key>" \
  -H "Content-Type: application/json" \
  -d '{"show_analytics": true, "show_payouts": true}' \
  "https://www.trackagoat.com/api/v2/creators/<creator_id>/share-link"
```

Returns `201` with the full object **including the PIN**. Supports `Idempotency-Key`.

> **Tip:**
>
> Capture the PIN from this response. You can read it back later with `GET /api/v2/creators/{id}/share-link?include_pin=true` and a `write`-scope key.

## PATCH /api/v2/creators/\{id\}/share-link

Requires the `write` scope. Send only the fields you want to change.

| Field | Type | Description |
|-------|------|-------------|
| `enabled` | boolean | `false` renders a neutral "not available" page without deleting the link. |
| `show_analytics` | boolean | Toggle the analytics section. |
| `show_payouts` | boolean | Toggle the payouts section. |
| `pin` | string | Set a specific 4-digit PIN. |
| `regenerate_pin` | boolean | Issue a new random PIN. |

Any PIN change (via `pin` or `regenerate_pin`) immediately invalidates every active unlock session, so anyone currently viewing the page must re-enter the new PIN. The new PIN is returned in the response.

```bash
# Rotate the PIN
curl -X PATCH -H "Authorization: Bearer tga_<key>" \
  -H "Content-Type: application/json" \
  -d '{"regenerate_pin": true}' \
  "https://www.trackagoat.com/api/v2/creators/<creator_id>/share-link"

# Turn the page off without deleting it
curl -X PATCH -H "Authorization: Bearer tga_<key>" \
  -H "Content-Type: application/json" \
  -d '{"enabled": false}' \
  "https://www.trackagoat.com/api/v2/creators/<creator_id>/share-link"
```

Returns `404` if the creator has no share link. Use `POST` to create one.

## DELETE /api/v2/creators/\{id\}/share-link

Requires the `write` scope. The URL then renders the same "not available" page as a disabled link.

```bash
curl -X DELETE -H "Authorization: Bearer tga_<key>" \
  "https://www.trackagoat.com/api/v2/creators/<creator_id>/share-link"
```

```json
{ "data": { "deleted": true }, "error": null, "meta": null }
```

## POST /api/v2/share-links/bulk

Creates or updates pages for up to 200 creators in one call. Requires the `write` scope.

### Body

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `creator_ids` | uuid[] | required | 1 to 200 creator ids. |
| `show_analytics` | boolean | `true` | Show the analytics section. |
| `show_payouts` | boolean | `false` | Show the payout accruals section. |
| `pin_mode` | `unique` \| `shared` | `unique` | `unique` gives each creator their own PIN; `shared` applies one PIN to the batch. |
| `pin` | string | None | Only meaningful with `pin_mode: "shared"`. Generated when omitted. |

```bash
curl -X POST -H "Authorization: Bearer tga_<key>" \
  -H "Content-Type: application/json" \
  -d '{"creator_ids": ["<uuid>", "<uuid>"], "show_analytics": true, "pin_mode": "unique"}' \
  "https://www.trackagoat.com/api/v2/share-links/bulk"
```

```json
{
  "data": [
    {
      "creator_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "url": "https://www.trackagoat.com/share/creator/3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "pin": "4821",
      "sections": ["analytics"],
      "status": "created"
    },
    {
      "creator_id": "00000000-0000-0000-0000-000000000000",
      "url": null,
      "pin": null,
      "sections": null,
      "status": "skipped",
      "error": "Creator not found in this organization"
    }
  ],
  "error": null,
  "meta": { "created": 1, "updated": 0, "skipped": 1, "pin_mode": "unique" }
}
```

Every PIN is returned, since a bulk create is the moment you distribute them. Creator ids outside the key's organization come back as `skipped` rather than an error; the endpoint never confirms whether an id exists elsewhere. Supports `Idempotency-Key`.

## Errors

| Code | Status | Meaning |
|------|--------|---------|
| `insufficient_scope` | 403 | Missing the required scope, or `include_pin=true` without `write` (share-link endpoint) or `admin` (creator list and detail). |
| `insufficient_role` | 403 | The member the key acts as is not an org admin. |
| `not_found` | 404 | No such creator in this org, or no share link to update. |
| `no_sections` | 400 | Both `show_analytics` and `show_payouts` were false. |
| `invalid_pin` | 400 | The PIN was not exactly 4 digits. |

---
Source: https://www.trackagoat.com/docs/api-reference/share-links
