Getting started

MCP Server

Qualy runs a remote Model Context Protocol (MCP) server that lets AI agents work with your Qualy data through a curated set of tools. Connect a client such as Claude Desktop, Cursor, or Windsurf — or build your own agent — and it can look up contacts, create payment requests, search your data, and more, all scoped to your tenant.

Endpoint

The server speaks the Streamable HTTP MCP transport at:

https://api.qualyhq.com/v1/mcp

There are two ways to authenticate:

Sign in with Qualy (OAuth) — for clients with a connectors UI (claude.ai, Claude Desktop, Claude Code, and any client that supports MCP authorization). Add the server URL with no credentials; your browser opens a Qualy sign-in (email + password, plus your usual two-factor code), you review what you're connecting, and you're done. No API key to create or store. The connection acts as you — it can see and do exactly what your Qualy user can, nothing more.

API key — for headless agents, scripts, and clients where you configure headers by hand. Pass your Qualy API key as a bearer token:

HeaderValue
AuthorizationBearer <your-api-key>

The tenant is encoded in your login or key, so MCP needs no X-TENANT-ID header. (Authorization: ApiKey <key> is also accepted, and if your client happens to send X-TENANT-ID it's cross-checked against the credential.) Either way, every tool call runs with the permissions of the connected user and is scoped to their tenant.


Connect a client

Claude (sign in — no key)

Add Qualy once as a custom connector on your Claude account, at claude.ai/settings/connectorsAdd custom connector → paste https://api.qualyhq.com/v1/mcp, leaving every other field empty. Your browser opens a Qualy sign-in and consent screen; approve it and the tools are available.

Because the connector lives on your account, it follows you into Claude chat, Claude Desktop, Cowork and Claude Code — you set it up once. On Team and Enterprise plans an Owner can add it for the whole organisation under Admin settings → Connectors; everyone else then only clicks Connect and signs in.

See Connect Qualy to Claude for the step-by-step version to hand a non-technical colleague.

To revoke a connection later, remove it in your client — or sign out of all sessions in the Qualy Dashboard, which disconnects every AI client at once.

Claude Code: connector or claude mcp add?

Claude Code can reach Qualy either way, and they don't reach the same places:

claude mcp add --transport http qualy https://api.qualyhq.com/v1/mcp
Account connectorclaude mcp add
Claude chat, Desktop, Coworkyesno
Claude Codeyesyes
Scopeyour Claude accountlocal (this folder, default), project (committed .mcp.json), or user (all your folders) via -s
Authsign in with Qualysign in, or an API key header

Reach for claude mcp add when you want Qualy pinned to a specific codebase — -s project writes a .mcp.json your teammates get on clone, which is useful for a repo whose whole job is talking to Qualy. For everything else the account connector is the better default: one setup, and it's there in Cowork and on your phone too.

Other clients

Most other MCP clients connect by adding an entry to their MCP configuration. Clients that support MCP authorization will also walk you through the same browser sign-in when you omit the header; otherwise, use an API key.

Clients with native remote MCP support

Cursor, VS Code, Zed and similar clients can point at the remote server directly:

{
  "mcpServers": {
    "qualy": {
      "url": "https://api.qualyhq.com/v1/mcp",
      "headers": {
        "Authorization": "Bearer pk_prod_your-api-key"
      }
    }
  }
}

Windsurf uses a different key

Windsurf (now Devin Desktop) expects serverUrl, not url. Copying the block above verbatim fails silently — the server is accepted but never connects. Use:

{
  "mcpServers": {
    "qualy": {
      "serverUrl": "https://api.qualyhq.com/v1/mcp"
    }
  }
}

Stdio-only clients

Clients that only launch local (stdio) servers can bridge to the remote server with mcp-remote (drop the --header arguments to sign in with OAuth instead):

{
  "mcpServers": {
    "qualy": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://api.qualyhq.com/v1/mcp",
        "--header",
        "Authorization: Bearer pk_prod_your-api-key"
      ]
    }
  }
}

After saving the configuration and restarting your client, the Qualy tools appear in its tool list.

Any MCP-compatible client connects the same way: point it at https://api.qualyhq.com/v1/mcp and either complete the sign-in flow or set Authorization: Bearer <your-api-key>. ChatGPT, Replit, Zed and others each have their own "add MCP server" / connectors screen — follow your client's MCP instructions (for example, Cursor's MCP install links).

For clients and tools that consume the MCP registry format, Qualy publishes a machine-readable entry — name, description, logo, and the remote endpoint — at https://docs.qualyhq.com/server.json.

