Overview
About 10 minutes if you have your keys handy. Anything you don't have yet, skip and come back to.
The first time an admin signs in, QuoteRunway opens the setup wizard: a full-screen guide with one task per page. Each page explains why the setting matters, shows an example of what it changes, and runs a real check (a test email, a ConnectWise call, a distributor price). Your progress is saved, so you can leave and come back.
Afterwards, Setup guide in the admin menu shows every step with a tick or a warning, and lets you re-check any connection.
- Your logo (PNG, JPG, WebP or SVG, under 1.4 MB; wide logos look best)
- An admin sign-in for Microsoft 365 (Entra) or Google Workspace, or your SMTP server details
- ConnectWise PSA API keys and a free client ID
- API access from the distributors you buy from
- An authenticator app on your phone (Microsoft Authenticator, Google Authenticator, 1Password, etc.)
1. Your company
Your name, address and logo go on every quote, every quote email, and the page where your customer accepts.
- Enter your company name (required), address, phone and website.
- Upload your logo. The wizard shows a live preview of the top of a quote as you type.
Change these any time under Settings. New quotes pick up the change; quotes already sent keep the version the customer saw.
2. Sign-in security
Everyone signs in with a password and a 6-digit code from an authenticator app. There's no way to turn the code off.
- Admin pages ask again. Settings, People, Backup, the audit log and setup ask for a fresh authenticator code every 15 minutes, so an unlocked laptop isn't an open door.
- Self-service password resets. On by default: the sign-in page shows Forgot your password?, which emails a link that works for an hour. The authenticator code is still needed afterwards. Turn it off and the link disappears; only an admin can then send a new password link from People.
- Admins can help people from People: send a password link, set a password, reset their authenticator, or sign them out everywhere.
3. Email
Quotes, reminders and customer answers go out through your mail system, so customers see your name and replies come to you. QuoteRunway never sends from its own domain.
Best for Microsoft 365. Each quote is sent from the rep's own mailbox and lands in their Sent Items.
- Sign in to entra.microsoft.com as a Global or Application administrator.
- Go to Applications > App registrations > New registration. Name it
QuoteRunway, single tenant, no redirect URI. - Open API permissions > Add a permission > Microsoft Graph > Application permissions, tick
Mail.Send, then Grant admin consent. - Open Certificates & secrets > New client secret. Copy the secret's Value (not its ID); it's only shown once.
- From Overview, copy the Directory (tenant) ID and Application (client) ID.
- In QuoteRunway, choose Microsoft 365, paste the three values, and click Save and send me a test email.
Best for Google Workspace. Each quote is sent from the rep's own Gmail and lands in their Sent folder.
- In console.cloud.google.com, create a project (e.g.
QuoteRunway) and enable the Gmail API. - Go to IAM & Admin > Service accounts > Create service account. No roles are needed.
- Open the service account, then Keys > Add key > Create new key > JSON. A file downloads; keep it safe.
- Copy the service account's Client ID (a long number, on its details page).
- In admin.google.com, go to Security > Access and data control > API controls > Manage domain-wide delegation > Add new. Paste the Client ID and the scope
https://www.googleapis.com/auth/gmail.send. - In QuoteRunway, choose Google Workspace, paste the whole JSON file, and click Save and send me a test email.
gmail.send scope lets QuoteRunway send mail as your users. It can't read anyone's inbox.Works with almost any provider. Every quote is sent from one address (for example sales@yourcompany.com), and replies go there.
| Setting | Usual value |
|---|---|
| SMTP server | From your provider, e.g. smtp.example.com |
| Port | 587 (STARTTLS, required) or 465 |
| TLS from the start | no for 587, yes for 465 |
| User name and password | A mailbox allowed to send. Use an app password if it has two-step sign-in. |
| Send from | Acme Sales <sales@acme.com> |
Unencrypted connections are refused: on port 587 the server must offer STARTTLS.
Checking it works
The test sends a real email to you. If it fails, the message says why (wrong secret, missing consent, blocked port). Add QuoteRunway's sending to your SPF/DKIM as you would for any mail system you already use; with Microsoft 365 and Google Workspace that's already done.
4. ConnectWise
Customers, contacts and tickets come from ConnectWise PSA, and won quotes become opportunities, sales orders and tickets there.
- In ConnectWise, go to System > Members > API Members and add a member (e.g.
QuoteRunway). Give it a security role that can read companies, contacts and service tickets, and write opportunities and sales orders. - Open the API member's API Keys tab and add a key. Copy the public and private keys; the private key is only shown once.
- Get a free Client ID from developer.connectwise.com.
- In QuoteRunway, enter your ConnectWise site (e.g.
na.myconnectwise.net), your company ID (the one you sign in with), both keys and the client ID. Leave the API path as it is. - Click Save and check the connection. You'll see your ConnectWise version when it works.
5. Distributors
Live cost and stock while you quote, side by side. Connect the ones you buy from; you can add more later.
| Distributor | What you need |
|---|---|
| Ingram Micro | An app on developer.ingrammicro.com with the Reseller API (client ID and secret), plus your customer number with the dash, e.g. 20-123456. |
| TD SYNNEX | Ask your rep to turn on the P&A (price and availability) XML feed: customer number, P&A user and password. Or API client credentials. |
| D&H | XML access (your dandh.com user and password) or API client credentials, plus your 10-digit account number. |
Each one is checked by pricing a real part. When you add a product to a quote, the cheapest distributor with stock is picked for you, and your margin updates as you type.
More distributors are on the way. See integrations.
6. Your team
Add the people who quote and approve. Each one gets an email to set their password and authenticator.
| Role | Can |
|---|---|
| Sales | Build and send quotes, see the pipeline and their own numbers. |
| Admin | Everything, plus people, settings, backups and the audit log. |
If email isn't connected yet, QuoteRunway gives you the set-up link to send yourself.
7. Quote templates
Your standard sections, terms and look, so every quote starts right.
Under Templates, start from the library (managed services, a firewall project, a laptop refresh, and more) or build your own: sections, saved items, terms and conditions. How the PDF looks is set under Designs: twelve built-in designs, or your own made in the design builder (colors, fonts, the cover, and blocks such as a timeline, your team or a testimonial).
8. Evidence of customer answers
Every accept, signature and decline is fingerprinted and chained, so you can prove later that nothing was changed. It works on its own; the email copy is optional.
Nothing to set up: QuoteRunway does all of this for every answer. The only choice is whether to also email the daily fingerprint to a mailbox you keep (Settings > Orders, "Also email the daily fingerprint to").
What is recorded when a customer answers
- The answer record: who (name, title, email), the typed signature, the exact words they agreed to, options and quantities chosen, PO number, date and time, IP address and browser. It is written once; the database refuses any later change or delete.
- The quote as sent: the PDF exactly as emailed, also written once.
- A certificate, made at that moment and emailed to the customer with their signed copy.
Fingerprints
Each of those gets a SHA-256 fingerprint: a 64-character code computed from every byte. Change one character and the fingerprint is completely different. The record's fingerprint is taken over its canonical JSON (keys sorted, no spaces), so anyone can recompute it. The record holds the PDF's fingerprint, and the certificate the customer received shows both.
The ledger (a hash chain)
Every answer also gets an entry in your company's evidence ledger. Each entry holds:
- its number, time, quote and revision, and the decision,
- the fingerprints of the record, the PDF and the certificate,
- the hash of the entry before it (
prevHash), - and its own hash: SHA-256 of all of the above as canonical JSON.
Because each entry includes the one before it, changing or removing any answer, even directly in the database, breaks every entry after it. QuoteRunway checks the whole chain every day and shows any break on the admins' error list.
The daily fingerprint (the anchor)
A chain proves nothing changed inside it, but someone with full database access could in theory rebuild the whole chain. So once a day, the latest entry's hash (the "head") is also kept outside the chain:
- Always: written to a daily record that can't be changed, and a copy is kept on QuoteRunway's side, apart from your company's data, where your own admins can't reach it.
- If you use ConnectWise: when an accepted quote becomes an order, its fingerprints and ledger hash go into an internal note on the sales ticket, dated by ConnectWise.
- Optional: emailed to a mailbox you choose. Best is a journal or archive mailbox (Microsoft 365 retention or litigation hold, Google Vault, or an archiving service) that keeps mail for years and can't be edited. Your mail server stamps its own date on it.
A rebuilt chain would have a different head, so it wouldn't match the copies held outside. The email is extra assurance, kept by you, not something QuoteRunway needs to work.
Proving an answer
On any answered quote, Evidence package downloads a .zip with the record, the PDF as sent, the certificate, the signed copy, the full activity log (sent, opened, viewed, answered, with IP addresses), the ledger entries and the daily fingerprints, plus a README that walks anyone through checking it by hand:
Windows: certutil -hashfile quote-as-sent.pdf SHA256
Mac / Linux: shasum -a 256 quote-as-sent.pdf
Each file's hash must match the fingerprints in the record and on the customer's certificate, and the ledger entry's hash must match the daily fingerprint for that day or later.
9. Backups
An encrypted copy of everything, kept by you, in addition to our own backups.
Under Backup, choose a passphrase and download. Keep the passphrase somewhere safe; without it the file can't be opened. A restore never undoes a customer's answer or an order that has gone through. The setup guide reminds you if your last backup is over 30 days old.
Automatic backups to your own storage
Under Backup > Automatic backups, QuoteRunway can save the same encrypted backup on a schedule to storage you own: every 6, 12 or 24 hours, every 2 days, or weekly.
- FTP (TLS on by default): host, port, user, password and folder.
- SFTP: host, port, user, a password or private key, and folder. The server's host key is remembered the first time and checked every time after, so a server that changes its key is refused.
- Google Cloud Storage: a bucket, a folder, and a service account key with permission to write objects to that bucket only.
- OneDrive (Microsoft 365): an app registration (tenant ID, client ID and secret) with
Files.ReadWrite.Allapplication permission, and the mailbox (UPN) whose OneDrive holds the backups. - Dropbox: an app's key, secret and a refresh token (scoped app folder recommended).
- Box: a Custom App using Client Credentials Grant (client ID, secret, enterprise ID) and the ID of the folder to save into.
How it works:
- Encryption: each file is encrypted with AES-256-GCM using a passphrase you choose when you turn it on. Keep that passphrase somewhere safe: QuoteRunway stores it sealed, and you need it to restore from any of these files.
- Your keys: storage passwords and keys are sealed too, and never shown again after saving.
- Changes need confirming: any change to these settings asks for your authenticator code.
- Test and run: Check the connection uploads a small test file. Back up now makes a backup straight away.
- Failures: a failed run is tried again an hour later, shows on the card and in Recent errors.
- Old files: QuoteRunway never deletes from your storage. Set your own retention there.
10. Your first quote
Start from a ConnectWise ticket or a customer, add parts, send it.
- New quote: pick the customer and contact (or a ticket).
- Add parts by searching distributors, from saved items, or from a template.
- Check the preview and send. The customer gets an email with a link to review, choose options, sign and add a PO, on any device.
- When they accept, you're told straight away and the order is built in ConnectWise.
Keeping it healthy
The Setup guide keeps checking your setup after you're done and flags problems such as:
- email that stopped sending (an expired Microsoft secret, a changed SMTP password),
- a ConnectWise key that was turned off,
- a distributor login that changed,
- an admin without an authenticator, or no recent backup.
Each problem has a Fix button that opens the right step, and Check again to re-test once it's fixed.
The API (Enterprise)
Connect QuoteRunway to your own tools: your PSA, CRM, accounting, scripts or Zapier.
The full reference (every endpoint, field and example, with a request tester) is in the app: API docs in the menu, or app.quoterunway.com/api-docs. The contract itself is public: openapi.v1.json (OpenAPI 3.1).
Keys. An admin makes them under Settings > API, with their authenticator code. Each key:
- works for your company only, and only for what the admin ticks (its scopes: read quotes, change them, send them, record answers, the ConnectWise steps, the catalog, templates, follow-ups, people, settings, the audit log, webhooks);
- looks like
qr_live_<id>_<secret>and is shown once. QuoteRunway keeps only a fingerprint of it; - expires (1 year unless you pick otherwise, 2 years at most), can be limited to your office's addresses, rotated (the old secret keeps working 24 hours, or stops at once) and revoked;
- shows in the audit log and on quotes as "API: its name", with when and where it was last used.
Never over the API: connection keys and secrets, backups and restores, updates, admin accounts, and API keys themselves. Those stay in the app.
Your first call. Send the key on every request as Authorization: Bearer … (nothing else: no cookies). GET /me shows the key's name, scopes and expiry:
export QUOTERUNWAY_KEY="qr_live_…"
curl https://app.quoterunway.com/api/v1/me -H "Authorization: Bearer $QUOTERUNWAY_KEY"
# quotes waiting for an answer, 50 at a time
curl "https://app.quoterunway.com/api/v1/quotes?status=sent&limit=50" -H "Authorization: Bearer $QUOTERUNWAY_KEY"
# a new quote (a retry with the same Idempotency-Key never makes a second one)
curl https://app.quoterunway.com/api/v1/quotes -H "Authorization: Bearer $QUOTERUNWAY_KEY" \
-H "Content-Type: application/json" -H "Idempotency-Key: crm-deal-4411" \
-d '{"customer":{"psaId":"19297","name":"Northwind Dental"},"salesTicket":{"psaId":"566015"},
"lines":[{"kind":"product","mpn":"20VE0057US","description":"ThinkPad T14","qty":5,"price":1150}]}'$h = @{ Authorization = "Bearer $env:QUOTERUNWAY_KEY" }
Invoke-RestMethod https://app.quoterunway.com/api/v1/me -Headers $h
# quotes waiting for an answer, every page
$url = "https://app.quoterunway.com/api/v1/quotes?status=sent&limit=200"
$all = @(); $after = $null
do {
$page = Invoke-RestMethod ($url + $(if ($after) { "&after=$after" })) -Headers $h
$all += $page.data; $after = $page.nextCursor
} while ($after)
$all | Select-Object number, title, @{ n = "total"; e = { $_.totals.total } }const api = async (path, init = {}) => {
const res = await fetch(`https://app.quoterunway.com/api/v1${path}`, {
...init,
headers: { Authorization: `Bearer ${process.env.QUOTERUNWAY_KEY}`, "Content-Type": "application/json", ...init.headers },
});
if (!res.ok) { const p = await res.json(); throw new Error(`${p.code}: ${p.detail} (request ${p.requestId})`); }
return res.status === 204 ? null : res.json();
};
console.log(await api("/me"));
// change a quote only if nobody changed it since you read it
const q = await api("/quotes/6650c0ffee0000000000abcd");
await api(`/quotes/${q.id}`, { method: "PATCH", headers: { "If-Match": String(q.version) }, body: JSON.stringify({ title: "Laptop refresh, phase 2" }) });The rules every call follows.
- Same rules as the app: the same code runs underneath. Someone editing a quote in the app has it (409
quote_locked); answered quotes can't be changed; sending needs the cover letter, a contact and a valid date, and goes out through your own mail from the quote owner's mailbox. - Errors are
application/problem+json(RFC 9457) with a stablecodeand arequestIdto quote to us. - Lists come newest first, up to 200 at a time: follow
nextCursorwith?after=.updatedSincegives only what changed, for syncing. - Changes take
If-Match(theETagorversionyou read); a stale one is a 412. Creates and actions take anIdempotency-Key, kept 24 hours. - Rate limits: 600 requests a minute per key and 1,200 per company, shown in
RateLimit-Limit,RateLimit-RemainingandRateLimit-Reset; over it is a 429 withRetry-After. - No breaking changes within v1. v1 only grows: new endpoints, new optional fields, new values. A breaking change would be a /v2, running alongside v1 for at least 12 months, announced at least 6 months ahead. Ignore fields, values and error codes you don't know.
- Keys work only while your company is on Enterprise. If it moves to another plan the keys are kept and refused (403
plan_required) until it's back.
OAuth and MCP: connecting Claude and other AI apps (Enterprise)
Let people use QuoteRunway from Claude or another AI app, as themselves, without handing out a key.
How it works. An app connects with OAuth 2.1. The person signs in to QuoteRunway as always (password or passkey, then the authenticator code) and sees a consent screen: which app, where it came from and where it sends them back, which company, and what it asks for in the same words as API key scopes. They can untick anything. Nothing is remembered between connections: every app asks.
- Never more than the person. An app gets what they ticked, cut to what their role allows, checked again on every call. Sales people can't give an app people, settings, audit-log or webhook access. Giving an app admin access asks for the authenticator code again.
- Tokens. An access token lasts an hour and works for one company at one address. The app renews it with a refresh token, which changes every time; if an old one is ever used again, the whole connection is cut. QuoteRunway keeps only fingerprints of tokens.
- In the audit log what an app does shows as "OAuth: app for person".
- Stopping it. Anyone can disconnect their own apps under My settings > Connected apps; admins see and can disconnect every connection under Settings > API. Disconnecting works at once.
- Same rules as the API: the same scopes, rate limits, errors and Enterprise plan check (403
plan_required). Never over OAuth or MCP either: connection keys and secrets, backups and restores, updates, admin accounts, API keys and OAuth clients.
Add QuoteRunway to Claude
- In Claude, open Settings > Connectors and choose Add custom connector. (On Team and Enterprise plans an owner adds it once under Organization settings > Connectors; then each person connects.)
- Name it QuoteRunway and enter the server address
https://app.quoterunway.com/mcp. Leave the OAuth client fields empty: Claude identifies itself. - Choose Connect. Pick your company, sign in, check what Claude asks for, untick what it shouldn't do, and choose Allow.
- In a chat, turn the QuoteRunway tools on from the tools menu. Ask, for example, "Which quotes are waiting for an answer?" or "Add 5 ThinkPad T14s to quote 1042".
Claude Code: claude mcp add --transport http quoterunway https://app.quoterunway.com/mcp, then /mcp to sign in.
The tools. About 40, built on the API: reading quotes, customers, items, products, templates, documents, follow-ups, people, settings and the audit log; drafting and changing quotes, items and follow-ups. Tools that send email to a customer, record a customer's answer, place an order in ConnectWise or delete something are marked as such, so Claude asks you before using them. PDFs and the evidence download stay in the app. Each tool appears only if the connection has its scope.
Treat what comes back as data. Quotes, customer names and notes are text other people wrote. An AI app can be misled by instructions hidden in that text ("prompt injection"), so keep the confirmations on for anything that sends, records or orders, and give an app only the scopes it needs.
For developers.
- MCP server:
https://app.quoterunway.com/mcp(Streamable HTTP, stateless JSON, protocol 2025-11-25, 2025-06-18 and 2025-03-26). Server card:/mcp/server-card. - Discovery:
/.well-known/oauth-protected-resource/mcpand/.well-known/oauth-protected-resource/api/v1(RFC 9728),/.well-known/oauth-authorization-server(RFC 8414). A 401 carriesWWW-Authenticate: Bearer resource_metadata="…". - Authorization code with PKCE (S256 only) at
/oauth/authorizeand/oauth/token, withresource(RFC 8707) naming the MCP server or the API. Clients identify with a Client ID Metadata Document (an HTTPS URL) or register at/oauth/register(RFC 7591; limited per address; unused registrations expire). Revoke at/oauth/revoke(RFC 7009). - OAuth access tokens also work on the API v1 as
Authorization: Bearer qr_at_…, alongside API keys. - Server to server: an admin can make an OAuth client (client credentials) under Settings > API. It acts as itself, like an API key, with the scopes the admin ticks.
Webhooks and chat alerts (Pro and Enterprise)
Tell your team or your own systems the moment something happens: a quote opened, accepted or declined, an order that failed.
Under Settings > Webhooks, an admin adds up to 20 destinations and picks the events each one gets. Chat channels come with Pro; webhooks to your own systems come with Enterprise.
Chat channels:
- Slack (an incoming webhook URL), Microsoft Teams (a Workflows webhook URL; messages arrive as an Adaptive Card) or Google Chat (a space's webhook URL).
- The address is the key to post in that channel, so it's stored sealed, and only its host is shown again.
- By default a channel gets quote viewed, accepted and declined, and order failed.
Your own systems ("custom"):
- Each event is an HTTPS
POSTof JSON to an address on the public internet (private and internal addresses are refused):{ "id": "evt_…", "type": "quote.accepted", "createdAt": "…", "data": { "object": { … } } }. - The signing secret (
whsec_…) is shown once when you add the destination. You can make a new one at any time.
Events: quote.created, quote.updated, quote.sent, quote.viewed, quote.accepted, quote.declined, quote.expired, quote.recalled, order.processed, order.failed, task.created, task.done, person.created, person.disabled.
Checking the signature. Each request carries QuoteRunway-Signature: t=<unix seconds>,v1=<hex>, where v1 is the HMAC-SHA256, keyed with your secret, of t, a dot, and the raw request body. Recompute it, compare in constant time, and refuse anything more than 5 minutes old:
const [t, v1] = header.split(",").map((p) => p.split("=")[1]);
const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
const ok = crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected)) && Math.abs(Date.now() / 1000 - t) < 300;
Delivery.
- Response: answer with any 2xx within 10 seconds. Anything else is retried after 1, 5, 30, 120, 360 and 720 minutes.
- Duplicates: the same event can arrive more than once, so use its
idto ignore repeats. - Log: each destination keeps a 30-day delivery log (status, response code, attempt), with Retry and a Send a test button.
- Repeated failures: after 20 events in a row give up, the destination is turned off and admins are emailed.
Getting help
In the app, open Help (the question mark at the top of every page): Ask for help for anyone, and Report a problem for admins. The version, page and any recent errors go with it unless you untick them, and you can paste a screenshot. Your requests and their status are under Help > My requests. Otherwise use the contact form or email support@quoterunway.com.