Skip to main content

API & AI Agent Access

SimplyQuote exposes a REST API so your AI agents, scripts, and integrations can work with your quotes and invoices on your behalf. Access is controlled with scoped, revocable personal access tokens.

1. Enable API access

  1. Open your Settings page and log in
  2. Scroll to the "API & AI Agent Access" section.
  3. Name your token, choose scopes, and click "Create Token".
  4. Copy the token immediately — for security, it is only shown once.

2. Make a request

Send the token as a Bearer token in the Authorization header:

curl https://simplyquote.net/api/v1/quotes \
  -H "Authorization: Bearer sq_your_token_here"

3. Create and update documents

Creating and updating quotes and invoices uses the same token with a write scope. Totals are always computed server-side from the items you send:

curl -X POST https://simplyquote.net/api/v1/quotes \
  -H "Authorization: Bearer sq_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "Acme Ltd",
    "client_email": "billing@acme.example",
    "items": [
      { "description": "Consulting", "quantity": 4, "unit_price": 120 },
      { "description": "Hosting (1 month)", "quantity": 1, "unit_price": 30 }
    ],
    "currency": "USD",
    "tax_rate": 10,
    "expiry_date": "2026-10-01"
  }'

Success response (lists look the same, with quotes/invoices + total/page/limit/has_more):

// Wrapped envelope (POST/PATCH/lists): { success:true, data:{ quote } }
{
  "success": true,
  "data": {
    "quote": { "id": "uuid", "quote_number": "Q-1001", "total": 528.0 }
  }
}
curl -X PATCH https://simplyquote.net/api/v1/invoices/{id} \
  -H "Authorization: Bearer sq_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{ "status": "paid" }'

Listing & pagination

List endpoints accept updated_since (ISO 8601), page (default 1), limit (default 20, max 100), and status (case-insensitive; unknown values are ignored). Responses include total, page, limit, and has_more.

GET /api/v1/quotes?status=sent&page=1&limit=20&updated_since=2026-01-01T00:00:00Z

Field rules for create & update

items is required on POST: non-empty array, each with a non-empty description, quantity > 0 (default 1), unit_price >= 0 (default 0). tax_rate is a percent (0–100); on create, tax_rate > 0 implies tax_enabled. currency is uppercased. issue_date accepts any parseable date (stored YYYY-MM-DD). Quotes use expiry_date, invoices use due_date (either alias is accepted, null clears). Status: quotes draft|sent, invoices draft|sent|viewed|paid|cancelled — setting paid stamps paid_at. Totals are always recomputed server-side.

4. Use with an AI agent

Give your agent the base URL, your token, and the endpoint list below. Example instructions for any tool-calling agent (Claude, GPT, LangChain, etc.):

You can access my SimplyQuote account.
Base URL: https://simplyquote.net
For every request, send header: Authorization: Bearer sq_your_token_here
Available endpoints: GET/POST /api/v1/quotes, GET/PATCH /api/v1/quotes/{id},
GET/POST /api/v1/invoices, GET/PATCH /api/v1/invoices/{id}, POST /api/mcp
Lists support ?page, ?limit (max 100), ?status, ?updated_since and return { success:true, data:{ quotes|invoices, total, page, limit, has_more } }.
Single GETs return the bare object. {id} is the UUID, not the document number.
Use POST to create documents — always send items with description, quantity, unit_price.

5. Connect via MCP (recommended for agents)

SimplyQuote ships a built-in Model Context Protocol (MCP) server at /api/mcp (stateless streamable HTTP, JSON-RPC 2.0). Agents that support remote MCP servers (Claude, ChatGPT, Cursor, VS Code, etc.) can connect directly with your token — no prompt engineering required. Available tools:

list_quotesget_quotecreate_quotelist_invoicesget_invoicecreate_invoice

{
  "mcpServers": {
    "simplyquote": {
      "type": "http",
      "url": "https://simplyquote.net/api/mcp",
      "headers": {
        "Authorization": "Bearer sq_your_token_here"
      }
    }
  }
}

