Skip to content

Pool, Results and Pages

Suggestions waiting on a decision, the results that say what the work changed, and the page registry underneath both.

Code example

Pool
curl -X GET 'https://api.trakkr.ai/get-opportunity-pool?brand_id=00000000-0000-4000-8000-81f286d10c3c&family=fix&limit=20' \
  -H "Authorization: Bearer $TRAKKR_API_KEY"
Response · 200 Success
{
  "opportunities": [
    {
      "id": "e1f2a3b4-...",
      "kind": "audit_fix",
      "family": "fix",
      "title": "Add a meta description to /help/returns",
      "impact": "high",
      "status": "new",
      "evidence": [
        {
          "kind": "signal",
          "source": "site_audit",
          "observed_at": "2026-07-28T06:00:00Z",
          "payload": { "label": "Missing since", "value": "12 days" },
          "deep_link": "https://app.trakkr.ai/optimize"
        }
      ],
      "page_id": "9a8b...",
      "page_url": "https://example.com/help/returns",
      "dedup_key": "audit_fix:meta_description:https://example.com/help/returns",
      "expires_at": "2026-08-18T06:00:00Z",
      "created_at": "2026-07-28T06:00:00Z"
    }
  ],
  "meta": { "total": 137, "limit": 20, "next_cursor": "o:20" }
}

Synthetic example. Values are made up.

Commit
curl -X POST 'https://api.trakkr.ai/commit-opportunity?brand_id=00000000-0000-4000-8000-81f286d10c3c' \
  -H "Authorization: Bearer $TRAKKR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"opportunity_id":"11111111-1111-4111-8111-111111111111","action":"commit"}'
Response · 200 Commit
{
  "status": "committed",
  "opportunity_id": "e1f2a3b4-...",
  "action_id": "a1b2c3d4-...",
  "measurement_plan": {
    "subject": { "page_id": "9a8b7c6d-..." },
    "primary": { "metric": "bot_fetches", "source": "crawler_logs" },
    "secondary": [],
    "window_days": 14,
    "moved_if": "after >= max(before*1.25, before+min_gain)",
    "harm_if": "after <= before*0.75",
    "rollback_ref": null
  }
}

Synthetic example. Values are made up.

Results
curl -X GET 'https://api.trakkr.ai/get-results?brand_id=00000000-0000-4000-8000-81f286d10c3c&verdict=earned&limit=20' \
  -H "Authorization: Bearer $TRAKKR_API_KEY"
Response · 200 Success
{
  "results": [
    {
      "id": "r1...",
      "action_id": "a1...",
      "verdict": "earned",
      "family": "fix",
      "summary": "Citations went from 2 to 6 over 14 days.",
      "primary_metric": {
        "label": "Citations",
        "before": 2,
        "after": 6,
        "source": "citations"
      },
      "window_days": 14,
      "reason": null,
      "measured_at": "2026-07-28T06:00:00Z",
      "rolled_back": false,
      "page_url": "https://example.com/help/returns"
    }
  ],
  "meta": { "total": 41, "limit": 20, "next_cursor": null },
  "verdict_counts": {
    "earned": 12,
    "no_change": 9,
    "couldnt_measure": 20
  }
}

Synthetic example. Values are made up.

Pages
curl -X GET 'https://api.trakkr.ai/get-pages?brand_id=00000000-0000-4000-8000-81f286d10c3c&ownership=owned&limit=50' \
  -H "Authorization: Bearer $TRAKKR_API_KEY"
Response · 200 Success
{
  "pages": [
    {
      "id": "9a8b7c6d-...",
      "url": "https://example.com/help/returns",
      "slug": "/help/returns",
      "ownership": "owned",
      "title": "Returns and refunds",
      "tracked": true,
      "bottleneck": "reached",
      "verdict": "This page is available but AI crawlers aren't fetching it.",
      "last_seen_at": "2026-07-28T06:00:00Z"
    }
  ],
  "meta": { "total": 812, "limit": 50, "next_cursor": "o:50" }
}

Synthetic example. Values are made up.

API key required

60 req/min reads, 30 req/min writes

How the three fit together

Every recommendation Trakkr makes lands in one pool. A person or an agent decides what to do with each one, and committing freezes how that change will be measured. When the window closes, the pipeline measures it and writes a result. Pages are what all of it hangs on: one row per URL, keyed the same way everywhere.

/get-opportunity-pool is not the same thing as /get-opportunities. The older endpoint lists citation outreach targets and keeps working exactly as it does today. This one is the unified pool that every recommendation system writes to.

Pool

GET/get-opportunity-pool

Authenticate with Authorization: Bearer $TRAKKR_API_KEY. Key and access guide

Request parameters

brand_idstringqueryRequired

Brand UUID

Format: uuid

familystring | nullqueryOptional

fix|refresh|create|earn|discuss|optimize|setup|play

