CloudTop Office API
Read-only access to your QuoteWerks data, hosted by CloudTop Office. Throughout this page,
<tenant> is your tenant name and <key> is the API key we send you.
Overview
| Base URL | https://api.cloudtopoffice.com/<tenant> |
|---|---|
| REST | https://api.cloudtopoffice.com/<tenant>/api/<Entity> |
| GraphQL | https://api.cloudtopoffice.com/<tenant>/graphql (POST) |
| MCP | https://api.cloudtopoffice.com/<tenant>/mcp (Streamable HTTP, for Claude and other AI tools) |
| Auth | HTTP header X-Api-Key: <key> on every request. A missing or wrong key returns 403. Keys are case-sensitive. |
| Access | Read only. Create, update and delete are refused (403 on REST; MCP write tools are disabled). |
| TLS | TLS 1.2 / 1.3 |
We send your key separately. Keep it out of email bodies, tickets and source control.
A key only works for its own tenant path (/<tenant>/...).
Entities & fields
| Entity | Key | What it is |
|---|---|---|
Documents | DocumentId | Quotes, orders and invoices, one row per document. Templates and crash-recovery copies are left out. |
DocumentItems | DocumentItemId | Line items. DocumentId links each item to its document. |
Customers | CustomerKey | Customers built from the sold-to company on your documents (QuoteWerks has no separate customer list). Contact and address come from the most recent document, with counts and order totals. |
Products | ProductId* | Your QuoteWerks product catalogue (sell prices only). |
* If you have more than one catalogue, Products also has a Catalog column and the key is Catalog + ProductId.
Documents
DocumentId, DocNo, DocType (QUOTE / ORDER / INVOICE), DocStatus (free text: Open, Closed, Won, Lost, ...), DocName,
IsSuperseded, RevisionMasterDocNo, ConvertedRef, ConvertedOn, ConvertedFromRef, DocDate, DocDueDate, ExpirationDate, CreatedOn,
LastModifiedOn, SalesRep, PreparedBy, CustomerKey, CustomerName, SoldToContact, SoldToEmail, SoldToPhone, SoldToCity, SoldToState,
SoldToCountry, CustomerPONumber, CrmCompanyId, CrmContactId, ShipToCompany, ShipToCity, ShipToState, ShipToCountry, ShipVia, Terms,
ProjectNo, Currency, SubTotal, ShippingAmount, TotalTax, GrandTotal, DepositAmount, ContractStartDate, ContractEndDate.
Your own QuoteWerks custom fields are added after these, named after your labels. Use describe_entities (MCP) or GraphQL introspection to see your exact list.
DocumentItems
DocumentItemId, DocumentId, DocNo, DocType, LineNumber, LineSequence, LineType (1 = normal item; other values are comments, subtotals or headings), ItemType, Manufacturer, ManufacturerPartNumber, InternalPartNumber, Vendor, VendorPartNumber, Category, Description, Quantity, UnitOfMeasure, UnitList, UnitPrice, ExtendedPrice, SalesTax, TaxCode, RecurringBillingCycle, RecurringStartDate, RecurringEndDate, PONumber, SONumber, OrderDate, ReceivedDate, LastModifiedOn.
Customers
CustomerKey, CustomerName, PrimaryContact, PrimaryContactTitle, Email, Phone, Address1, Address2, City, State, PostalCode, Country, LastSalesRep, CrmCompanyId, DocumentCount, QuoteCount, OrderCount, InvoiceCount, OrderTotal (non-superseded orders), FirstDocDate, LastDocDate.
Products
ProductId, ManufacturerPartNumber, InternalPartNumber, VendorPartNumber, Manufacturer, Vendor, Category, ItemType, ShortDescription, Description, Price, ListPrice, UnitOfMeasure, TaxCode, IsDiscontinued, CreatedOn, LastModifiedOn.
Not exposed, by design: costs, profit, commission, internal and purchasing notes, payment data and QuoteWerks users. If you need a different set of fields, talk to us.
REST
Query options (OData style): $select, $filter, $orderby, $first (page size, default 100) and
$after (cursor). Responses look like {"value":[...], "nextLink":"..."}; follow nextLink as-is to get the next page.
In a shell, quote URLs so $ isn't expanded.
KEY='<key>' B='https://api.cloudtopoffice.com/<tenant>/api' # 10 most recent open quotes curl -H "X-Api-Key: $KEY" "$B/Documents?\$filter=DocType eq 'QUOTE' and DocStatus eq 'Open'&\$orderby=DocDate desc&\$first=10&\$select=DocumentId,DocNo,DocDate,CustomerName,GrandTotal,SalesRep" # one document by key curl -H "X-Api-Key: $KEY" "$B/Documents/DocumentId/4" # a document's line items, in order curl -H "X-Api-Key: $KEY" "$B/DocumentItems?\$filter=DocumentId eq 4&\$orderby=LineSequence" # documents changed since a date (incremental sync) curl -H "X-Api-Key: $KEY" "$B/Documents?\$filter=LastModifiedOn ge 2026-11-01T00:00:00Z&\$orderby=LastModifiedOn" # customers whose name starts with A curl -H "X-Api-Key: $KEY" "$B/Customers?\$filter=CustomerName ge 'A' and CustomerName lt 'B'&\$select=CustomerKey,CustomerName"
REST $filter supports eq ne gt ge lt le and or. It does not support startswith() or contains()
(they return 400); use GraphQL for partial-text search.
OpenAPI description (for tools that import one): GET https://api.cloudtopoffice.com/<tenant>/api/openapi with the
X-Api-Key header. It lists relative server paths and no security scheme, so set the server URL and the API-key header in the importing tool.
PowerShell:
$h = @{ 'X-Api-Key' = '<key>' }
Invoke-RestMethod -Headers $h 'https://api.cloudtopoffice.com/<tenant>/api/Products?$first=5'
GraphQL
Query names are the plural entity names in lower camel case (documents, documentItems, customers,
products). To get a single row by key, use document_by_pk(DocumentId: ...) and so on. Introspection is on, so tools like
Postman or Insomnia can load the schema.
curl -H "X-Api-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"query":"{ documents(first: 5, filter: { DocType: { eq: \"ORDER\" } }, orderBy: { DocDate: DESC }) { items { DocumentId DocNo DocDate CustomerName GrandTotal } hasNextPage endCursor } }"}' \
https://api.cloudtopoffice.com/<tenant>/graphql
# partial-text search (startsWith / endsWith / contains)
curl -H "X-Api-Key: $KEY" -H 'Content-Type: application/json' \
-d '{"query":"{ customers(first: 10, filter: { CustomerName: { contains: \"Inc\" } }) { items { CustomerKey CustomerName LastDocDate } } }"}' \
https://api.cloudtopoffice.com/<tenant>/graphql
MCP (AI assistants)
The MCP endpoint speaks Streamable HTTP (protocol 2025-06-18). It offers three tools: describe_entities, read_records and
aggregate_records. Write tools are disabled. Every request needs the X-Api-Key header, except the claude.ai path-key URL
described below.
Quick test:
curl -H "X-Api-Key: $KEY" -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"test","version":"1"}}}' \
https://api.cloudtopoffice.com/<tenant>/mcp
Clients that only speak stdio (for example Claude Desktop's config file) can use the mcp-remote bridge, which needs Node.js:
{
"mcpServers": {
"quotewerks": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://api.cloudtopoffice.com/<tenant>/mcp", "--header", "X-Api-Key:${CLOUDTOP_API_KEY}"],
"env": { "CLOUDTOP_API_KEY": "<key>" }
}
}
}
Connect from Claude
claude.ai (custom connector)
claude.ai's custom-connector dialog only takes a URL, so for MCP only you can put the key in the path:
https://api.cloudtopoffice.com/<tenant>/mcp/<key>
- In claude.ai, open Settings → Connectors → Add custom connector.
- Name: for example QuoteWerks.
- URL:
https://api.cloudtopoffice.com/<tenant>/mcp/<key>. Use the 32-character key exactly as we sent it; it is case-sensitive. - Leave OAuth and the advanced settings empty, then select Add.
- In a chat, turn the connector on from the tools menu and ask, for example, "List my 5 most recent open quotes".
The key is part of the URL. Anyone who can see the connector settings, or who is given the URL, can read your data. Only add the connector on claude.ai accounts or organisations your company controls (on Team or Enterprise, ideally added by an admin), never on shared or personal accounts, and don't paste the URL into chats, tickets or email. If the URL leaks, ask us to rotate the key; the connector then needs the new URL.
The path key only works on /<tenant>/mcp/.... REST and GraphQL always need the X-Api-Key header
(/<tenant>/api/<key>/... returns 403). URL paths and query strings are not recorded in our web server logs, so the key isn't written to disk.
Claude Code (CLI)
claude mcp add --transport http quotewerks https://api.cloudtopoffice.com/<tenant>/mcp --header "X-Api-Key: <key>"
This sends the key as a header, so it never appears in a URL.
Connect an AI assistant
Use MCP where the tool supports it, and the REST API everywhere else. The steps below are a summary; follow the linked vendor docs for the screens themselves.
| Tool | How it connects | Auth | Status |
|---|---|---|---|
| Claude | claude.ai: add a custom connector with the path-key MCP URL https://api.cloudtopoffice.com/<tenant>/mcp/<key> (see Connect from Claude).
Claude Code / Desktop: MCP URL …/<tenant>/mcp with the header.
Claude custom connectors · Claude Code MCP |
Key in the MCP path (claude.ai) or X-Api-Key header |
Tested |
| ChatGPT | Custom GPT Action on the REST API. Download the OpenAPI description from GET /<tenant>/api/openapi (send your key; it isn't public), set
servers to https://api.cloudtopoffice.com/<tenant>/api, give each operation an operationId, and paste it into the Action.
Where your plan supports MCP connectors, use the MCP URL instead.
GPT Actions · ChatGPT connectors |
Action auth: API key, custom header X-Api-Key |
Documented, not yet tested |
| Microsoft Copilot | Copilot Studio: add an MCP tool pointing at …/<tenant>/mcp, or build a custom connector from the OpenAPI description (same edits as for ChatGPT).
MCP in Copilot Studio · Custom connectors |
API key in header X-Api-Key |
Documented, not yet tested |
| Gemini | Gemini CLI: add an MCP server in settings.json with httpUrl …/<tenant>/mcp and headers {"X-Api-Key": "<key>"}. Elsewhere, call the REST API.
Gemini CLI MCP servers |
X-Api-Key header |
Documented, not yet tested |
Help
Questions, a new key, or more data exposed: email api@cloudtopoffice.com or call 866-710-4228. New here? Get started.