Merciful developer docs

Merciful is an AI chat service you can use on its own or connect to your own projects. Start here to work with files, add chat to a website, or build a custom integration.

For everyday chat, open Merciful on the web.

Choose where to start

Download a client

For deeper development on your machine. Work with project files, run commands, and let the assistant use the results to continue a task. The desktop client offers access to system files and shell tools you enable.

Find your download Read about files and tools

Add a chat widget

For web developers who want chat on an existing site. Drop in a stylesheet and script, and visitors can start a conversation. Customize the title, colors, and first message without building a chat interface or running a backend.

Add the widget Customize its appearance

Build with the API

For advanced integrations in your own apps and services. Create a conversation, send a message, and retrieve the reply over HTTP. Build your own interface, automate a workflow, or connect Merciful to another tool.

Try the API quickstart Set up authentication

How the pieces fit together

The clients and widget use the same conversation API you can call from your own code. The API stores conversation history in rooms and queues each prompt as a job. Retrieve the job's result when the reply is ready.

ComponentWhat it provides
Packaged clientsChat and agent workspaces with local files and tools: virtual files in Chrome and legacy clients, with native file and shell options on desktop
Embeddable widgetA ready-made chat interface for your website, with a configurable first message, appearance and conversation continuation link
REST APIRooms, queued model replies and bounded conversation context for your integration

The API supplies conversation responses; workspace tools execute in the packaged client. For API and widget integrations, include task instructions and reference material in your messages. Room history carries that context forward within the conversation's context window.

Fair use and anonymous limits

Anonymous access needs no registration or API key, including from third-party sites. All access is subject to service policy, request limits and available capacity. Account keys do not override Merciful's safety instructions.

The widget displays a Merciful welcome message and a continuation link. Keep that attribution visible so visitors know who provides the service. Embedding is open to arbitrary websites without manual origin approval.

Direct and embedded anonymous requests share the same IP allowance. An embedding origin also has aggregate limits across visitors, and the service has a shared anonymous budget. Changing room IDs, client IDs, retry flags or model tier does not reset those counters.

Anonymous limitDefault
Generation submissions per IP6 per minute; 30 per 24-hour budget window
Room creation per IP16 per hour
Prompt size16 KiB of UTF-8 text per request
Generation submissions per external origin30 per minute; 300 per 24-hour budget window; 1 MiB of submitted prompt text per window

Operators can adjust these defaults. Existing model/token and concurrent-job limits also apply, including to Economy. Limits measure requests and text bytes, not a guaranteed number of model tokens. A budget window begins when its counter is first used; it is not a promise of a midnight reset.

429 responses include Retry-After and an error code. Wait for that interval instead of retrying in a loop. Visitors can open Merciful and sign in for their account allowance; Pro provides higher account limits. A server integration can use an account API key. Never put an account key in public browser code.

Origin headers help aggregate browser traffic; they are not authentication. Non-browser callers may omit them and still receive anonymous IP/global limits. Malformed or opaque null origins are rejected for anonymous writes; opaque-origin legacy clients can sign in instead. Full Merciful chat pages deny framing; use the script widget for embedding.

Authentication and permissions

  • Anonymous: create a room and retain its returned access token. A client ID identifies a client; do not treat another visitor's identifier as an account credential.
  • Account API key: use X-API-Key or Authorization: Bearer merc_live_... from a server. Account keys grant chat; room ownership/access checks still apply.
  • Account session: use an authenticated session or user JWT to create, list and revoke account API keys.

runner and admin are operator-provisioned permissions. The account-facing key endpoint cannot grant them. A key's request limit can be lowered but cannot exceed the account's server-owned allowance (normally 60/minute, Pro 300/minute). Keys belonging to one account also share its minute allowance.

Requests use Merciful's service instructions. Supply task instructions and reference material as conversation messages. The widget's initial message is ordinary visible conversation text and remains subject to service instructions and limits. Room IDs alone do not authorize private account conversations. Store room tokens securely; distributing one grants access.

Client files and tools

The packaged clients let the assistant work with files, run commands and use the results to continue a task. Chrome and legacy clients use virtual local files. Native desktop file and shell access are separate opt-ins. File previews show escaped source.

Agent workspace rooms stay tied to their workspace; shared rooms are chat conversations. Reading a workspace transcript on another device gives you the conversation history, while file access and tool execution belong to the original workspace.

Storage and privacy

Workspace files are stored on the device, separately from account room history. Signing into another client does not move those files. Reading files into model context sends their content to the selected backend. Provider and service log retention are separate from local file storage.

Client downloads

Visit merciful.app for desktop, Chrome, ChromeOS and legacy Windows clients, with installation instructions for each platform. You can also open Merciful in your browser.

The embeddable widget

The widget creates a room when opened, submits ordinary jobs and polls messages. Merely loading its script does not request a model response. It uses the same visitor limits as direct chat, plus shared embedding-site limits.

Supply site information as reference text in a short visible first message. The widget uses the conversation text you provide; it does not automatically read the embedding page. Merciful's service instructions apply to every message.

Widget quickstart

HTML
<link rel="stylesheet" href="https://merciful.ai/widget/widget.css">
<script src="https://merciful.ai/widget/widget.js"
  data-title="Site chat"
  data-subtitle="Powered by Merciful.ai"
  data-initial-message="Help visitors explore this site. Our opening hours are Monday to Friday, 9–5."
  defer></script>

The optional data-initial-message is sent once when a fresh conversation opens, shown in its history, and charged like any other prompt. Reopening an existing room does not send it again. Opening a fresh conversation programmatically also sends the initial message. Keep it short and use information suitable for visitors to see.