kindstring | nullqueryOptional

Comma-separated kinds

impactstring | nullqueryOptional

low|medium|high

limitintegerqueryOptional

Default: 50 · Minimum: 1 · Maximum: 200

cursorstring | nullqueryOptional

Opaque cursor from meta.next_cursor

Responses and errors
200Successful Response

application/json

opportunitiesobject[]Required

Fields in opportunities

Array of object.

idstringRequired

kindstringRequired

familystringRequired

titlestringRequired

impactstringRequired

statusstringRequired

evidenceobject[]Optional

Default: []

Fields in evidence

Array of object. Default: []

object. Additional keys are allowed.

page_idstring | nullOptional

page_urlstring | nullOptional

dedup_keystring | nullOptional

expires_atstring | nullOptional

created_atstringRequired

metaobjectRequired

Cursor pagination (07 §11 rule 6). Never mixed with PaginationInfo.

Fields in meta

totalintegerRequired

limitintegerRequired

next_cursorstring | nullOptional

401Missing, malformed, or expired credentials

application/json

detailstring | object | nullOptional

errorstring | nullOptional

402The current plan does not fund this operation

application/json

detailstring | object | nullOptional

errorstring | nullOptional

403Invalid credentials, missing brand access, or insufficient role

application/json

detailstring | object | nullOptional

errorstring | nullOptional

422Validation Error

application/json

detailobject[]Optional

Fields in detail

Array of object.

locstring | integer[]Required

Fields in loc

Array of string | integer.

string

string.

integer

integer.

msgstringRequired

typestringRequired

429Rate limit exceeded

application/json

detailstring | object | nullOptional

errorstring | nullOptional

500The operation failed

application/json

detailstring | object | nullOptional

errorstring | nullOptional

503Authentication, entitlement, or a required dependency is temporarily unavailable

application/json

detailstring | object | nullOptional

errorstring | nullOptional

OpenAPI source

Suggestions waiting on a decision. The order is deterministic: impact bands first, then each family's freshest items in turn, so one high-volume source cannot crowd out everything else. Nothing here is committed work yet.

Only two statuses ever come back: new and seen. Anything committed, dismissed, snoozed or expired has left the pool, so there is no filter that brings those rows back here.

Commit, dismiss or snooze

POST/commit-opportunity

Authenticate with Authorization: Bearer $TRAKKR_API_KEY. Key and access guide

Request parameters

brand_idstringqueryRequired

Brand UUID

Format: uuid

JSON request body (required)

opportunity_idstringRequired

Format: uuid

actionstringOptional

Default: "commit"

reasonstring | nullOptional

snooze_daysinteger | nullOptional

Minimum: 1 · Maximum: 90

Responses and errors
200Successful Response

application/json

statusstringRequired

opportunity_idstringRequired

action_idstring | nullOptional

measurement_planobject | nullOptional

401Missing, malformed, or expired credentials

application/json

detailstring | object | nullOptional

errorstring | nullOptional

402The current plan does not fund this operation

application/json

detailstring | object | nullOptional

errorstring | nullOptional

403Invalid credentials, missing brand access, or insufficient role

application/json

detailstring | object | nullOptional

errorstring | nullOptional

422Validation Error

application/json

detailobject[]Optional

Fields in detail

Array of object.

locstring | integer[]Required

Fields in loc

Array of string | integer.

string

string.

integer

integer.

msgstringRequired

typestringRequired

429Rate limit exceeded

application/json

detailstring | object | nullOptional

errorstring | nullOptional

500The operation failed

application/json

detailstring | object | nullOptional

errorstring | nullOptional

503Authentication, entitlement, or a required dependency is temporarily unavailable

application/json

detailstring | object | nullOptional

errorstring | nullOptional

OpenAPI source

One explicit verb per call. Committing creates tracked work and freezes its measurement plan, which is what makes a before/after result possible later. Dismissing takes a reason. Snoozing hides a suggestion and brings it back.

This endpoint cannot grant an agent permission to do anything. Autonomy is set per brand inside the product, never over the API.

A commit returns status, opportunity_id, action_id and the frozen measurement_plan. Dismiss and snooze return only status and opportunity_id. Setup work is never measured, so its plan comes back null, and committing something already committed returns already_accepted with the existing action id.

snooze_days is validated, not just documented: anything outside 1-90 is rejected with 422 before the call runs. An action other than commit, dismiss or snooze returns 400, and an opportunity_id that belongs to another brand returns 404.

Results

GET/get-results

Authenticate with Authorization: Bearer $TRAKKR_API_KEY. Key and access guide

Request parameters

brand_idstringqueryRequired

Brand UUID

Format: uuid

verdictstring | nullqueryOptional

earned|no_change|harm|couldnt_measure

familystring | nullqueryOptional

daysinteger | nullqueryOptional

Only Results from the last N days

