# Agents and API keys

Grok Bot, Claude Duet, Dots, Muse, n8n and your own code: one message to paste, or one key for the REST API and MCP.

An assistant that signs in with **Allow** needs none of this page: see [Connect your assistant](/docs/getting-started/quickstart). This page is for an agent or a client that works with a key.

## Agents: one message to paste

Claude Duet, Grok Bot, Dots and Muse get one message to paste, not a form. Pick your agent at [Connect your agent](https://mysdr.app/en/connect) or under **Connect assistant** in the app, copy the message and paste it in a chat with your agent.

The message holds no key. It points to the docs (`https://mysdr.app/llms.txt`) and to your Leads and Companies tables (`https://mysdr.app/api/v1/tables/leads` and `/companies`), and tells the agent to ask you for one. When it asks, click **Create key** and copy the `gtk_` key. Enter it where the agent asks: in its field for secrets if it has one, so the key stays out of the chat history. The agent then makes one free call to check it (`GET https://mysdr.app/api/v1/usage`). That first call connects it, and the app shows it.

The agent works with the [REST API](#the-rest-api): it reads and fills your Leads and Companies tables with the same key, so what it finds and keeps is what you see in mySDR. It can run LinkedIn sequences from your own LinkedIn account with the same key, once you connected LinkedIn yourself under **LinkedIn in the menu**: the agent cannot do that for you. The key needs API access on your plan. mySDR has not verified a Duet-specific dialog (or one for Dots or Muse), and the message needs none: you paste it in a chat.

## The REST API

Every request goes to `https://mysdr.app/api/v1` with your key as a Bearer token. Create the key under **API keys in the menu**; it starts with `gtk_` and is shown once. Examples read it from `MYSDR_API_KEY`.

```bash
curl "https://mysdr.app/api/v1/usage" \
  -H "Authorization: Bearer $MYSDR_API_KEY"
```

What a key can call:

- **Every key:** Search People (also the people at your companies, with `companies`), Enrich People (also a work email from a name), Find Phone, Reverse Email Lookup, Search Companies (by name, by industry, size, place and recent activity, or like a company with `similar_to`), Enrich Companies, the technology data (a tech stack, companies using a technology), the Leads and Companies tables, your process (state, activity, agenda, exclusions), LinkedIn outreach and usage.
- **Signals, for every organization:** signal searches, monitors and webhooks. They spend credits. A balance that is too low answers **402** `insufficient_credits`.

For n8n, Make, Zapier or a cron job, including how to pick up new leads, replies and LinkedIn steps on a schedule: [n8n and automations](/docs/getting-started/n8n).

A missing, invalid or revoked key is **401** on every call, including while an API is closed. A valid key on Signals or the Company API while that API is turned off answers **403** `api_not_enabled`, with what the key can do instead. Base URL, errors and rate limits are in the [API reference](/docs/api-reference).

## The two MCP servers

The same tools over MCP. Both are stateless streamable HTTP and take `POST` only: GET, PUT, PATCH and DELETE return **405** with `Allow: POST`, before the key is checked and while an API is closed. Both require `Authorization: Bearer` on every POST: an access token from signing in with OAuth (paste the URL in your assistant and click **Allow**), or a key. POST without a key is **401**, with `resource_metadata` pointing to the sign-in.

- **Assistant server** — `POST https://mysdr.app/api/mcp`. Allow, or a key that starts with `ejk_`. The JSON response holds the tools of all three blocks: enrichment, the signal tools, the tables, your process and LinkedIn. It allows 60 calls per minute per key, and a body larger than 512 KB is rejected.
- **Signals server** — `POST https://mysdr.app/api/v1/mcp`. The same `gtk_` key as the REST API, for a client that works with a key: companies, signals, people at companies, `enrich` and monitors. Every organization can call it. The response is a server-sent event stream, and its searches use credits. A valid `gtk_` key while Signals is turned off answers **403** `api_not_enabled`. It allows 60 requests per minute per key and 3 concurrent calls that can spend credits. The tools are listed on [MCP](/docs/api-reference/mcp).

An assistant that signs in with Allow needs only the assistant server.

### Keys for MCP

- **Assistant server:** key prefix `ejk_`, from **API keys in the menu, then Other → Create assistant key** in the app. The full key is shown once. An organization can have 10 active MCP keys; connections made with Allow do not count.
- **Signals server:** key prefix `gtk_`, from **API keys in the menu**.

An `ejk_` key cannot call the REST API, and a `gtk_` key cannot call the assistant server. Do not put a key in the query string. Give it only to the client or agent that uses it, never in a shared chat or in code.

### Generic MCP config

Paste this when a client asks for a remote MCP server and a header. Replace the key. A client with neither a header field nor OAuth cannot connect.

Assistant server (all three blocks):

```json
{
  "mcpServers": {
    "mysdr-assistant": {
      "url": "https://mysdr.app/api/mcp",
      "headers": {
        "Authorization": "Bearer ejk_…"
      }
    }
  }
}
```

Signals server (company → signal → lead):

```json
{
  "mcpServers": {
    "mysdr-signals": {
      "url": "https://mysdr.app/api/v1/mcp",
      "headers": {
        "Authorization": "Bearer gtk_…"
      }
    }
  }
}
```

## Grok Bot over MCP

Grok Bot works with the REST API above. To give it the MCP tools as well, open **Settings → Plugins** in the Grok Bot app and add a custom MCP server (the app also accepts "Add this MCP server" in chat, then still needs the header below).

- **Name:** mySDR
- **URL:** `https://mysdr.app/api/mcp` for your workspace and LinkedIn, or `https://mysdr.app/api/v1/mcp` for signal searches
- **HTTP header name:** `Authorization`
- **HTTP header value:** `Bearer ` and the key (`ejk_` on the assistant URL, `gtk_` on the Signals URL)
- Leave the OAuth client id and secret empty

Save, install the server from Plugins, and attach it in a chat with @. A URL with no header is rejected.

## A key in a client that also signs in

Claude Code, Claude, Cursor and Codex take a key when you prefer one. The header is `Authorization: Bearer`.

### Claude Code

```bash
claude mcp add --transport http --scope user mysdr-signals https://mysdr.app/api/v1/mcp \
  --header "Authorization: Bearer $MYSDR_API_KEY"
```

Give each server its own name: `mysdr` for the assistant server and `mysdr-signals` for the Signals server. Claude Code refuses a second server with a name it already has, and a config file holds each name once. A project `.mcp.json` needs `"type": "http"`, the URL, and the same header. An entry with a `url` and no `type` is skipped. With the header set, Claude Code does not start a sign-in.

### Claude Desktop and Claude.ai

In the custom connector, choose **No sign-in** and set request header `authorization` to `Bearer ` plus the key. Request headers are in beta and missing on some organizations; signing in with Allow works everywhere.

### Cursor

`~/.cursor/mcp.json`, then restart Cursor. Do not add an `auth` block: mySDR registers Cursor itself when it signs in.

```json
{
  "mcpServers": {
    "mysdr-signals": {
      "url": "https://mysdr.app/api/v1/mcp",
      "headers": {
        "Authorization": "Bearer ${env:MYSDR_API_KEY}"
      }
    }
  }
}
```

### Codex

In `~/.codex/config.toml`. Codex adds the word Bearer itself.

```toml
[mcp_servers.mysdr-signals]
url = "https://mysdr.app/api/v1/mcp"
bearer_token_env_var = "MYSDR_API_KEY"
```
