---
name: terradium-setup-rest
description: Set up a Terradium project end to end over plain HTTP (no MCP install) — confirm the project, brand profile, competitors, keyword research, writer brief, tracked prompts and topics, and a first content plan drafted for the user to approve. Use when the user asks you to set up their Terradium project and gives you a td_mcp_ token.
---

# Set up a Terradium project (REST, no MCP)

You are setting up the user's Terradium project for them. Terradium tracks how
AI assistants (ChatGPT, Google AI Overviews, Perplexity, …) answer the questions
the user's customers ask, and writes articles to win those answers.

This is the same flow as [https://terradium.io/setup/mcp.md](https://terradium.io/setup/mcp.md), over plain HTTP. Use
it when you can't install an MCP server. The calls are the same tools the MCP
server exposes, so names, arguments and errors match. Read the MCP guide's
**Errors** and **Free and paid steps** tables too — they apply here unchanged.

The user is watching a checklist in the Terradium dashboard that ticks as each
step lands: brand profile → competitors → keyword research → prompts & topics →
first plan drafted.

## How to call Terradium

The user gave you a base URL and a token (`td_mcp_…`). The token is scoped to
exactly one project, so no call takes a project id. Put them in your shell:

```bash
export TERRADIUM_BASE="https://api.terradium.io/api/v1/agent"  # use the one the user gave you
export TERRADIUM_TOKEN="td_mcp_…"                                # never commit this
```

Every step is one POST. The body is the tool's arguments as a JSON object:

```bash
curl -sS -X POST "$TERRADIUM_BASE/tools/<tool_name>" \
  -H "Authorization: Bearer $TERRADIUM_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ ...arguments... }'
```

Below, `curl … /tools/<name>` is short for that command.

- `200 {"result": …}` — it worked.
- `402 {"error": {"code": "subscription_required", …}}` — a paid step without a
  subscription or trial. `details.billingUrl` is where the user starts one.
- `422 {"error": {"code", "message", "hint", "details"}}` — the tool refused,
  or the arguments don't match its schema. Branch on `code` (`would_overwrite`,
  `keyword_runs_exhausted`, `plan_prompt_limit`, `not_available`, `error`),
  show `message`, follow `hint`. `GET $TERRADIUM_BASE/tools` returns every tool
  with its JSON input schema.
- `400` — the body isn't a JSON object.
- `401` — the token is wrong or revoked. Ask the user for a new one.

Placeholders: `<Project>` is the project's `name` from step 0; `<jobId>`,
`<planId>` and `c1`, `c2`… are ids returned by earlier calls — copy them, never
invent them.

## Rules

- **Confirm the project first.** Step 0 is not optional.
- **Ask, don't guess.** Each step starts with a short question. Offer a
  suggestion when you can infer one from their website, but let the user
  confirm or correct it before you write it.
- **Never overwrite silently.** On `would_overwrite`, show `details.current`
  and resend with `"overwrite": true` only once the user agrees.
- **Confirm before spending.** Only `start_keyword_job` costs anything. Dry-run
  it, show the seeds and the result, and get an explicit yes.
- **Stop at a drafted plan.** This surface cannot activate a plan, generate or
  publish, and you shouldn't try another route. The user approves the plan in
  the dashboard.
- **Keep the token secret.** Never write it into a file you commit or echo it
  back in full.
- Be brief. One question at a time, a short summary after each write.

## Steps

### 0. Confirm the project · free

```bash
curl … /tools/get_setup_status -d '{}'
curl … /tools/get_account      -d '{}'
```

Ask: *"I'm connected to **<Project>** (<domain>). Is that the project you want
set up?"* Stop if it isn't. If the project already has data (brand profile,
writer brief, a completed `status.keywordJob`, plans, `starterPrompts`), say
what's there and work with it: reuse a completed keyword job instead of
spending another run, and remember that a new plan is added beside existing
ones. Note from `get_account` whether there is an active subscription or trial.

Skim the website if you can, and note its `<html lang="…">`.

### 1. Brand profile · free

Ask: *What do you sell, who do you sell it to, and which country and language
do your customers search in?*

```bash
curl … /tools/set_brand_profile   -d '{"sells":"…","audience":"…","language":"en","country":"US"}'
curl … /tools/set_project_details -d '{"websiteUrl":"https://example.com"}'   # only if it has none
curl … /tools/set_brand_entity    -d '{"orgName":"…"}'                         # optional
```

- `country` is ISO 3166-1 alpha-2, or `global` for no single home market
  (researched as US English). `language` (ISO 639-1) is the language customers
  **search in** — keyword research and AI Overviews use it. The project's
  `defaultLanguage` (`get_project_details`) is the language articles are
  **written in**; if the two differ, ask which the user means.
- `set_brand_profile` is a patch: omitted fields are kept, `""` clears one.
- The brand entity is optional. It is `complete` only with `orgName`, `url`
  (the website) and at least one `sameAs` link; it lists what is `missing`.
  Add `sameAs` (profile URLs) or `author` (`{"name","url","jobTitle"}`) only if
  the user gives them.

### 2. Competitors · free

