# Trakk Agency API - Skills

Skills for fetching SEO report content and performing common integration tasks via the Trakk Agency API.

**Base URL:** `https://admin.trakk.ai/api/v1`
**Auth:** `Authorization: Bearer YOUR_API_TOKEN` on every request.

---

## Skill 1 - Fetch the latest report content for a project

Use this when you need the generated SEO content (headings, body text, metadata, etc.) for the most recent report.

**Steps:**

1. Call `GET /projects/{project}/reports/latest` to get the latest report.
2. Check `data.status === "completed"`. If not, the content is not ready yet.
3. Call `GET /projects/{project}/reports/{data.report_number}/content` to get the full content.
4. Iterate `data.keywords`. For each keyword where `has_content === true`, read `content` and `html`.
5. Check `data.content_scheme` first - it determines the shape of `content` and `html` for every keyword in this report.

```
GET /projects/42/reports/latest
→ { data: { report_number: 14, status: "completed", ... } }

GET /projects/42/reports/14/content
→ { data: { content_scheme: "h3_text_blocks", keywords: [...] } }
```

**Content shape** depends on `content_scheme`:
- Array schemes (e.g. `h3_text_blocks`, `metadata`, `h1_headlines`) → `content` is an array of variant objects; pick one.
- Object schemes (e.g. `single_text_block`, `bullet_points`, `subnavigation`) → `content` is a single object; use directly.

The `html` field mirrors the same structure with pre-rendered HTML strings ready to embed.

---

## Skill 2 - Sync content for all active projects

Use this for a nightly or scheduled batch job that pulls new content across all projects.

**Steps:**

1. Page through `GET /projects?status=active` until `meta.last_page` is reached.
2. For each project, compare `latest_report_number` against the last value you synced.
3. If it has increased (or is new), fetch `GET /projects/{id}/reports/{latest_report_number}/content`.
4. Process each keyword with `has_content === true`.

**Stable sync key:** `report_number` scoped to a project (1-26). Use `report_id` if you need a globally unique reference.

```
GET /projects?status=active&page=1
→ paginate until meta.last_page

for each project where latest_report_number > last_synced:
  GET /projects/{id}/reports/{latest_report_number}/content
```

---

## Skill 3 - Audit keyword content coverage

Use this to find which keywords have never received content, or to check coverage for a specific report.

**Which keywords have no content at all:**
```
GET /projects/42/keywords
→ filter: data where content_count === 0
```

**Which keywords are missing content for a specific report:**
```
GET /projects/42/reports/14/content
→ filter: data.keywords where has_content === false
→ data.summary.keywords_with_content vs data.summary.keyword_count
```

**Which reports have content for a given keyword:**
The `content_action_ids` array on each keyword lists all report numbers (1-26) that have content for it.

---

## Skill 4 - Publish pre-rendered HTML into a CMS

Use this when the downstream system accepts HTML directly. The `html` field provides ready-to-embed fragments.

**For heading+text schemes** (`h2_text_blocks`, `h3_text_blocks`, `h4_text_blocks`):
```
keyword.html[0].h2_and_text   // "<h2>...</h2>\n<p>...</p>"
keyword.html[0].h3_and_text
keyword.html[0].h4_and_text
```

**For metadata scheme:**
```
keyword.html[0].meta_title         // "<title>...</title>"
keyword.html[0].meta_description   // "<meta name="description" content="...">"
```

**For single-object schemes** (`bullet_points`, `numbered_points`, `subnavigation`):
```
keyword.html.bullet_points    // "<h2>...</h2>\n<ul>...</ul>"
keyword.html.numbered_points  // "<h2>...</h2>\n<ol>...</ol>"
keyword.html.subnavigation    // "<div>...</div>"
```

See AGENT.md for the full content scheme reference table.

---

## Skill 5 - Resolve users and reference data

The API embeds compact user objects (`{id, name, role, agency: {id, name}}`) in report fields like `approved_by` and `managed_by`. To get all users visible to your token:

```
GET /users        → list of users (same compact shape as embedded objects)
GET /users/{id}   → single user
```

Country and language IDs appear in project metadata. Resolve them with:
```
GET /countries    → [{id, label}]
GET /languages    → [{id, label}]
```

---

## Error handling

All errors use the same envelope:
```json
{ "error": { "code": "not_found", "message": "..." } }
```

| Code | HTTP | Action |
|---|---|---|
| `unauthenticated` | 401 | Check your Bearer token |
| `forbidden` | 403 | Wrong agency, or IP not whitelisted |
| `not_found` | 404 | Project or report doesn't exist |
| `bad_request` | 400 | Invalid parameter (message says which) |
| `rate_limit_exceeded` | 429 | Back off; check `Retry-After` header |
| `server_error` | 500 | Retry once; escalate if persistent |
