# track

> Register or update things to follow: opportunities (a post, person, community or partner worth acting on), experiments (a channel attempt with a…

URL: https://mcp-docs.opennomos.com/docs/tools/growth/track

> Register or update things to follow: opportunities (a post, person, community or partner worth acting on), experiments (a channel attempt with a deadline and an optional stop rule) and short links (for click attribution). Only a URL or title is needed for an opportunity. Duplicate URLs update the existing opportunity. Stages can be set in any order and are never inferred. Up to 20 items per call; one failing item does not affect the others. Experiments are never concluded automatically: record a decision (stop, scale, extend, keep) when you decide.

| Field | Value |
| --- | --- |
| Pack | Core (`core`) |
| Estimated cost per call | $0 |
| Monthly pack limit | none |
| Project | resolved: explicit `project` → header → credential default → workspace default |

## Parameters

| Parameter | Required | Type | Description |
| --- | --- | --- | --- |
| `project` | no | `string` | Project slug or id. Omit to use the credential or workspace default. |
| `items` | yes | `object[]` | 1–20 items to register or update, e.g. [\{"type":"opportunity","opportunity":\{"url":"https://reddit.com/r/SaaS/comments/abc"\}\}]. |
| `items[].type` | yes | `"opportunity"` \| `"experiment"` \| `"link"` | What this item is; put its fields in the object of the same name. |
| `items[].id` | no | `string` | Existing opportunity or experiment id, to update it instead of creating a new one. |
| `items[].opportunity` | no | `object` | For type=opportunity. Fields: title, url, kind, summary, tags[], priority(-1\|0\|1), stage, experiment_id, next_followup(\{in_days\}\|\{at\}\|ISO\|null), source. A title or url is enough. A URL already tracked updates that opportunity. |
| `items[].experiment` | no | `object` | For type=experiment. Create: name, channel, metric, days\|deadline, optional hypothesis, target, stop_rule\{metric,op,value\}, stop_rule_text. With id, update: name, hypothesis, target, stop_rule_text, decision(stop\|scale\|extend\|keep), extend_days, note. metric: tracked, contacted, replied, willing_to_talk, converted, actions, clicks or outcome:&lt;type>. |
| `items[].link` | no | `object` | For type=link. Fields: target_url (https), channel, label, opportunity_id, experiment_id, mode(redirect\|utm_only). Returns a short URL with UTM parameters. |

## Example call

```json
{
  "project": "acme-crm",
  "items": [
    {
      "type": "opportunity",
      "opportunity": {
        "url": "https://www.reddit.com/r/SaaS/comments/1abc23/looking_for_a_lightweight_crm",
        "tags": [
          "crm"
        ],
        "next_followup": {
          "in_days": 2
        }
      }
    },
    {
      "type": "experiment",
      "experiment": {
        "name": "Reddit helpful replies",
        "channel": "reddit",
        "metric": "willing_to_talk",
        "days": 14,
        "target": 3,
        "stop_rule": {
          "metric": "willing_to_talk",
          "op": "<",
          "value": 2
        }
      }
    },
    {
      "type": "link",
      "link": {
        "target_url": "https://acme.com/pricing",
        "channel": "reddit",
        "experiment_id": "exp_7Hn2LpWc0"
      }
    }
  ]
}
```

REST: `POST https://service.opennomos.com/v1/tools/track` with that JSON as the body. MCP: `tools/call` with `name: "track"` and that JSON as `arguments`.

## Example result

Values are illustrative; the shape comes from the service code.

```json
{
  "project": {
    "id": "prj_k3Vd9aQx2",
    "slug": "acme-crm"
  },
  "results": [
    {
      "index": 0,
      "type": "opportunity",
      "status": "created",
      "id": "opp_Qm4x9TnB1",
      "stage": "discovered",
      "next_followup_at": "2026-10-18T09:00:00.000Z"
    },
    {
      "index": 1,
      "type": "experiment",
      "status": "created",
      "id": "exp_7Hn2LpWc0",
      "deadline_at": "2026-10-30T09:00:00.000Z"
    },
    {
      "index": 2,
      "type": "link",
      "status": "created",
      "mode": "redirect",
      "id": "lnk_Tz81vM2",
      "code": "aB3dE9xQ",
      "short_url": "https://service.opennomos.com/l/aB3dE9xQ",
      "final_url": "https://acme.com/pricing?utm_source=reddit&utm_medium=opennomos&utm_campaign=exp_7Hn2LpWc0&utm_content=aB3dE9xQ"
    }
  ],
  "summary": {
    "created": 3,
    "updated": 0,
    "duplicate": 0,
    "error": 0
  }
}
```

## Notes

- One call takes 1–20 items. Items are independent: one failing item gets `status: "error"` with a `code` and does not affect the others.
- **Opportunity**: a title or a URL is enough. A URL that is already tracked (after normalising Reddit, X and Xiaohongshu post URLs and stripping `utm_*`, `fbclid`, `gclid`) updates the existing opportunity and returns `duplicate` or `updated`. Stages can be set in any order and are never inferred.
- **Experiment**: create with `name`, `channel`, `metric` and exactly one of `days` or `deadline`. Update by passing the experiment `id`: `name`, `hypothesis`, `target`, `stop_rule_text`, `note`, and `decision` (`stop`, `scale`, `extend` or `keep`; `extend` also needs `extend_days`). Experiments are never concluded automatically. See [Experiments](https://mcp-docs.opennomos.com/docs/growth-loop/experiments.md).
- **Link**: returns a short URL (mode `redirect`) or a UTM-tagged URL only (mode `utm_only`). Links cannot be updated, only created. See [Attribution](https://mcp-docs.opennomos.com/docs/growth-loop/attribution.md).
- Writes are rate limited per workspace (60 per minute, per isolate).

## Errors

Rejected calls (bad input, unknown ids, rate limits) return `message` and `code`; over MCP the result has `isError: true`. See [Errors](https://mcp-docs.opennomos.com/docs/reference/errors.md).
