> ## Documentation Index
> Fetch the complete documentation index at: https://docs.topify.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Authorized AI agent setup

> Set up Topify with a tool-using AI agent after the user authorizes the account, team, and project details.

<Columns cols={3}>
  <Card title="Claude Code" icon="terminal">
    Use tool\_use to call the endpoints below directly from Claude Code.
  </Card>

  <Card title="Cursor" icon="code">
    Cursor's agent mode can call these endpoints via HTTP requests.
  </Card>

  <Card title="OpenClaw" icon="robot">
    Any tool-use-capable LLM agent can complete this flow.
  </Card>
</Columns>

These endpoints let AI agents set up Topify.ai from scratch and generate a full GEO visibility report using account details explicitly supplied or authorized by the user.

The full flow is three API calls:

1. **Create account** -- register with an email, receive credentials instantly
2. **Get API key** -- exchange credentials for an API key
3. **Create project** -- start brand tracking and receive results via webhook

<Warning>
  Use an email address supplied or explicitly authorized by the user. Agents must not invent a temporary email or admin identity. API keys can only be created by admins and currently grant access to the full team; project-scoped API keys are not available.
</Warning>

<Danger>
  Run this flow only in a trusted server-side environment. Never expose the generated password or API key in browser code, client-side applications, logs, prompts, chat history, or tool output shown to other users.
</Danger>

