---
title: "Headless / API key"
description: "No browser on this machine? CI, SSH, containers, or paste-a-key-only clients — use a key."
url: https://www.unitpost.com/guides/mcp/headless
section: MCP
updated: 2026-08-28
---
# Headless / API key

## Connection settings

- **Server URL**: https://mcp.unitpost.com/mcp
- **Transport**: Streamable HTTP
- **Auth (recommended)**: OAuth 2.1 — add the URL, approve in the browser (scopes match API capabilities; tokens refresh automatically)
- **Auth (legacy)**: Authorization: Bearer pk_live_YOUR_KEY (workspace API key from Settings → API keys; for clients that can only paste a key)
- **Tools**: one per API operation (email_send, email_campaigns_send, …)

## Headless / API key

> No browser on this machine? CI, SSH, containers, or paste-a-key-only clients — use a key.

OAuth needs a browser. Where there isn't one — CI jobs, SSH sessions, containers, Codex CLI, or any client that can only paste a static header — send a workspace API key as the Bearer token instead. Same scopes, same gates, same tools.

### 1. Create an API key

Open https://www.unitpost.com → Settings → API keys → create a scoped key. Copy the full `pk_live_…` value (shown once). In every snippet below, replace `pk_live_YOUR_KEY` with that value.

> Shown once. Keep the `Bearer ` prefix in every snippet. Grant only the scopes the agent needs (e.g. `emails:send` for `email_send`). Prefer a revocable key dedicated to the agent.

### 2. Cursor (static key)

Edit `~/.cursor/mcp.json` (all projects) or `.cursor/mcp.json` (this project only) — or Cursor Settings → MCP → Add new MCP server. Put the `unitpost` entry under top-level `mcpServers`.

```json
{
  "mcpServers": {
    "unitpost": {
      "url": "https://mcp.unitpost.com/mcp",
      "headers": {
        "Authorization": "Bearer pk_live_YOUR_KEY"
      }
    }
  }
}
```

### 3. Claude Code (static header)

Flags stay before the name/URL.

```bash
claude mcp add --transport http --header "Authorization: Bearer pk_live_YOUR_KEY" unitpost https://mcp.unitpost.com/mcp
```

### 4. Claude Desktop (mcp-remote bridge)

For plans without custom connectors. In Claude Desktop: Settings → Desktop app → Developer → Edit Config, then merge the block below — `mcpServers` must be a top-level key (sibling of `preferences`), not nested inside it.

```json
{
  "mcpServers": {
    "unitpost": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://mcp.unitpost.com/mcp",
        "--header",
        "Authorization:${AUTH_HEADER}"
      ],
      "env": {
        "AUTH_HEADER": "Bearer pk_live_YOUR_KEY"
      }
    }
  }
}
```

> Deliberately no space after `Authorization:` in `args` — Claude Desktop mangles spaces there; the value lives in `env` instead. Requires Node.js 18+. If `npx` is not found, set `"command"` to your absolute npx path (e.g. `/opt/homebrew/opt/node@22/bin/npx`). Save, then ⌘Q (Quit) Claude Desktop — closing the window is not enough.

### 5. Codex CLI (TOML)

The CLI reads static config only. Edit `~/.codex/config.toml` (create it if needed).

```toml
[mcp_servers.unitpost]
url = "https://mcp.unitpost.com/mcp"

[mcp_servers.unitpost.http_headers]
Authorization = "Bearer pk_live_YOUR_KEY"
```

### 6. VS Code (runtime-prompted key)

Create or edit `.vscode/mcp.json`. When VS Code prompts later, paste only the `pk_live_…` value — not `Bearer ` (the config already adds it).

```json
{
  "inputs": [
    {
      "id": "unitpost-key",
      "type": "promptString",
      "description": "Unitpost API key (pk_live_…)",
      "password": true
    }
  ],
  "servers": {
    "unitpost": {
      "type": "http",
      "url": "https://mcp.unitpost.com/mcp",
      "headers": {
        "Authorization": "Bearer ${input:unitpost-key}"
      }
    }
  }
}
```

### 7. Gemini CLI (static header)

Edit `~/.gemini/settings.json` (user-level) or `.gemini/settings.json` (project).

```json
{
  "mcpServers": {
    "unitpost": {
      "httpUrl": "https://mcp.unitpost.com/mcp",
      "headers": {
        "Authorization": "Bearer pk_live_YOUR_KEY"
      }
    }
  }
}
```

### 8. Windsurf (static header)

Edit `~/.codeium/windsurf/mcp_config.json`. Windsurf uses `serverUrl` (not `url`) for remote HTTP.

```json
{
  "mcpServers": {
    "unitpost": {
      "serverUrl": "https://mcp.unitpost.com/mcp",
      "headers": {
        "Authorization": "Bearer pk_live_YOUR_KEY"
      }
    }
  }
}
```

### 9. OpenCode (static header)

Set `oauth: false` and pass the Bearer key.

```json
{
  "mcp": {
    "unitpost": {
      "type": "remote",
      "url": "https://mcp.unitpost.com/mcp",
      "oauth": false,
      "headers": {
        "Authorization": "Bearer pk_live_YOUR_KEY"
      }
    }
  }
}
```

### 10. Any other client (generic)

Configure a streamable-HTTP MCP server with the URL below, and send the header on every MCP request.

```json
{
  "url": "https://mcp.unitpost.com/mcp",
  "headers": {
    "Authorization": "Bearer pk_live_YOUR_KEY"
  }
}
```

### 11. Verify with the key

Call a read tool (e.g. list domains). A 401 Invalid API key usually means a missing `Bearer ` prefix or a mistyped key — recreate the key and retry. Then send a test email from a verified domain and confirm it in Unitpost Activity.

> Don't see it? Fully quit and reopen the client — closing the window is often not enough — then check again.

## Related

- [Cursor](https://www.unitpost.com/guides/mcp/cursor): Connect Cursor to Unitpost with OAuth — approve in the browser, or a key block.
- [Claude Code](https://www.unitpost.com/guides/mcp/claude-code): Connect Claude Code to Unitpost with OAuth — add the URL, authenticate from /mcp, approve in the browser.
- [Claude Desktop](https://www.unitpost.com/guides/mcp/claude-desktop): Connect with OAuth via Add custom connector — approve in the browser. API-key bridge as fallback.