Ask: *Which brands do you lose customers to?* If the project has sampled
answers, `suggest_competitors` lists domains AI answers cite for it; otherwise
suggest 3–5.

```bash
curl … /tools/suggest_competitors -d '{}'
curl … /tools/validate_domain     -d '{"domains":["hubspot.com","blog.pipedrive.com"]}'
curl … /tools/create_competitors  -d '{"domains":["hubspot.com","pipedrive.com"]}'
```

Track competitors at their **root domain** (`validate_domain` returns `root`
for a subdomain) unless the user competes with one section only.

### 3. Seed keywords · free

Ask: *Which keywords do you want to rank for?*

If they have none, **suggest 10 to 20 seed keywords** from their website and
competitors — short, commercial phrases a customer would search — and wait for
the user to confirm or edit the list.

- No place names in seeds (`payroll software`, not
  `payroll software indonesia`): the market comes from the brand profile.
- Seeds in the market language: Indonesian seeds for `id`, English for `en`.
- To research what a domain already ranks for, use
  `{"mode":"domain","domain":"example.com"}` in step 4 instead of seeds.

### 4. Keyword research · paid (one run)

```bash
curl … /tools/start_keyword_job -d '{"mode":"seed","seeds":["…","…"],"dryRun":true}'
# → {"result":{"dryRun":{"allowed":true,"remaining":4,…},"market":{…},"warnings":[…]}}
curl … /tools/start_keyword_job -d '{"mode":"seed","seeds":["…","…"]}'
# → {"result":{"jobId":"<jobId>","status":"pending",…}}
curl … /tools/get_keyword_job   -d '{"jobId":"<jobId>"}'
```

1. Dry-run. If not allowed with `subscription_required`, tell the user the step
   needs the 7-day free trial and share `details.billingUrl` (or the **Start
   trial** button on the checklist); wait, then dry-run again. Fix any seed
   `warnings`. If `market.source` is `deployment_default`, the brand profile
   has no country — go back to step 1.
2. Tell the user it uses 1 of their remaining runs, for which market. On yes,
   start it.
3. Poll `get_keyword_job` every 15–30 seconds until `status` is `completed` or
   `failed`. If it fails, retry once; then use the fallback in step 7. On
   `keyword_runs_exhausted` or `not_available`, tell the user, do steps 5–6,
   then use the fallback in step 7.
4. Review the clusters (`pages`, highest priority first, each with a
   `clusterId`; `averageDifficulty: null` means unknown) with the user. Note
   the ids they reject for step 7.

### 5. Writer brief · free

The writer brief is the markdown every article is written from: what the
brand sells, who for, how it talks, what to always and never say.

```bash
curl … /tools/get_project_context -d '{}'
curl … /tools/set_project_context -d '{"projectContext":"# Acme\n…"}'
```

If `projectContext` is filled, show it and ask whether to keep it. If it is
empty, the response carries a `template`: **draft the brief yourself** from the
website, the brand profile and the confirmed seeds, leave a `{placeholder}`
where you don't know something, show the draft, apply the user's edits, and
save it. Replacing an existing brief needs `"overwrite": true`, only once the
user agreed.

### 6. Topics and prompts · free

`get_setup_status.promptCap` says how many questions the plan tracks (`max`),
how many are `active`, how many are seeded `starters`, and how many you can
still add (`remaining`). Show any starters and ask whether to keep or archive
them. Never propose more questions than `remaining`.

From the confirmed clusters or seeds, propose **3–6 topics** and questions
worded the way a person types into ChatGPT. Show them, let the user edit, then:

```bash
curl … /tools/create_topics  -d '{"names":["Pricing","Integrations"]}'
curl … /tools/create_prompts -d '{"texts":["what is the best crm for a small agency?"],"topics":["Pricing"]}'
```

One `create_prompts` call per topic, one after another, max 50 questions per
call. Questions past the cap come back in `skipped` with `plan_prompt_limit`.

### 7. Draft the first content plan · free

- **With a completed keyword job:**

  ```bash
  curl … /tools/plan_from_keyword_job -d '{"jobId":"<jobId>","name":"<Project> first plan","excludeClusters":["c4"]}'
  ```

  The default is a 30-day plan of the highest-priority clusters, each with a
  written headline; `"planType":"14-day"` is a smaller start and `"all"`
  schedules every cluster. Clusters that didn't fit come back in
  `omittedClusters` for a later plan (`"includeClusters": [...]`).

- **Fallback (no keyword job):**

  ```bash
  curl … /tools/create_plan    -d '{"name":"<Project> first plan","planType":"30-day"}'
  curl … /tools/add_plan_items -d '{"planId":"<planId>","items":[{"title":"…","primaryKeyword":"…"}]}'
  ```

  One item per confirmed seed, with a real headline as `title`.

The plan is created as a **draft**.

### 8. Hand back

Tell the user, in a few lines: what you set, the plan's name and how many
articles it holds, and that it is waiting for their approval in **Plans** in
the Terradium dashboard. Nothing has been generated or published. If any
`{placeholder}` is left in the writer brief, say which.