Minimum: 1 · Maximum: 3650

limitintegerqueryOptional

Default: 50 · Minimum: 1 · Maximum: 200

cursorstring | nullqueryOptional

Responses and errors
200Successful Response

application/json

resultsobject[]Required

Fields in results

Array of object.

idstringRequired

action_idstringRequired

verdictstringRequired

familystringRequired

summarystringRequired

primary_metricobject | nullOptional

window_daysinteger | nullOptional

reasonstring | nullOptional

measured_atstringRequired

rolled_backbooleanOptional

Default: false

page_urlstring | nullOptional

metaobjectRequired

Cursor pagination (07 §11 rule 6). Never mixed with PaginationInfo.

Fields in meta

totalintegerRequired

limitintegerRequired

next_cursorstring | nullOptional

verdict_countsobjectRequired

401Missing, malformed, or expired credentials

application/json

detailstring | object | nullOptional

errorstring | nullOptional

402The current plan does not fund this operation

application/json

detailstring | object | nullOptional

errorstring | nullOptional

403Invalid credentials, missing brand access, or insufficient role

application/json

detailstring | object | nullOptional

errorstring | nullOptional

422Validation Error

application/json

detailobject[]Optional

Fields in detail

Array of object.

locstring | integer[]Required

Fields in loc

Array of string | integer.

string

string.

integer

integer.

msgstringRequired

typestringRequired

429Rate limit exceeded

application/json

detailstring | object | nullOptional

errorstring | nullOptional

500The operation failed

application/json

detailstring | object | nullOptional

errorstring | nullOptional

503Authentication, entitlement, or a required dependency is temporarily unavailable

application/json

detailstring | object | nullOptional

errorstring | nullOptional

OpenAPI source

What completed work actually changed. Every row is computed by Trakkr's pipeline from real before/after data over the window that was frozen at commit time. Nothing here is self-reported.

A result never claims causation. It says a metric moved during the window, which is a different and more honest statement. Four verdicts are possible: earned (the metric cleared the bar the plan set), no_change (it held steady), harm (it fell, reported as "coincided with a drop"), and couldnt_measure (the data needed was not there, and the reason says which).

/get-proof remains available as a deprecated compatibility alias for existing integrations.

Pages

GET/get-pages

Authenticate with Authorization: Bearer $TRAKKR_API_KEY. Key and access guide

Request parameters

brand_idstringqueryRequired

Brand UUID

Format: uuid

ownershipstring | nullqueryOptional

owned|competitor|editorial|social|video|other

trackedboolean | nullqueryOptional

limitintegerqueryOptional

Default: 50 · Minimum: 1 · Maximum: 200

cursorstring | nullqueryOptional

Responses and errors
200Successful Response

application/json

pagesobject[]Required

Fields in pages

Array of object.

idstringRequired

urlstringRequired

slugstringRequired

ownershipstringRequired

titlestring | nullOptional

trackedbooleanOptional

Default: false

bottleneckstring | nullOptional

verdictstring | nullOptional

last_seen_atstring | nullOptional

metaobjectRequired

Cursor pagination (07 §11 rule 6). Never mixed with PaginationInfo.

Fields in meta

totalintegerRequired

limitintegerRequired

next_cursorstring | nullOptional

401Missing, malformed, or expired credentials

application/json

detailstring | object | nullOptional

errorstring | nullOptional

402The current plan does not fund this operation

application/json

detailstring | object | nullOptional

errorstring | nullOptional

403Invalid credentials, missing brand access, or insufficient role

application/json

detailstring | object | nullOptional

errorstring | nullOptional

422Validation Error

application/json

detailobject[]Optional

Fields in detail

Array of object.

locstring | integer[]Required

Fields in loc

Array of string | integer.

string

string.

integer

integer.

msgstringRequired

typestringRequired

429Rate limit exceeded

application/json

detailstring | object | nullOptional

errorstring | nullOptional

500The operation failed

application/json

detailstring | object | nullOptional

errorstring | nullOptional

503Authentication, entitlement, or a required dependency is temporarily unavailable

application/json

detailstring | object | nullOptional

errorstring | nullOptional

OpenAPI source

One row per URL the brand owns or appears on. Because everything is keyed on one canonical URL form, a page here is the same page across crawler data, citations, audits and search. Rows carry the page's bottleneck, meaning the first stage of its funnel that is stuck.

Rows come back newest last_seen_at first. Each one carries id, url, slug, ownership, title, tracked, bottleneck, verdict and last_seen_at. The bottleneck is a funnel stage name (available, reached, understood, relevant, selected or visited) and the verdict is the plain sentence that goes with it. Both are null until there is enough data to name one.

Pagination

These endpoints use cursors, not offsets. Read meta.next_cursor and pass it back as cursor to get the next page. A null cursor means you have reached the end. Older endpoints such as /get-actions keep their existing offset pagination unchanged.