# Getting started with Truto

> Source: https://truto.one/docs/getting-started/

Install an integration, connect a Hubspot account, mint a link token from your backend, open the Truto Link UI from your frontend, and make your first Unified API call.

:::skip-reading{header="true" cli="curl -fsSL https://cli.truto.one/install.sh | bash && truto login --token $TRUTO_API_TOKEN" skill="truto"}
:::

## Setting up Truto with a coding agent

If you're using Cursor, Claude Code, or any other agent that supports the [Agent Skills](https://www.anthropic.com/news/skills) convention, install the Truto skills pack and the Truto CLI once — the agent then knows how to wire Truto into your codebase from a single prompt.

### 1. Install the Truto skills pack

```bash
npx skills add trutohq/truto-skills
```

This installs five skills your agent can call into:

| Skill | What it covers |
|-------|----------------|
| `truto` | Application code that calls the Truto API — link-token routes, unified/proxy/custom calls, webhook handlers |
| `truto-link-sdk` | Embedding the Truto connection UI in a frontend with `@truto/truto-link-sdk` |
| `truto-cli` | Driving the Truto CLI for setup, exploration, and debugging in the terminal |
| `truto-jsonata` | Writing JSONata for unified-API mappings, sync jobs, workflows, RapidForms |
| `truto-api-conventions` | Truto API base URL, auth header, URL patterns, pagination, idempotency |

### 2. Install the Truto CLI

```bash
curl -fsSL https://cli.truto.one/install.sh | bash
truto login --token "$TRUTO_API_TOKEN"
truto whoami -o json
```

The installer drops the `truto` binary in `~/.truto/bin`. `TRUTO_API_TOKEN` is an API token created from the Truto Dashboard — see [Generating an API token](/docs/guides/api-tokens/creating-api-tokens).

`truto whoami -o json` prints your team and environment. If you see your team name, the agent and the CLI are ready.

### 3. Prompts to drop into your agent

Once the skills and CLI are in place, paste any of these into your agent. Each one uses skills installed in step 1 and is enough on its own.

```text
Set up Truto in this codebase. Add a POST /api/truto/link-token route on the
backend that mints a link token for a given tenantId, and a button in the
frontend that opens Truto Link with @truto/truto-link-sdk. Use the truto and
truto-link-sdk skills.
```

```text
Add a webhook handler at POST /webhooks/truto. Switch on event_type. When we
receive integrated_account:active, mark the tenant's connection as ready and
store the integrated_account_id. Handle integrated_account:post_install_error
and integrated_account:validation_error too. Use the truto skill.
```

```text
Add a server-side helper that lists CRM contacts for a given
integrated_account_id by calling GET /unified/crm/contacts on api.truto.one,
then walks pages with next_cursor. Use the truto skill.
```

---

## Asking the docs with AI

The Truto Docs MCP server lets any AI assistant search and read these docs directly. No API key, no install — point your client at the endpoint and ask questions.

```
https://docs-mcp.truto.one/mcp
```

Pick your client below and add the config.

### Claude Desktop

Claude Desktop connects to remote MCP servers through an `npx` bridge. Add this to your `claude_desktop_config.json` (Settings > Developer > Edit Config):

```json
{
  "mcpServers": {
    "truto_docs": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://docs-mcp.truto.one/mcp"]
    }
  }
}
```

Fully quit and reopen Claude Desktop after saving.

### Cursor

Cursor supports remote MCP servers natively. Add this to `.cursor/mcp.json` in your project or `~/.cursor/mcp.json` globally:

```json
{
  "mcpServers": {
    "truto_docs": {
      "url": "https://docs-mcp.truto.one/mcp"
    }
  }
}
```

### Claude Code

```bash
claude mcp add --transport http truto_docs https://docs-mcp.truto.one/mcp
```

Add `--scope user` to make it available across all projects.

### VS Code + Copilot

Add this to `.vscode/mcp.json` in your workspace:

```json
{
  "servers": {
    "truto_docs": {
      "type": "http",
      "url": "https://docs-mcp.truto.one/mcp"
    }
  }
}
```

### Windsurf

Add this to `~/.codeium/windsurf/mcp_config.json`:

```json
{
  "mcpServers": {
    "truto_docs": {
      "serverUrl": "https://docs-mcp.truto.one/mcp"
    }
  }
}
```

---

## Setting up Truto by hand

