Developers
Havadis MCP server, REST API and Node SDK
One API key, two doors: MCP-capable AI clients, and @havadis/sdk for your own backend.
- Endpoint
https://api.gethavadis.co/api/mcp- Protocol
mcp/streamable-http
Live information temporarily unavailable
Quickstart
- 1
Sign in or sign up
Havadis uses Google or email login. The next step needs a logged-in dashboard session.
- 2
Mint an API key
From Dashboard → Settings → Developer, create a key. The plaintext is shown only once — copy it now.
- 3
Configure your client
Pick your MCP client below and paste the snippet into the right config file. Set the key as a Bearer token.
- 4
Verify the connection
Before invoking any tool, run a `tools/list` ping to make sure the server is reachable with your key.
- 5
Call your first tool
Once `tools/list` works, invoke a tool with `tools/call`. See the Tool reference for input schemas.
Connect your client
Copy-paste configurations for common MCP clients. Each tab shows the snippet, the exact file path, and a restart hint.
{
"mcpServers": {
"havadis": {
"type": "http",
"url": "https://api.gethavadis.co/api/mcp",
"headers": {
"Authorization": "Bearer havadis_<your-key-here>"
}
}
}
}Restart: Quit and reopen Claude Desktop after saving the config.
Verify the connection
Run a `tools/list` request before invoking any tool — it confirms the endpoint is reachable and your Bearer token is valid.
curl -X POST 'https://api.gethavadis.co/api/mcp' \
-H 'Authorization: Bearer havadis_<your-key-here>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'{
"jsonrpc": "2.0",
"id": 1,
"result": {
"tools": [
{
"name": "create_brand",
"description": "...",
"inputSchema": { "type": "object", "properties": { ... } }
}
]
}
}If the response isn't 200 OK
| Response | Cause | Fix |
|---|---|---|
| 401 Unauthorized | The token is missing, malformed or revoked. | Create a new key in the dashboard. |
| 404 / connection refused | The endpoint URL is wrong. | It must end with `/mcp` — no trailing slash. |
| 429 Too Many Requests | You hit the request limit. | Wait `retryAfterMs` and retry (see Request limits). |
Tool reference
All tools are rendered from the live manifest — a newly added tool appears here within 5 minutes.
No tools
Rate limits
Applied per workspace (1 active key = 1 workspace). Scales with plan; Pro and Pro+ allow higher request volume.
REST API & @havadis/sdk
Production from code: feed your blog with Havadis content, queue jobs from cron, pull content in a static-site build.
npm install @havadis/sdkimport { Havadis } from '@havadis/sdk';
const havadis = new Havadis({ apiKey: process.env.HAVADIS_API_KEY! });
const [brand] = await havadis.brands.list();
const job = await havadis.brand(brand.id).jobs.createAndWait({
contentTypes: ['blog'],
brief: '...',
});- Credit-spending calls require an Idempotency-Key; the SDK generates one per call, and retries replay the same result — no double charges.
- Errors use a closed envelope with frozen codes; match the code, not the text. 429 responses tell you how long to wait via retry_after_seconds.
- The key stays server-side: the package's browser condition and a runtime guard deliberately break any build that would ship it to visitors.
| Method | Path | Summary |
|---|---|---|
| GET | /api/v1/me | Identity, effective scopes and limits of the calling key |
| GET | /api/v1/credits | Credit pool snapshot of the workspace owner's account |
| GET | /api/v1/brands | Brands owned by the calling tenant |
| GET | /api/v1/brands/{brandId} | One owned brand |
| GET | /api/v1/brands/{brandId}/contents | Keyset-paged contents, drafts included |
| GET | /api/v1/brands/{brandId}/contents/{idOrSlug} | Full content (body + SEO/AEO/GEO), ETag/If-None-Match aware |
| POST | /api/v1/brands/{brandId}/contents/{contentId}/publish | Publish to the customer's own site (custom_website) |
| POST | /api/v1/brands/{brandId}/contents/{contentId}/unpublish | Take content off the custom_website channel |
| POST | /api/v1/brands/{brandId}/contents/{contentId}/site-publication | Report where your site published a post Havadis sent it, or that it holds it |
| POST | /api/v1/brands/{brandId}/jobs | Queue a content generation job (async — poll jobs.get) |
| GET | /api/v1/brands/{brandId}/jobs | Keyset-paged jobs |
| POST | /api/v1/brands/{brandId}/jobs/cost-preview | Free per-model cost preview for a job shape |
| GET | /api/v1/brands/{brandId}/jobs/{jobId} | One job with per-platform state |
| POST | /api/v1/brands/{brandId}/topics/suggest | Queue topic discovery (async — results land in topics.list) |
| GET | /api/v1/brands/{brandId}/topics/runs/{runId} | One discovery run: its status, the error code if it failed, and its suggestions |
| GET | /api/v1/brands/{brandId}/topics | Topic suggestions for the brand |
Connect your website
Show Havadis posts on your own site. Havadis knows where each post lives and measures its performance from the day it goes live.
- 1
Connect the site
In Dashboard → Settings → Connected Accounts → Custom Website, enter your site's name and domain. Leave the webhook address empty.
- 2
Read the posts
In the browser, use the keyless public feed; it serves published posts only. On your server, @havadis/sdk also reads drafts.
- 3
Serve each post under its slug
Show posts under one path plus the feed's slug, for example /blog/{slug}. That is how Havadis knows each post's address. A site that picks its own paths reports them through the site-publication endpoint.
- 4
Add a webhook if you need one
If pages must refresh the moment a post is published, serve POST /api/webhooks/havadis on your domain; Havadis sends there by default. Events are signed with Standard Webhooks; the signing secret is shown once when you connect the site, and rotating it on the connection issues a new one. A site that reads the feed on every request needs no webhook.
| Method | Path | Summary |
|---|---|---|
| GET | /api/v1/public/brands/{brandId}/contents | Published posts, no key (an array; ?format=paged for an envelope) |
| GET | /api/v1/public/brands/{brandId}/contents/{slugOrId} | One post, by slug or id |
| GET | /api/v1/public/brands/{brandId}/contents/{slugOrId}/cover | Cover image; embed it directly as an image on your site |