Automation & API Available
MCP (Model Context Protocol)
Point an AI client at your own instance and ask in plain language. Nothing gets exported and no second data path appears: the MCP server talks in-process to the same API the web app uses. A client that speaks OAuth does not even need a key, just the address.
What you need
- Your own tenant hostname over HTTPS: the verified custom domain, or <slug>.<base domain>
- A licence covering the mcp sku; without it the whole /mcp mount answers 402
- SCHAKL_MCP_ENABLED must not be false (it is on by default)
- For a key: the apikeys.personal.manage permission, or apikeys.service_account.manage for a service account. For OAuth: nothing extra, just an ordinary login
- For an OAuth client with a fixed client id and secret: the settings.oauth.manage permission to create it
- A client that speaks Streamable HTTP, for example Claude, Claude Code, Claude Desktop or ChatGPT
How to connect it
- 1 Check your licence under Instellingen → Licentie (Settings → Licence). MCP has its own sku (
mcp); if it is not covered,/mcpanswers 402 and there is nothing to connect. - 2 Claude or ChatGPT as a connector: add
https://<your-host>/mcpas a custom connector (ChatGPT:/mcp/compact). The client registers itself, you land on your own workspace's sign-in screen and then on Toegang toestaan (Allow access), where you choose between Alles wat ik zelf mag (everything I may do), Alleen lezen (read only) or Zelf kiezen (choose myself). No secret is copied anywhere. - 3 A client with a key: open Instellingen → API en MCP, choose AI-assistent (MCP) under Wat koppel je? (what are you connecting), give the key a Naam, pick under Wat mag die doen? (what may it do; Alleen lezen by default) and the section under Welke tools. Leave Verloopt op empty for a key that never expires and press Sleutel aanmaken.
- 4 The screen prints the connection line for your client with the new key already in it, for Claude Code for example:
claude mcp add --transport http schakl https://<your-host>/mcp --header "Authorization: Bearer schakl_…". The key is shown once. - 5 A key that needs nobody's account: Instellingen → Service-accounts → Service-account toevoegen, then Nieuwe sleutel with its own scopes.
- 6 A tool that asks for a fixed client id and secret (an enterprise connector config, an automation tool): an admin creates one under OAuth-clients on the same screen, with one redirect URI per line. The secret is shown once.
- 7 Always use your own tenant hostname; a key presented on another organisation's hostname is simply not found.
- 8 Done with a connection: Ontkoppelen (disconnect) under Gekoppelde clients ends every session of that client, Intrekken (revoke) beside a key cuts access immediately.
The same gate as the web app
There is no second data path. A tool call goes in-process through the same handling as an HTTP request: the hostname resolves the organisation, row-level security is bound, and the key's permissions are resolved. A call that is not allowed surfaces the API's own 401 or 403 envelope as a tool error. The incoming key is never forwarded to any external service; the proxy only ever calls the API in-process.
OAuth mints a key, not a second kind of access
Clients that want a connector rather than a pasted secret, Claude and ChatGPT first among them, get a full OAuth 2.1 flow: self-registration, authorization code with PKCE, refresh, revocation and discovery. What that flow hands back is an ordinary API key belonging to the person who consented, so every rule that governs a personal key governs this too. And the authorization server signs nobody in: consent runs on the session the web app already holds, with your password and two-factor verification or with your organisation's SSO. An anonymous call to /mcp gets a 401 with the invitation to do OAuth, so a client discovers the server by being refused and a stranger cannot see which modules you run.
Read-first is a choice at mint time
The tool surface includes writes, because the whole API is in it. What a client may actually do lives in its key's permissions, and the screen starts on read only. A personal key and an OAuth connection are additionally capped by their owner's live permissions on every request, so demoting someone narrows their connections with them. Choose everything I may do or read only at consent and that is stored as a rule rather than as today's list: a module that arrives later is included by itself, without a chat client having to ask for consent again, and never wider than the person. Choose myself stays a fixed list, and the screen says so. A service-account key carries exactly the scopes you granted it.
- Scopes come from the same permission catalogue as roles, including the own/any variant such as time.entry.read:own
- The API's deny-by-default rule answers every call, an AI's included
- Only enabled modules contribute routes, so the tool surface tracks what you actually run
An address per job
The full surface is some 620 tools. A coding assistant reads that list once; a chat client puts every tool in the model's context on every turn, and ChatGPT allows 5,000 tokens for all tools together. So the same server also answers on narrower addresses. A module section is derived from that module's routes, so whatever arrives tomorrow is in it tomorrow; a bundle names modules and never individual tools; and the compact fourteen is the only hand-picked list, because an outside ceiling forces it. A narrower list is not a smaller permission: what a key may do is decided when it is minted.
- /mcp/compact: fourteen read-only tools, the right choice for ChatGPT
- /mcp/agent: clients, contacts, projects, tasks, hours and contact moments
- /mcp/infra: domains, websites, hosting, Cloudflare, OXXA, uptime and WordPress
- /mcp/finance: invoices, subscriptions and payments
- /mcp/growth: Google Ads, Analytics, Search Console, Tag Manager, marketing and reporting
- /mcp/<module>: one module, for example /mcp/google-analytics, /mcp/google-search-console or /mcp/wordpress
- An address that points at nothing is refused with the list of what does exist
Transport and pace
Streamable HTTP, stateless, with JSON responses: every JSON-RPC POST stands alone, so it works behind any load balancer and from plain curl. Each key is limited to 600 requests per minute, generous for automation and a firm ceiling under a leaked key. The Laatst gebruikt (Last used) column deliberately lags: it is updated out of band, so a tool call never waits on it.
Permissions
Grant these to the role that manages this integration (Settings → Roles). Admin-only by default, and never to the client role.
-
apikeys.personal.manageManage your own API keys: mint and revoke them yourself. Granted to admin and member by default, because such a key can never exceed its owner. -
apikeys.service_account.manageManage service accounts: shared principals that belong to no one person. Admin only by default. -
settings.oauth.manageCreate OAuth clients with a fixed client id and secret, issue a new secret and revoke a client along with every key it ever issued. Admin only by default, because a standing credential onto MCP is the organisation's decision.
What it deliberately does not do
- Not everything is a tool: /api/v1/auth, /api/v1/setup, /api/v1/instance, /api/v1/users and the import upload endpoints (/inspect and /import) are deliberately excluded. The column list and the export side of import and export stay reachable.
- A route that takes a file as multipart is not a tool; where it matters there is a JSON twin, such as uploading an attachment as base64.
- An OAuth connection made with choose myself keeps that day's list. A module switched on later only joins when you reconnect.
- The older SSE transport is not offered, it is deprecated.
- 600 requests per minute per key is a fixed ceiling, not a setting.
- SCHAKL_MCP_ENABLED=false removes the whole /mcp surface; there is no per-tool switch.
Where to find it Instellingen → API en MCP (Settings → API and MCP): mint a key with the connection instructions attached, the connected OAuth clients, and for admins the OAuth clients with a client id and secret. Instellingen → Service-accounts for a key that outlives someone leaving. The endpoint itself is https://<your-host>/mcp.
Read the guide