The rest of this page is the same flow without an agent — install integrations from the Dashboard, generate an API token, mint a link token from your backend, and call the unified API yourself. Use it as a step-by-step walkthrough on its own, or as a reference for what the agent prompts above are doing.

### Install your first integration

Integrations are what enable you to connect your customer's apps to Truto.

Your Truto account by default doesn't come with any Integrations installed and any integration you need to use needs to be installed manually before connecting an account for it.

You can head on over to [Available integrations list](https://app.truto.one/integrations/available) to see the integrations available for installation.

In this guide, we'll install the Hubspot integration. You can install an integration by clicking the Install button and selecting the environments you want to install the integration in.

![Integration list](https://docs-assets.truto.one/hubspot-integration-install.png)

![Integration install modal](https://docs-assets.truto.one/hubspot-integration-install-modal.png)

:::callout{type="info"}
Environments are logical grouping of resources in your Truto account. They help you try out things in isolation without affecting your production data.

:::

### Install the Unified API for CRMs

After installing Hubspot, we will install the Unified API for CRMs which will enable us to query the Hubspot API and get responses in a unified format. This Unified API can also be used with other CRMs.

Head on over to [Available Unified API list](https://app.truto.one/unified-apis/available) and install the CRM Unified API. Installing Unified APIs is similar to installing Integrations.

![Unified API list](https://docs-assets.truto.one/crm-model-install.png)

![Unified API install modal](https://docs-assets.truto.one/crm-model-install-modal.png)

### Generate an API token

You can follow our guide to generate an API token [here](/docs/guides/api-tokens/creating-api-tokens).

### Connect your first Hubspot account

#### Via the dashboard

Go to [Integrated accounts tab](https://app.truto.one/integrated-accounts) and click the `Connect Account` button on the top right corner of the page.

Enter a value in the `Tenant ID` field and click `Get connection link`.

Head on over to the link shown and select Hubspot. After you go through the OAuth flow, you can come back to the `Integrated accounts` tab and close the connection modal. An Integrated account would have been created.

#### From your own app

Connecting an account from your own app is two steps:

1. **Backend**: mint a link token by `POST`ing to `https://api.truto.one/link-token` with your API token.
2. **Frontend**: open the Truto Link UI with `@truto/truto-link-sdk` and that link token.

**Step 1. Mint a link token from your backend**

`TRUTO_API_TOKEN` is a server-side secret. Read it from an env var; never ship it to the browser. The same route handles new connections (`tenant_id`) and reconnections (`integrated_account_id`) — wiring both from the start prevents users from creating duplicate accounts when an existing connection fails.

:::code-tabs

```bash label="cURL"
curl --location 'https://api.truto.one/link-token' \
  --header 'Content-Type: application/json' \
  --header "Authorization: Bearer $TRUTO_API_TOKEN" \
  --data '{
    "tenant_id": "acme-1"
  }'
```

```typescript label="TypeScript (Express)"
import express from 'express';

const app = express();
app.use(express.json());

app.post('/api/truto/link-token', async (req, res) => {
  const { tenantId, integratedAccountId } = req.body;

  const body = integratedAccountId
    ? { integrated_account_id: integratedAccountId, persist_previous_context: true }
    : { tenant_id: tenantId };

  const response = await fetch('https://api.truto.one/link-token', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.TRUTO_API_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });

  const { link_token } = await response.json();
  res.json({ linkToken: link_token });
});
```

```typescript label="TypeScript (Next.js)"
// app/api/truto/link-token/route.ts
export async function POST(req: Request) {
  const { tenantId, integratedAccountId } = await req.json();

  const body = integratedAccountId
    ? { integrated_account_id: integratedAccountId, persist_previous_context: true }
    : { tenant_id: tenantId };

  const response = await fetch('https://api.truto.one/link-token', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.TRUTO_API_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });

  const { link_token } = await response.json();
  return Response.json({ linkToken: link_token });
}
```

```typescript label="TypeScript (Hono)"
import { Hono } from 'hono';

type Env = { TRUTO_API_TOKEN: string };
const app = new Hono<{ Bindings: Env }>();

app.post('/api/truto/link-token', async (c) => {
  const { tenantId, integratedAccountId } = await c.req.json();

  const body = integratedAccountId
    ? { integrated_account_id: integratedAccountId, persist_previous_context: true }
    : { tenant_id: tenantId };

  const response = await fetch('https://api.truto.one/link-token', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${c.env.TRUTO_API_TOKEN}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(body),
  });

  const { link_token } = await response.json();
  return c.json({ linkToken: link_token });
});

export default app;
```

```typescript label="TypeScript SDK"
// npm install @truto/truto-ts-sdk
import TrutoApi from '@truto/truto-ts-sdk';

const truto = new TrutoApi({
  token: process.env.TRUTO_API_TOKEN!,
});

// New connection
const { link_token } = await truto.linkToken.createForNewIntegration({
  tenant_id: 'acme-1',
});

// Reconnect an existing account
const reconnect = await truto.linkToken.createForExistingIntegratedAccount({
  integrated_account_id: 'existing-account-id',
});
```

```python label="Python (Flask)"
import os
import requests
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.post("/api/truto/link-token")
def link_token():
    payload = request.get_json() or {}
    tenant_id = payload.get("tenantId")
    integrated_account_id = payload.get("integratedAccountId")

    body = (
        {"integrated_account_id": integrated_account_id, "persist_previous_context": True}
        if integrated_account_id
        else {"tenant_id": tenant_id}
    )

    response = requests.post(
        "https://api.truto.one/link-token",
        headers={
            "Authorization": f"Bearer {os.environ['TRUTO_API_TOKEN']}",
            "Content-Type": "application/json",
        },
        json=body,
        timeout=10,
    )
    response.raise_for_status()
    return jsonify(linkToken=response.json()["link_token"])
```

```python label="Python SDK"
# pip install truto-python-sdk
import asyncio, os
from truto_python_sdk import TrutoApi

async def main():
    truto = TrutoApi(token=os.environ["TRUTO_API_TOKEN"])

    # New connection
    new_token = await truto.link_tokens.create({"tenant_id": "acme-1"})

    # Reconnect an existing account
    reconnect_token = await truto.link_tokens.create({
        "integrated_account_id": "existing-account-id",
        "persist_previous_context": True,
    })

    print(new_token, reconnect_token)

asyncio.run(main())
```

:::

Response:

```json
{
  "link_token": "e73c7b9b-2e4d-47d1-b5d2-83441fd24e4c"
}
```

:::callout{type="warning"}
`TRUTO_API_TOKEN` must stay on the server. Never expose it to the browser, and never hardcode it in client bundles. Link tokens are short-lived, single-use credentials safe for the frontend; the API token isn't.
:::

**Step 2. Open Truto Link from your frontend**

Install the SDK:

```bash
npm install @truto/truto-link-sdk
```

Call your backend route to get a link token, then hand it to `authenticate()`. The same call works for new connections and reconnections — the difference is in the body your backend posts.

```typescript
import authenticate from '@truto/truto-link-sdk';

async function getLinkToken(body: Record<string, string>) {
  const res = await fetch('/api/truto/link-token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(body),
  });
  const { linkToken } = await res.json();
  return linkToken;
}

async function openTrutoLink(linkToken: string) {
  try {
    const result = await authenticate(linkToken);
    console.log('Connected:', result);
    // { result: 'success', integration: 'hubspot', integrated_account_id: '...' }
    return result;
  } catch (err) {
    if (err === 'closed') {
      console.log('User closed the connection dialog');
    } else {
      console.error('Connection failed:', err);
    }
    throw err;
  }
}

// New connection
const linkToken = await getLinkToken({ tenantId: 'acme-1' });
await openTrutoLink(linkToken);

// Reconnect an existing account
const reconnectToken = await getLinkToken({ integratedAccountId: 'existing-account-id' });
await openTrutoLink(reconnectToken);
```

If you'd rather not embed the SDK, you can hand the link token to Truto's hosted connection page directly:

```http
https://app.truto.one/connect-account?link_token=<link-token-from-previous-step>
```

See [Embedding the Truto Link SDK](/docs/guides/link-sdk/how-to-embed) for popup, iframe, and same-window options, and the full request schema for [`POST /link-token`](/docs/api-reference/admin/link-token/create).

### Make your first API request

You can checkout the API methods available for the newly connected Hubspot Integrated account by going to its detail page and switching to the `Unified API` tab. You can also check out the API reference page for the [CRM Unified API](/docs/api-reference/unified-crm-api/accounts) in the docs.

Use the same API token in the Bearer authorization and make a request to one of the URLs.

## API Reference

To know more about how our APIs work, you can checkout our [API Reference](/docs/api-reference/overview/introduction).
