---
name: askbase
description: Work with an AskBase help centre (knowledge base, AI answer widget and shared inbox) through its MCP server or REST API. Use when asked to keep help articles current after shipping a feature or merging a PR, document a feature, write or update help centre articles, fill content gaps (questions customers asked that the help centre couldn't answer), check what the docs say about something, or answer support inbox questions from the knowledge base. Help centre changes are proposed for a person to review, never published directly; inbox replies are drafted for approval unless the key is allowed to send.
---

# AskBase

AskBase is a help centre with an AI assistant that answers customers from its articles, and a shared inbox. You can read everything in it and **propose** changes. Proposals wait in **Suggested changes** for a person on the team to approve, edit or reject. Treat that review step as the point: you draft, a person publishes.

## Connect

Use whichever of these is available. Check in this order.

### 1. MCP (preferred)

If you have tools named `list_help_centres`, `search_articles`, `propose_article` and so on, the AskBase MCP server is connected. It signs in as the person who connected it, with their role. It has no tool that publishes, approves or deletes.

To connect it, the server is `https://askbase.com/mcp` (streamable HTTP, OAuth sign-in). In Claude Code:

```bash
claude mcp add --transport http askbase https://askbase.com/mcp
```

Then run `/mcp` and sign in.

### 2. REST API

Use the REST API when MCP isn't connected, and for the inbox, which only the REST API covers. It needs a key from the AskBase dashboard (**Setup → API keys**, admins only). Expect it in `ASKBASE_API_KEY`. The base URL is `ASKBASE_URL` if that's set, otherwise `https://askbase.com`. Never print the key or write it to a file.

Every operation is a POST of its input as JSON:

```bash
curl -sS -X POST "${ASKBASE_URL:-https://askbase.com}/api/v1/ops/search" \
  -H "Authorization: Bearer $ASKBASE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"kbId": "…", "query": "export to CSV"}'
```

A success is `{"result": …}`. A failure is `{"error": "…", "code": "…"}` with a matching HTTP status. Read the message: it says what to do.

- `GET /api/v1/me` shows the key's workspace and access. Call it first to check the key works.
- `GET /api/v1/ops` lists every operation with its input JSON Schema, and `available` says whether this key can run it. When unsure of an operation's input, look it up there rather than guessing.

A key's access is one of these:

| Access | Can do |
|---|---|
| `read` | Read everything, and ask the assistant (`answer`) |
| `propose` | Also propose changes (`suggestions.propose`), ask AskBase to draft (`suggestions.request`), write inbox replies as drafts for approval, and add internal notes |
| `full` | Also publish, approve and edit directly. Don't, unless the person you're working for explicitly asks. Sends inbox replies only if the key is set to send them |

A key has a name, such as "Support agent", and everything it writes appears in AskBase under that name. `GET /api/v1/me` also says whether its replies are sent or drafted (`"replies": "send"` or `"draft"`).

A `403` with code `forbidden` means the key's access doesn't allow it. Say so, and say which access would. Don't look for a way round it.

### Equivalents

| Task | MCP tool | REST operation |
|---|---|---|
| Find the help centre id | `list_help_centres` | `kb.list` |
| Search published articles | `search_articles` | `search` |
| List all articles | `list_articles` | `article.list` |
| Read one article | `read_article` | `article.get` |
| Product screens and screenshots | `list_product_screens` | `product.screens` |
| Unanswered customer questions | `list_content_gaps` | `suggestions.gaps` |
| Dismiss a content gap | `dismiss_content_gap` | `suggestions.dismissGap` |
| Changes waiting for review | `list_suggestions` | `suggestions.list` |
| Read one suggested change | `read_suggestion` | `suggestions.get` |
| Propose a new article | `propose_article` | `suggestions.propose` without `articleId` |
| Propose an update | `propose_article_update` | `suggestions.propose` with `articleId` |
| Have AskBase draft from a request | `request_articles` | `suggestions.request` |
| Check those requests | `check_requests` | `suggestions.requests` |
| Upload an image to embed | `upload_image` | none |
| Ask the assistant a question | none | `answer` |
| Inbox: list, read, reply (sent or drafted), add a note | none | `inbox.list`, `conversation.get`, `conversation.reply`, `conversation.note` |

MCP tools take `help_centre_id`, and REST operations take `kbId`. They're the same id. Field names differ too: MCP's `body_markdown` is REST's `bodyMd`, and `article_id` is `articleId`.

## Writing for AskBase

These apply to every workflow below.

- **Read before you write.** Search first, and read the whole article before proposing an update. An update replaces the entire article, so send the complete new text, not a diff or only the changed section.
- **Check the queue.** List pending suggestions before proposing, so you don't propose the same change twice.
- **Write for customers.** Use plain language and the product's own names for screens and buttons (from the product screens list when there is one). Use Markdown with no title heading; the title is a separate field. Write the title the way a customer would ask or search.
- **Don't invent.** Put anything you couldn't confirm from the code, changelog, existing articles or product screens on its own line as `> **To check:** …`. The reviewer gets each one as a checklist item. A short article with two checks is better than a confident wrong one.
- **Give the reviewer a reason.** One sentence on what changed and why, and `sources` (the PR, changelog entry or doc you relied on, with URLs).
- **Link and illustrate.** Link related articles by their URLs. Embed screenshots from the product screens list with `![What it shows](url)`.
- **Never publish.** Don't call `suggestions.approve`, `article.write` with `status: "published"`, `article.delete` or other publishing operations unless the person explicitly asks for that specific change to go live.

## Workflow: update the help centre after shipping a feature

Use this when given a PR, a diff, release notes or a changelog entry.

1. **Understand the change from the customer's side.** Read the PR description, changelog and the user-facing parts of the diff: UI text, settings, limits, defaults, error messages, plan gates. List what a customer would notice: new things they can do, things that moved or were renamed, and things that changed behaviour.
2. **Find the affected articles.** Get the help centre id (`list_help_centres` / `kb.list`). Search for each noticeable change using the feature name, old and new UI labels, and the setting names. Read each hit in full. An article is affected if anything it says is now wrong, incomplete or missing the new option.
3. **Check the queue.** List pending suggestions. Skip or build on anything already proposed.
4. **Draft.** Propose an update for each affected article, as its whole text with only the needed changes. Propose a new article only when the feature needs its own how-to that doesn't fit an existing one. Use product screens for accurate labels and screenshots. If the help centre has no screens for a new UI, say what screenshot would help in a `> **To check:**` line.
5. **Report back** with a short list: each article updated or proposed, one line on what changed, and the review link the proposal returned (MCP gives a `review` URL; over REST, suggestions are reviewed in the dashboard under Suggested changes). Also list anything you deliberately didn't change and why.

If there are more than about six changes, or the person would rather AskBase drafted them, use `request_articles` / `suggestions.request` with a one-paragraph description of the change. It drafts up to six articles in the background. Poll `check_requests` / `suggestions.requests` until it's done.

## Workflow: fill content gaps

1. List content gaps (`list_content_gaps` / `suggestions.gaps`). Each gap is a group of questions customers asked that the help centre couldn't answer, with how often and what was missing. Most asked comes first.
2. For each of the top gaps, search to make sure it really isn't covered. It may be covered under different words, in which case propose an update that adds the customer's wording.
3. Work out the answer from sources you can check: the codebase, product screens, existing articles and the company website. If you can't establish the answer, don't guess. Draft what you can, with `> **To check:**` lines, or leave the gap and say why.
4. Propose the article or update. Use the customers' own phrasing in the title.
5. Dismiss gaps that aren't worth an article (spam, one-offs, questions about another company) with `dismiss_content_gap` / `suggestions.dismissGap`, using the gap's question and count exactly as listed. Only do this when you're confident, and say which ones you dismissed.

## Workflow: answer inbox questions from the knowledge base

This needs the REST API with `propose` or `full` access. MCP has no inbox tools.

First check `GET /api/v1/me`. A key's replies work in one of two modes:

- **Draft mode** (`"replies": "draft"`, the default, and always the case for `propose` keys). `conversation.reply` saves your reply as a draft in the conversation. It does not email the customer. The result is `{"drafted": true, "sent": false}`. A teammate sees it marked as from the key, waiting for approval, and either uses it (editing and sending it as their own reply) or discards it. Writing to the same conversation again replaces your earlier draft.
- **Send mode** (`"replies": "send"`, `full` keys only). `conversation.reply` emails the customer. The result has `"drafted": false`, a `messageId` and a `deliveryStatus`. The customer sees the team's usual sender and signature. Pass `holdSeconds` (up to 30) to keep a moment to take it back with `conversation.cancelReply`.

Steps:

1. List open conversations: `inbox.list` with `{"filter": "unassigned"}` or `{"filter": "open"}`, optionally with `kbId` or `search`.
2. Read a conversation with `conversation.get` (`{"conversationId": "…"}`).
3. Find the answer in the help centre with `search` and `article.get`, or ask the assistant with `answer` (`{"kbId": "…", "question": "…"}`). `answer` refuses rather than guessing when the articles don't cover something, and a refusal is a useful result. `conversation.draftReply` (`{"conversationId": "…"}`) has AskBase write a reply from the articles, with anything they don't cover marked `[Needs your input: …]`; use its text as a starting point.
4. Write the reply in Markdown and call `conversation.reply` with `conversationId` and `text`. Answer only from the help centre. If something isn't covered, don't guess: say in an internal note (`conversation.note`) what's missing, and leave that part for a person.
5. Read the result. If `drafted` is true, tell the person you're working for that the reply is waiting for approval. Never present a drafted reply as sent.
6. Even in send mode, draft rather than send when you're unsure, the customer is upset, or money, refunds, legal or account security are involved: put the reply in a `conversation.note` for a teammate instead.
7. When the answer isn't in the help centre, that's a content gap. Offer to propose an article for it (see the workflow above).

## Errors

| Status | `code` | Meaning |
|---|---|---|
| 400 | `invalid_input`, `invalid_json` | The input doesn't match the schema. The message names the field. Check `GET /api/v1/ops` |
| 401 | `unauthenticated` | The key is missing, wrong or revoked |
| 401 | `key_expired` | The key's expiry date has passed. An admin needs to make a new one |
| 402 | `limit_reached` | The plan's limit is reached (AI drafts, for example). Tell the person; don't retry |
| 403 | `forbidden` | The key's access doesn't allow it, or it only drafts replies and can't resend one |
| 404 | `not_found` | No such thing in this workspace. Ids from other workspaces look like this too |
| 409 | `conflict` | Someone changed it meanwhile. Read it again and redo the change |
| 429 | `rate_limited` | Too many requests. Wait for `Retry-After` seconds |
| 502 | `provider_error` | An AI provider failed. Retry once, later |