AttributeDefault / meaning
data-api-basehttps://merciful.ai/api
data-app-urlhttps://merciful.ai
data-title, data-subtitleUI labels only
data-initial-messageOptional visible first message; no privileged system-prompt setting
data-positionbottom-right or bottom-left
data-themeauto (dark default) or light

CSS theming

CSS
#merciful-widget-container {
  --merc-primary: #10b981;
  --merc-bg: #0f172a;
  --merc-text: #f8fafc;
}

JavaScript control

JavaScript
MercifulWidget.open();
MercifulWidget.close();
MercifulWidget.toggle();
// A normal visible message; normal rate/token limits apply.
await MercifulWidget.sendMessage("What can you help me with?");
MercifulWidget.openInMerciful();
// Explicitly start a fresh conversation (and seed it again, if configured).
await MercifulWidget.reset();

Continue a widget conversation

The welcome link and “Open in Merciful” open https://merciful.ai/chat/{roomId}?access_token={token}. Visitors can view the history and continue the conversation in the full app. Treat that URL as a sharing credential: anyone with its token can access the room.

REST API quickstart

Create a private room

POST /api/chat-rooms

Accepts a client identifier (client_id or owner_id), optional model/context-window settings and widget source information. New rooms are private. The response contains id and access_token; retain both. Visibility changes use the separate owner-authorized route, not an ignored creation field.

This example creates a room, submits a message and waits for the result. Run it from your server or a normal browser origin. It deliberately supplies no account secret.

JavaScript
const base = 'https://merciful.ai/api';
const clientId = 'client_' + crypto.randomUUID();
async function read(response) {
  const data = await response.json();
  if (!response.ok) {
    const wait = response.headers.get('Retry-After');
    throw new Error(`${response.status}: ${data.error || data}${wait ? `; retry after ${wait}s` : ''}`);
  }
  return data;
}
const room = await read(await fetch(`${base}/chat-rooms`, {
  method: 'POST', headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ client_id: clientId })
}));
const headers = { 'Content-Type': 'application/json',
  'X-Access-Token': room.access_token, 'X-Client-Id': clientId };
const job = await read(await fetch(`${base}/jobs`, {
  method: 'POST', headers,
  body: JSON.stringify({ prompt: 'Hello Merciful!', job_type: 'txt2txt',
    client_id: clientId, chat_room_id: room.id })
}));
const deadline = Date.now() + 300000;
while (true) {
  if (Date.now() > deadline) throw new Error('Timed out; inspect this job before submitting again.');
  const status = await read(await fetch(`${base}/jobs/${job.id}`, { headers }));
  if (status.status === 'completed') {
    console.log(status.result?.text_data);
    break;
  }
  if (['failed', 'abandoned'].includes(status.status)) throw new Error(`Job ${status.status}`);
  await new Promise(resolve => setTimeout(resolve, 2000));
}

Send a message

POST /api/jobs

Supply prompt, job_type: "txt2txt", chat_room_id and the client identifier. Send the room token in X-Access-Token, or use an account key authorized for that room. The response contains the queued job's id.

cURL
curl https://merciful.ai/api/jobs \
  -H 'Content-Type: application/json' \
  -H 'X-Access-Token: <room_token>' \
  -d '{"prompt":"Hello!","job_type":"txt2txt","chat_room_id":"<room_id>","client_id":"<client_id>"}' 

Common statuses: created, claimed, completed, failed, abandoned. A model reply is asynchronous; a successful submission is not a completed response. Free retries are available for a matching request from the last 30 minutes. Send is_retry: true and optionally parent_job_id; the server checks the same account (anonymous IP for guests), conversation and job type. Without a parent ID, the latest matching request is used. Pending/completed requests also qualify for delivery failures. Approved retries use no token allowance, including at zero balance, but normal request/concurrency limits and model permissions remain. An expired or unmatched retry returns 400 free_retry_unavailable rather than silently charging.

Read room messages

GET /api/chat-rooms/:id/messages

Authenticate with the room token or authorized account/key. The response contains room and messages. Messages include role, content, job_id, created_at and status. A pending user message can appear before its assistant reply.

JavaScript
const history = await read(await fetch(
  `${base}/chat-rooms/${room.id}/messages?limit=20`, { headers }
));
console.log(history.messages);

limit counts jobs, which may produce multiple messages. before/after filter job creation timestamps; results are returned chronologically. Deduplicate by job ID and role because after is inclusive. A timestamp alone is not a unique cursor. Account/key-owned room listing is available at GET /api/my-rooms, currently limited to 50 rooms; it is not an exhaustive shared-room manifest.

Account API keys

POST /api/keys

Requires the user's JWT/session, not an existing account API key. The plaintext key is returned once as plain_key. Store it server-side.

cURL
curl https://merciful.ai/api/keys \
  -H 'Authorization: Bearer <user_jwt>' \
  -H 'Content-Type: application/json' \
  -d '{"name":"My server integration","scopes":["chat"]}' 

GET /api/keys lists active keys; DELETE /api/keys/:id revokes an owned key. Both require the user session/JWT. Use a created key with X-API-Key: merc_live_... on room/job operations. Free and Pro remain account tiers with model, token and request allowances; a key does not remove those boundaries.

StatusMeaning / action
400Invalid/unsupported input; correct it before retrying.
401 / 403Missing credentials, missing permission or restricted origin; do not retry blindly.
404Unavailable resource.
413Prompt/body too large; reduce the input.
429Request/token/concurrency allowance reached. Honor Retry-After when present. Admission errors use code rate_limit_exceeded.
503Admission/shared infrastructure unavailable. Retry-After is provided for admission failures; never spin in a tight loop.