Keep your key safe

If you configure an API key, your MCP configuration stores it in plain text on your machine. Treat it like any other secret — see API key best practices. Signing in with Qualy avoids this: the client holds short-lived tokens that you can revoke at any time.

How it appears in your client

On connect, the server tells your client who it is — the name Qualy, a website, and the Qualy logo — so clients that support it show proper branding rather than a bare hostname. It also returns a short set of usage instructions that guide the assistant on how to use the tools (find before acting, confirm writes, and so on). You don't configure any of this; it comes from the server. Support varies by client, so exactly how much is shown (logo, description) depends on the app you connect from.


Available tools

All tools are scoped to your tenant. Reads are safe to call freely; writes are rate-limited and de-duplicated.

Contacts

ToolWhat it does
contact_listList contacts (customers / leads / students), newest first; filter by email.
contact_getGet one contact by id or email.
contact_createCreate a contact. Returns the existing contact if the email is already on file.
contact_updateUpdate a contact's name, phone, or email.

Payment intents

ToolWhat it does
payment_intent_listList payment requests; filter by contact or status.
payment_intent_getGet one payment intent, including its shareable payment link.
payment_intent_createCreate a payment request for a contact.
payment_intent_cancelCancel a payment intent.
payment_intent_remindEmail the contact a payment reminder — the same reminder the Dashboard sends. Customer-visible; agents confirm before sending.

Transactions

ToolWhat it does
transaction_listList transactions (charges and refunds); filter by contact, payment intent, or status.
transaction_getGet one transaction by its id.

Orders

ToolWhat it does
order_listList orders; filter by contact.
order_getGet one order by its id.

Partnerships

ToolWhat it does
partnership_listList partnerships (agents / institutions / suppliers).
partnership_getGet one partnership by id, code, or name.
partnership_createCreate a partnership.
ToolWhat it does
searchFuzzy search across contacts or partnerships by name, email, or keyword.

Analytics

Aggregations computed in the database — ask for totals, trends, and forecasts instead of having the agent page through lists. Amounts in analytics results are integer minor units (cents), grouped by currency.

ToolWhat it does
payment_intent_statsPayment analytics in 11 formats: consolidated totals, month-by-month, payer behavior, by-services, calendar-by-day, cancellations, customer payments by month, cash velocity, revenue by service, receivables forecast, and FX corridor margin.
transaction_statsPer-currency transaction counts and totals, broken down by status or by type.
payment_split_statsPartner-commission analytics: owed / paid / outstanding, monthly cohorts, per-partner and per-team breakdowns, reconciliation.

Payouts & payment splits

Payouts are read-only; splits can also be recorded.

ToolWhat it does
payout_listList partner / supplier payouts; filter by status, partnership, method, or currency.
payout_getGet one payout, including its FX details and failure reasons. Bank account numbers are always masked.
payment_split_listList payment splits (partner commissions / revenue shares).
payment_split_getGet one payment split, including amounts, approval, and payout linkage.
payment_split_createRecord a fixed-amount commission on a payment intent. Records what is owed — paying it still happens through payouts.

Customer subscriptions

Your customers' recurring payment schedules — not your own Qualy plan.

ToolWhat it does
subscription_listList customer payment schedules; filter by status, mode, or contact.
subscription_getGet one payment schedule, including cadence, amounts, and charge stats.
subscription_createCreate a recurring payment schedule for a contact — Qualy raises a payment request each cycle.

Refunds & disputes

Disputes are read-only. Refunds can be started — never executed: a created refund is a draft that a human must approve in the Dashboard before any money moves.

ToolWhat it does
refund_intent_listList refund requests and their approval / settlement state.
refund_intent_getGet one refund request with per-item amounts, fees, and taxes.
refund_intent_createStart a refund against a settled charge, as a draft for human approval.
dispute_listList disputes (chargebacks); open: true narrows to those still needing attention.
dispute_getGet one dispute, including its reason, evidence, and due dates.

Your Qualy billing

Your own Qualy subscription and invoices — what you pay Qualy, not your customers' payments. Requires an admin user.

ToolWhat it does
billing_invoice_listList your Qualy subscription invoices.
billing_subscription_listYour Qualy subscription status and plans.
billing_statsAccount balance, currency, and the upcoming invoice preview.

What's not exposed

