Introduction
Build on Zoop with a 65-tool MCP API, secured by API keys or OAuth 2.1.
This page explains how the Zoop API works and how to make your first request.
Zoop's API is built on MCP (Model Context Protocol) — an open standard for calling structured tools over HTTP. You send a request to one endpoint, name the tool you want to run (for example customers.search), and get back structured data. This is the same tool layer the in-product Zoop AI agent uses, exposed as a stable API for your own integrations.
There is no OpenAPI/REST spec yet. All programmatic access goes through the MCP endpoint described here.
What you can build
A few examples of what the API makes possible:
- AI agents and chatbots — connect Claude Desktop, LangGraph, n8n, or any MCP-capable agent framework to a contractor's Zoop account so it can look up customers, create jobs, draft invoices, and more.
- Reporting dashboards — pull jobs, invoices, and quotes into your own data warehouse or BI tool.
- Automation scripts — create jobs or notes in bulk, sync customers from an external CRM, or trigger follow-up tasks when a job is marked done.
- Internal tools — build a custom dispatch interface or field-tech mobile view on top of the same data Zoop uses.
The API surface
Endpoint: POST /api/mcp
There is one endpoint. Every request is a POST to that path — you do not need a tenant ID in the URL. The tenant your request operates on is determined by your access token, not the path.
Requests use JSON-RPC 2.0 format: a JSON body with a method field naming the action and a params field carrying the arguments. You will see full examples in Making your first request below.
The server advertises 65 tools across 13 entities:
| Entity | Tools include |
|---|---|
| Customers | search, get, create, update, archive |
| Contacts | list, get, create, update, archive |
| Locations | list, get, create, update, archive |
| Tags | list, get, create, update, delete |
| Notes | list, get, create, update, archive |
| Jobs | list, get, create, update, cancel |
| Job series | list, get, create, update, end |
| Invoices | list, get, create, update, void |
| Quotes | list, get, create, update, archive |
| Catalog items | list, get, create, update, archive |
| Catalog categories | list, get, create, update, delete |
| Plans | list, get, create, update, cancel |
| Tax rates | list, get, create, update, archive |
Every entity follows the same five-verb shape: list or search, get one, create, update (partial patch), and a destructive verb (void, cancel, archive, or delete). The full catalog with scope requirements is in MCP tools.
Authentication
Every request needs an Authorization: Bearer <token> header. A bearer token is a secret string your code sends to prove it has permission to act on a Zoop account. Two token types are accepted:
API keys
The simplest path. Issue a key from Settings → API & MCP in Zoop (owner role required). Pass it directly as the bearer token — no token exchange needed. Use this for server-side scripts, background jobs, and agent pipelines.
OAuth 2.1
For integrations where your users connect their own Zoop accounts. Implements OAuth 2.1 with PKCE. The issued JWT carries the tenant ID and scopes in its claims.
Key prefixes
When you issue an API key you choose the actor type. The prefix on the key tells you which type you have:
| Prefix | Actor type | Has a user identity |
|---|---|---|
zoop_uk_ | User key | Yes — writes are attributed to the issuing user |
zoop_tk_ | Tenant key | No — reads and un-attributed writes only |
Use a user key when you need actions recorded against a specific person (for example, created_by on a new job). Use a tenant key for background scripts and read-only integrations.
Tools that require a real user identity are marked U in the tool catalog and can only be called with a user key or an OAuth token.
Scopes
Scopes are permissions attached to a token that control which tools it can call. They follow a read:<entity> / write:<entity> pattern — for example, read:customers or write:jobs. You choose scopes when you create an API key in Settings → API & MCP.
If your token is missing a required scope, you get a 403 insufficient_scope response that names the missing scope — so you know exactly which permission to add.
The full scope list is in Authentication — scopes.
Making your first request
The example below calls tools/list — a built-in method that returns all the tools your token can access. It is a safe, read-only call you can use to confirm your token works before writing any real integration code.
Replace zoop_uk_<secret> with your actual API key, and replace https://app.zoop.example with your Zoop app host.
curl -X POST https://app.zoop.example/api/mcp \
-H "Authorization: Bearer zoop_uk_<secret>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
A successful response returns a JSON object containing a list of tool definitions. If you get a 401, your token is invalid or expired. If you get a 403, a required scope is missing — check the error message for which one.
To call a specific tool, change method to tools/call and add a params body. The example below searches for customers matching "Henderson":
curl -X POST https://app.zoop.example/api/mcp \
-H "Authorization: Bearer zoop_uk_<secret>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "customers.search",
"arguments": { "q": "Henderson" }
}
}'
The Accept: application/json, text/event-stream header is required on every request. Without it, the endpoint will reject the call. Include it exactly as shown above.
Error responses
Auth failures (bad token, missing scope) return standard HTTP error codes (401, 403). All other tool failures return HTTP 200 with a JSON error object inside the response body — this is how JSON-RPC works, and it catches many developers off guard. Always check the response body, not just the HTTP status. The error message field is human-readable. Common cases:
| Situation | HTTP status | Error kind |
|---|---|---|
| Missing or invalid token | 401 | — |
| Token expired | 401 | — |
| Insufficient scope | 403 | insufficient_scope |
| Input failed validation | 200 (RPC error) | invalid_input |
| Record not found | 200 (RPC error) | not_found |
| Conflicting state | 200 (RPC error) | conflict |
| Rate limited | 429 | — (see Retry-After header) |
See Errors and Rate limits for full details.