Example tool call (JSON-RPC 2.0 — batch requests are rejected, notifications return 202, GET returns 405):

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": { "name": "list_quotes", "arguments": { "limit": 5 } }
}

Each tool call is enforced against your token's scopes — a read-only token can list and fetch, while creating quotes or invoices requires the matching write scope.

Endpoints

MethodPathRequired scopeDescription
GET/api/v1/quotesquotes:readList quotes
GET/api/v1/quotes/{id}quotes:readGet a single quote
POST/api/v1/quotesquotes:writeCreate a quote (totals computed server-side)
PATCH/api/v1/quotes/{id}quotes:writeUpdate a quote (status: draft | sent)
GET/api/v1/invoicesinvoices:readList invoices
GET/api/v1/invoices/{id}invoices:readGet a single invoice
POST/api/v1/invoicesinvoices:writeCreate an invoice (totals computed server-side)
PATCH/api/v1/invoices/{id}invoices:writeUpdate an invoice (status: draft | sent | viewed | paid | cancelled)
GET/api/v1/webhooksany valid tokenList webhook subscriptions
POST/api/v1/webhooksany valid tokenCreate a webhook (secret returned once)
PATCH/api/v1/webhooksany valid tokenUpdate a webhook
DELETE/api/v1/webhooks?id=any valid tokenDelete a webhook (?id=)
GET/api/v1/openapi.jsonnoneMachine-readable OpenAPI spec
POST/api/mcpper-toolMCP server (JSON-RPC 2.0) for AI agents

Scopes

  • quotes:readRead your quotes
  • invoices:readRead your invoices
  • clients:readReserved — no separate clients endpoint yet (client info is on quotes/invoices)
  • quotes:writeCreate and update quotes
  • invoices:writeCreate and update invoices
  • clients:writeReserved — no separate clients endpoint yet (client info is created/updated implicitly when you save quotes/invoices)

Response format & errors

REST endpoints use two shapes — check which one applies:

{
  "success": true,
  "data": { ... },
  "error": { "code": "...", "message": "...", "details": { ... } }
}

Single-document GETs return the bare object above (no wrapper), with flat { error, message } errors — e.g. 404 when the id is unknown. Lists and POST/PATCH always use the wrapped envelope.

// Flat envelope (single GET): bare object, no success/data wrapper
{
  "id": "uuid",
  "quote_number": "Q-1001",
  "status": "sent",
  "total": 528.0
}

// unknown id:
{ "error": "not_found", "message": "Quote not found" }
  • successtrue on success, false on failure
  • datathe payload on success
  • errorthe error code, message, and optional details on failure

The most common error codes:

  • UNAUTHORIZEDMissing, invalid, or expired token (HTTP 401)
  • FORBIDDENToken does not have the required scope (HTTP 403)
  • VALIDATION_ERRORRequest body failed validation (HTTP 400)
  • NOT_FOUNDThe requested document does not exist (HTTP 404)
  • DATABASE_ERRORStorage error while handling the request (HTTP 500)
  • RATE_LIMITToo many requests — retry later (HTTP 429)

Webhooks, OAuth & full reference

Webhooks: manage subscriptions at GET/POST/PATCH/DELETE /api/v1/webhooks (any valid token, no specific scope). The url must be a public HTTPS endpoint. The secret is returned only on creation — verify deliveries via the X-SimplyQuote-Signature HMAC-SHA256 header.

Partner apps use OAuth 2.0 (/api/oauth/authorize, /api/oauth/token, /api/oauth/revoke) instead of personal tokens. Full details in docs/API.md and the machine-readable spec at /api/v1/openapi.json.

Security notes

  • Tokens are stored hashed — SimplyQuote never keeps the raw token.
  • Grant only the scopes your agent needs; read-only scopes are usually enough.
  • Revoke a token at any time from Settings to immediately cut off access.
  • A machine-readable spec for agents is available at /api/v1/openapi.json