<Note>
  These endpoints are designed for **tool-use AI agents** such as Claude Code, Cursor, and OpenClaw that can make HTTP requests programmatically. If you are using a web-based AI chatbot (ChatGPT, Perplexity, etc.) that cannot call APIs directly, create your account and project manually at [app.topify.ai](https://app.topify.ai), then use the **Get API key** endpoint with your email and password to obtain a key for the read-only data endpoints.
</Note>

***

## Credits and rate limits

Every API key has a **rate limit tier** and a **monthly credit allowance**. Credits are consumed when projects fetch AI responses during bootstrap and daily refresh cycles.

### Rate limit tiers

Rate limits use a sliding 60-second window. The burst allowance lets you briefly exceed the base rate.

| Tier     | Requests / minute | Burst | Effective limit / window |
| -------- | ----------------- | ----- | ------------------------ |
| Standard | 60                | +10   | 70                       |
| Premium  | 300               | +50   | 350                      |

New API keys are created on the **standard** tier. Contact us to upgrade to premium.

When the limit is exceeded, the API returns `429 Too Many Requests` with a `Retry-After` header indicating how many seconds to wait.

### Research credits by plan

Each plan includes a monthly credit budget that determines how many AI research queries your projects can run.

| Plan                                          | Monthly credits |
| --------------------------------------------- | --------------- |
| Skip Trial (default for API-created accounts) | 10              |
| Basic                                         | 200             |
| Pro                                           | 500             |
| Enterprise                                    | 2,000           |

Accounts created via the API start on the **skip\_trial** plan with 10 credits. Each project bootstrap consumes credits proportional to the number of prompts and AI providers queried (typically 5 prompts across 3 providers = 15 credits per full bootstrap cycle). The skip\_trial plan runs a reduced bootstrap that fits within the 10-credit budget. For a full bootstrap with all prompts and providers, upgrade to the Basic plan or higher from the [dashboard](https://app.topify.ai) or contact us.

### Response headers

Every response includes rate limit and credit information:

| Header                  | Description                                     |
| ----------------------- | ----------------------------------------------- |
| `X-RateLimit-Limit`     | Maximum requests allowed in the current window  |
| `X-RateLimit-Remaining` | Requests remaining before throttling            |
| `X-Credit-Available`    | Research credits remaining on your key          |
| `Retry-After`           | Seconds until the window resets (only on `429`) |

***

## Create account

```
POST /account/create
```

Creates a new Topify.ai account with a team on the `skip_trial` plan. The account is created without email verification, so it can be used immediately.

<Note>
  No authentication is required for this endpoint. The generated password is returned once and cannot be retrieved later -- store it securely.
</Note>

### Request body

| Field      | Type   | Required | Description                                                                                       |
| ---------- | ------ | -------- | ------------------------------------------------------------------------------------------------- |
| `email`    | string | Yes      | Email address supplied or explicitly authorized by the user. Do not generate a temporary identity |
| `password` | string | No       | Ignored. A secure password is generated server-side                                               |

```json theme={null}
{
  "email": "user@example.com"
}
```

### Response

```json theme={null}
{
  "code": 200,
  "message": "200 OK",
  "data": {
    "user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "team_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "email": "user@example.com",
    "password": "<generated-password-returned-once>"
  }
}
```

### Response fields

| Field      | Type          | Description                                  |
| ---------- | ------------- | -------------------------------------------- |
| `user_id`  | string (UUID) | The new user's ID                            |
| `team_id`  | string (UUID) | The team created for this account            |
| `email`    | string        | The normalized email address                 |
| `password` | string        | Auto-generated password. Store this securely |

### Errors

| Status | Detail                                                           | Cause                                 |
| ------ | ---------------------------------------------------------------- | ------------------------------------- |
| `400`  | `A valid email is required`                                      | Email is empty or missing `@`         |
| `409`  | `Failed to create account. The email may already be registered.` | A user with this email already exists |

<Tip>
  **If you receive a 409:** The account already exists. Skip to the **Get API key** step using the same email and the user's existing password. If the password is unknown, ask the user or direct them to reset it at [app.topify.ai](https://app.topify.ai).
</Tip>

***

## Get API key

```
POST /account/api-key
```

Authenticates a team administrator with email and password, then creates and returns a new API key on the **standard** rate limit tier. The key can access every project owned by the selected team; project-scoped keys are not currently issued.

### Request body

| Field      | Type          | Required | Description                                                                            |
| ---------- | ------------- | -------- | -------------------------------------------------------------------------------------- |
| `email`    | string        | Yes      | Account email address                                                                  |
| `password` | string        | Yes      | Account password (from the create endpoint)                                            |
| `team_id`  | string (UUID) | No       | Team to create the key for. Required when the administrator manages more than one team |

```json theme={null}
{
  "email": "user@example.com",
  "password": "<account-password>"
}
```

### Response

```json theme={null}
{
  "code": 200,
  "message": "200 OK",
  "data": {
    "api_key": "tk_live_<your-key>",
    "team_id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
    "key_prefix": "tk_live_214d",
    "rate_limit_tier": "standard"
  }
}
```

### Response fields

| Field             | Type          | Description                                                                                   |
| ----------------- | ------------- | --------------------------------------------------------------------------------------------- |
| `api_key`         | string        | Full API key. Store it only in a server-side secret manager or protected environment variable |
| `team_id`         | string (UUID) | The team this key belongs to                                                                  |
| `key_prefix`      | string        | First 12 characters of the key, for identification                                            |
| `rate_limit_tier` | string        | Rate limit tier assigned to this key (`standard` or `premium`)                                |

### Errors

| Status | Detail                               | Cause                                                               |
| ------ | ------------------------------------ | ------------------------------------------------------------------- |
| `401`  | `Invalid email or password`          | Authentication failed                                               |
| `403`  | `No admin team found for this user`  | Only team administrators can create or retrieve public API keys     |
| `400`  | `team_id is required`                | The administrator manages multiple teams; specify the intended team |
| `500`  | `Authentication service unavailable` | The authentication service is temporarily unavailable               |

***

## Create project

```
POST /account/projects
```

Creates a new brand tracking project and starts the bootstrap pipeline in the background. The endpoint returns immediately with a `202 Accepted` status while the pipeline runs asynchronously.

The bootstrap pipeline generates tracking prompts, fetches initial AI responses from all providers, calculates brand metrics, and detects competitors. When complete, a webhook callback is sent to the URL you provide.

<Note>
  Requires API key authentication via the `X-API-Key` header.
</Note>

### Request body

| Field         | Type   | Required | Description                                      |
| ------------- | ------ | -------- | ------------------------------------------------ |
| `brand_name`  | string | Yes      | The brand name to track                          |
| `brand_url`   | string | Yes      | The brand's website URL                          |
| `webhook_url` | string | Yes      | URL to receive the completion callback           |
| `language`    | string | No       | Language for generated prompts (e.g. `en`, `ja`) |
| `location`    | string | No       | Target market country code (e.g. `US`, `JP`)     |

```json theme={null}
{
  "brand_name": "Acme Corp",
  "brand_url": "https://acme.com",
  "webhook_url": "https://your-service.com/webhooks/topify",
  "language": "en",
  "location": "US"
}
```

### Response

```json theme={null}
{
  "code": 202,
  "message": "202 Accepted",
  "data": {
    "project_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
    "status": "initializing"
  }
}
```

### Response fields

| Field        | Type          | Description                           |
| ------------ | ------------- | ------------------------------------- |
| `project_id` | string (UUID) | The new project's ID                  |
| `status`     | string        | Initial status. Always `initializing` |

### Webhook callback

When the bootstrap pipeline completes (or fails), a POST request is sent to your `webhook_url` with the following payload:

**Success:**

```json theme={null}
{
  "event": "project.bootstrap",
  "project_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "status": "completed",
  "error": null,
  "timestamp": "2026-03-10T03:27:35Z"
}
```

**Failure:**

```json theme={null}
{
  "event": "project.bootstrap",
  "project_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "status": "error",
  "error": "Failed to generate prompts",
  "timestamp": "2026-03-10T03:27:35Z"
}
```

<Warning>
  Treat the bootstrap callback only as a completion signal. Match its `project_id` to the ID returned by your create request, then confirm the current project state through an authenticated API request before performing any follow-up mutation.
</Warning>

### Errors

| Status | Detail                                             | Cause                         |
| ------ | -------------------------------------------------- | ----------------------------- |
| `400`  | `brand_name is required`                           | Missing or empty brand name   |
| `400`  | `brand_url must be a valid URL`                    | URL failed validation         |
| `400`  | `webhook_url must be a valid URL`                  | Webhook URL failed validation |
| `401`  | `Missing X-API-Key header`                         | No API key provided           |
| `403`  | `You've reached the maximum number of projects...` | Plan project limit reached    |

***

## After bootstrap completes

After the webhook reports `"status": "completed"` and you confirm the project through the authenticated API, use these read-only endpoints (documented in [API reference](/api-reference/introduction)) to retrieve results:

| Endpoint                                        | What it returns                                         |
| ----------------------------------------------- | ------------------------------------------------------- |
| `GET /projects`                                 | List of projects with current brand metrics             |
| `GET /projects/{id}/overview`                   | Per-prompt aggregated analysis with competitor mentions |
| `GET /projects/{id}/visibility?duration_days=7` | Daily visibility, sentiment, and position trends        |
| `GET /projects/{id}/prompts`                    | All tracked prompts with per-provider metrics           |
| `GET /projects/{id}/competitors`                | Detected competitor brands and their metrics            |
| `GET /projects/{id}/sources`                    | Domains cited by AI providers, sorted by citation count |

All data endpoints require the `X-API-Key` header and return responses in the format `{"success": true, "data": {...}}`.

<Tip>
  For a quick summary to present to the user, start with `GET /projects` to get the brand's overall visibility score and sentiment, then `GET /projects/{id}/overview` for a prompt-level breakdown.
</Tip>

***

## Full workflow example

The workflow can run end-to-end after the user supplies or explicitly authorizes the account email. Do not generate a temporary identity. The API key created in step 2 is an admin-created, full-team credential.

<Tabs>
  <Tab title="cURL">
    ```bash theme={null}
    # 1. Create an account
    curl -X POST "https://topify-customer-api-production.up.railway.app/api/public/v1/account/create" \
      -H "Content-Type: application/json" \
      -d '{"email": "user@example.com"}'

    # 2. Get an API key (use the password from step 1)
    curl -X POST "https://topify-customer-api-production.up.railway.app/api/public/v1/account/api-key" \
      -H "Content-Type: application/json" \
      -d '{"email": "user@example.com", "password": "Tp..."}'

    # 3. Create a project (use the API key from step 2)
    curl -X POST "https://topify-customer-api-production.up.railway.app/api/public/v1/account/projects" \
      -H "Content-Type: application/json" \
      -H "X-API-Key: tk_live_..." \
      -d '{
        "brand_name": "Acme Corp",
        "brand_url": "https://acme.com",
        "webhook_url": "https://your-service.com/webhooks/topify"
      }'

    # 4. Query project data after webhook confirms completion
    curl "https://topify-customer-api-production.up.railway.app/api/public/v1/projects" \
      -H "X-API-Key: tk_live_..."
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import httpx

    BASE = "https://topify-customer-api-production.up.railway.app/api/public/v1"

    # 1. Create account
    resp = httpx.post(f"{BASE}/account/create", json={
        "email": "user@example.com",
    })
    account = resp.json()["data"]
    password = account["password"]

    # 2. Get API key
    resp = httpx.post(f"{BASE}/account/api-key", json={
        "email": "user@example.com",
        "password": password,
    })
    api_key = resp.json()["data"]["api_key"]

    # 3. Create project
    headers = {"X-API-Key": api_key}
    resp = httpx.post(f"{BASE}/account/projects", json={
        "brand_name": "Acme Corp",
        "brand_url": "https://acme.com",
        "webhook_url": "https://your-service.com/webhooks/topify",
    }, headers=headers)
    project_id = resp.json()["data"]["project_id"]
    print(f"Project {project_id} is bootstrapping...")

    # 4. After webhook confirms completion, query data
    resp = httpx.get(f"{BASE}/projects", headers=headers)
    print(resp.json())
    ```
  </Tab>

  <Tab title="Node.js (server-side)">
    ```javascript theme={null}
    const BASE = "https://topify-customer-api-production.up.railway.app/api/public/v1";

    // 1. Create account
    let resp = await fetch(`${BASE}/account/create`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ email: "user@example.com" }),
    });
    const { data: account } = await resp.json();

    // 2. Get API key
    resp = await fetch(`${BASE}/account/api-key`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        email: "user@example.com",
        password: account.password,
      }),
    });
    const { data: keyData } = await resp.json();

    // 3. Create project
    const headers = {
      "Content-Type": "application/json",
      "X-API-Key": keyData.api_key,
    };
    resp = await fetch(`${BASE}/account/projects`, {
      method: "POST",
      headers,
      body: JSON.stringify({
        brand_name: "Acme Corp",
        brand_url: "https://acme.com",
        webhook_url: "https://your-service.com/webhooks/topify",
      }),
    });
    const { data: project } = await resp.json();
    console.log(`Project ${project.project_id} is bootstrapping...`);

    // 4. After webhook confirms completion, query data
    resp = await fetch(`${BASE}/projects`, { headers });
    console.log(await resp.json());
    ```
  </Tab>
</Tabs>