Nothing on the MCP surface executes money movement: no capturing charges, no approving or executing refunds, no creating or sending payouts. Creates raise requests or drafts — a payment request the payer settles through a secure link, a refund draft a human must approve. Executing operations still go through the API or the Dashboard.


Smart references

Wherever a tool takes a contact or partnership, you can pass a natural identifier instead of a Mongo ObjectId — Qualy resolves it to the right record before the tool runs. An agent that only knows a student's email or a partner's name doesn't need to look up an id first. This is the same Smart references resolution the REST API uses, applied to the MCP tools' reference fields.

FieldAccepts
contacta contact id, or the contact's email (case-insensitive)
partnershipa partnership id, code, exact name, or a unique name prefix

Examples

Get a contact by email instead of an id — contact_get:

{ "contact": "camila@example.com" }

Raise a payment request straight from the payer's email, with no lookup step — payment_intent_create:

{
  "contact": "camila@example.com",
  "currency": "AUD",
  "items": [{ "name": "Tuition — Semester 1", "amount": 12000 }]
}

Fetch a partner by name (or its code) — partnership_get:

{ "partnership": "Sydney English College" }

The same contact resolution applies to the contact filter on payment_intent_list, transaction_list, and order_list.

When a reference is ambiguous

If a name or prefix matches more than one record, the tool returns an AGENT_TOOL_AMBIGUOUS_REFERENCE error listing the candidate records, so the agent can retry with something more specific (an exact name, the partnership code, or an id). If nothing matches, it returns AGENT_TOOL_REFERENCE_NOT_FOUND.

For automated flows, prefer stable identifiers — email, partnership code, or id — over name prefixes, which can drift into ambiguity as new records are added.


When to use MCP vs the API

The MCP server and the REST API run on the same platform, the same data, and the same API-key auth — they're two front doors to the same back office. Pick by how your integration is driven:

Use the MCP server when an AI assistant is your interface. A person or agent asks in natural language and the model decides which tools to call, in what order, with you approving anything that writes. It exposes a curated, tenant-scoped subset of the platform — ideal for the long tail of ad-hoc questions and tasks ("find this student, draft a plan, chase that invoice") without writing or maintaining integration code.

Use the REST API when you're building a programmatic integration. You get the full endpoint surface, deterministic control over every call, webhooks, idempotency keys, and conveniences like Smart references — sending a contact email, partnership code, or service name where an endpoint expects an ObjectId. (MCP tools accept Smart references too, so this isn't a reason to choose one over the other.)

Many teams use both — an assistant for day-to-day operators, the API for their own product surface. They never conflict: every call, from either door, runs through the same tenant scoping, permissions, and platform controls.


Build your own agent

To drive the server from your own agent, connect with any MCP-compatible client library. With the official TypeScript SDK:

import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const transport = new StreamableHTTPClientTransport(
  new URL('https://api.qualyhq.com/v1/mcp'),
  {
    requestInit: {
      headers: {
        Authorization: 'Bearer pk_prod_your-api-key',
      },
    },
  },
);

const client = new Client({ name: 'my-agent', version: '1.0.0' });
await client.connect(transport);

// Discover the tools…
const { tools } = await client.listTools();

// …and call one.
const result = await client.callTool({
  name: 'contact_list',
  arguments: { limit: 5 },
});

console.log(result);

Behavior and limits

  • Tenant scoping — every call runs against your tenant only; the tenant is taken from your credentials.
  • Permissions — each tool requires the matching view/edit permission on the connected user (for example, payout_list requires the splits view permission). A call the user isn't allowed to make returns AGENT_TOOL_FORBIDDEN. OAuth connections act as the signed-in user; API keys carry their own user's permissions.
  • Authoritative totals — every *_list result includes count (the total matching your filter) and hasMore. Agents should read those instead of counting pages; for sums and trends, the *_stats tools aggregate in the database.
  • Rate limits — calls are rate-limited per tool. Reads have a generous budget; analytics and billing are tighter. Exceeding a limit returns an AGENT_TOOL_RATE_LIMITED error with a retry hint.
  • Idempotency — write tools (create / update / cancel) are idempotent: repeating the same call with the same input within 24 hours returns the original result instead of creating a duplicate.
  • Structured errors — tool errors come back with a code and message so your agent can react — for example, retry on a rate limit or re-prompt on invalid input.
  • OAuth session lifetime — sign-in tokens are short-lived and refresh automatically; your client re-prompts to sign in only if the connection is revoked or long unused. Signing out of all Qualy sessions disconnects AI clients too.
Previous
Connect Qualy to Claude