{
	"openapi": "3.1.0",
	"info": {
		"title": "QuoteRunway API",
		"version": "1.1.0",
		"summary": "Quotes, lines, customers' answers and orders, for your PSA, CRM, accounting and scripts.",
		"description": "QuoteRunway's API, for your PSA, CRM, accounting and scripts. It's part of the **Enterprise** plan.\n\n## Getting started\n1. An admin creates a key under **Settings > API**, ticking only the scopes the integration needs. It looks like `qr_live_<id>_<secret>` and is shown once.\n2. Send it on every call: `Authorization: Bearer qr_live_...`. Nothing else: no cookies, no other headers.\n3. Try `GET /me` to see the key's name, scopes and expiry.\n\nApps that act for a person (Claude and other AI agents, partner apps) use **OAuth 2.1** instead of a key: authorization code with PKCE (S256), and the person signs in to QuoteRunway and agrees on a consent screen. A company's own servers can use OAuth client credentials (Settings > API). Access tokens look like `qr_at_...`, are sent the same way (`Authorization: Bearer`), last an hour, and have the same scopes; a person's token never does more than their role allows. Discovery: `/.well-known/oauth-authorization-server` and `/.well-known/oauth-protected-resource/api/v1`. There's also an MCP server at `/mcp`. See https://quoterunway.com/docs/#oauth.\n\n```bash\ncurl https://app.quoterunway.com/api/v1/quotes?status=sent \\\n  -H \"Authorization: Bearer $QUOTERUNWAY_KEY\"\n```\n\nThe key decides the company: `app.quoterunway.com` works for every company, and so does your own address (`<you>.quoterunway.com`). A key used on another company's address is refused.\n\n## Conventions\n- **JSON over HTTPS.** camelCase fields, ISO 8601 UTC times, days as `YYYY-MM-DD`, money as numbers rounded to cents, ids as strings.\n- **Pagination:** `?limit=50&after=<cursor>` (limit up to 200), newest first. Lists return `{ data, nextCursor }`; `nextCursor` is null on the last page. `updatedSince` syncs changes only.\n- **Concurrency:** objects carry `version` and answers an `ETag`; send either as `If-Match` on `PATCH` and `PUT`. A stale one is a 412 `precondition_failed`.\n- **Idempotency:** send an `Idempotency-Key` (8 to 100 letters, digits, `.`, `:`, `-`, `_`) on every `POST`, so a retry never sends a quote twice. The first answer is replayed for 24 hours (with `Idempotent-Replayed: true`); the same key with a different body is a 409 `idempotency_conflict`.\n- **Errors** are RFC 9457 `application/problem+json` with a stable `code` (listed under the Problem schema) and a `requestId` to quote to support.\n- **Rate limits:** 600 requests a minute per key and 1200 per company. Every answer has `RateLimit-Limit`, `RateLimit-Remaining` and `RateLimit-Reset` (seconds); over the limit is a 429 with `Retry-After`.\n- **Same rules as the app:** the same code runs underneath. Someone editing a quote in the app locks it (409 `quote_locked`); answered quotes can't be changed; sending needs the cover letter, a contact and a valid date; mail goes out only through your own mail system; ConnectWise steps follow your settings.\n- **Who did it:** each key acts as \"API: <key name>\", and an OAuth token as \"OAuth: <app> for <person>\" (or \"OAuth: <client>\"). It shows in the audit log (with the key's id), on quotes as owner or editor, and in the quote's history.\n- **Never over the API:** connection keys and secrets, backup and restore, updates, and API keys themselves (an admin manages keys in the app, with their authenticator code).\n\n## Webhooks\nEach delivery is a `POST` of an `Event` with `QuoteRunway-Signature: t=<unix>,v1=<hex>`, where `v1` is HMAC-SHA256 of `\"<t>.<raw body>\"` with the endpoint's secret. Reject anything older than 5 minutes, and drop duplicate `id`s. Failed deliveries are retried for about 21 hours.\n\n## Versioning\nWithin v1, changes are only additive: new endpoints, new optional inputs, new fields in answers, new enum values and error codes (ignore ones you don't know). A breaking change means /v2, which runs alongside v1 for at least 12 months, with at least 6 months' notice before v1 ends.",
		"contact": {
			"name": "QuoteRunway support",
			"email": "support@quoterunway.com",
			"url": "https://quoterunway.com/docs/#api"
		},
		"license": {
			"name": "Proprietary",
			"identifier": "LicenseRef-QuoteRunway"
		}
	},
	"jsonSchemaDialect": "https://spec.openapis.org/oas/3.1/dialect/base",
	"servers": [
		{
			"url": "https://app.quoterunway.com/api/v1",
			"description": "Every company (the key picks the company)"
		}
	],
	"security": [
		{
			"apiKey": []
		},
		{
			"oauth2": []
		}
	],
	"tags": [
		{
			"name": "Quotes",
			"description": "Quotes: list, read, create, change, delete drafts"
		},
		{
			"name": "Quote lines",
			"description": "Lines on a quote"
		},
		{
			"name": "Quote actions",
			"description": "Send, remind, recall, reopen, record the answer, and the ConnectWise steps"
		},
		{
			"name": "Quote files",
			"description": "PDFs, sent revisions, the customer's answer and its evidence"
		},
		{
			"name": "Saved items",
			"description": "Labor, licences, stock items and anything quoted often"
		},
		{
			"name": "Products",
			"description": "Product search and live distributor price and stock"
		},
		{
			"name": "Templates",
			"description": "Quote templates"
		},
		{
			"name": "Documents",
			"description": "Document sections that go around the pricing"
		},
		{
			"name": "Tasks",
			"description": "Follow-ups"
		},
		{
			"name": "People",
			"description": "Your team's accounts"
		},
		{
			"name": "Customers",
			"description": "Customers, contacts and sites, read from ConnectWise"
		},
		{
			"name": "Settings",
			"description": "Company settings (never keys or secrets)"
		},
		{
			"name": "Audit",
			"description": "The audit log"
		},
		{
			"name": "Webhooks",
			"description": "Webhook endpoints and deliveries"
		},
		{
			"name": "Meta",
			"description": "The key you're using, and this description"
		}
	],
	"paths": {
		"/audit": {
			"get": {
				"operationId": "listAudit",
				"tags": [
					"Audit"
				],
				"summary": "The audit log",
				"description": "Newest first: who did what, to what, from where. Kept 2 years. Calls made with keys carry the key's id (`actor.keyId`).\n\nScope: `audit:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"audit:read"
						]
					}
				],
				"x-scopes": [
					"audit:read"
				],
				"parameters": [
					{
						"name": "limit",
						"in": "query",
						"schema": {
							"type": "integer",
							"minimum": 1,
							"maximum": 200,
							"default": 50
						},
						"description": "Up to 200 (default 50)"
					},
					{
						"name": "after",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The `nextCursor` from the previous page"
					},
					{
						"name": "person",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "A person's id"
					},
					{
						"name": "key",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "Who called, by id, as `actor.keyId` shows it: an API key's id (the <id> in qr_live_<id>_…), `oauth_<grantId>` for a person's OAuth connection (MCP too), or the client id (`qrc_…`) for a company's client-credentials app"
					},
					{
						"name": "action",
						"in": "query",
						"schema": {
							"type": "string",
							"maxLength": 60
						},
						"description": "An action, or its start (`quote.` for everything on quotes)"
					},
					{
						"name": "from",
						"in": "query",
						"schema": {
							"type": "string",
							"format": "date"
						},
						"description": "From this day (YYYY-MM-DD, UTC)"
					},
					{
						"name": "to",
						"in": "query",
						"schema": {
							"type": "string",
							"format": "date"
						},
						"description": "To this day, inclusive"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"required": [
										"data",
										"nextCursor"
									],
									"properties": {
										"data": {
											"type": "array",
											"items": {
												"$ref": "#/components/schemas/AuditEntry"
											}
										},
										"nextCursor": {
											"type": [
												"string",
												"null"
											],
											"description": "Pass as `after` for the next page; null on the last page"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/customers": {
			"get": {
				"operationId": "listCustomers",
				"tags": [
					"Customers"
				],
				"summary": "Find customers in ConnectWise",
				"description": "Active ConnectWise companies whose name or identifier contains `q` (up to 25).\n\nScope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "q",
						"in": "query",
						"schema": {
							"type": "string",
							"maxLength": 80
						}
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/PsaCustomer"
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/customers/{id}/contacts": {
			"get": {
				"operationId": "listCustomerContacts",
				"tags": [
					"Customers"
				],
				"summary": "A customer's contacts",
				"description": "Scope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The customer's ConnectWise id (`psaId`)"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/Contact"
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/customers/{id}/sites": {
			"get": {
				"operationId": "listCustomerSites",
				"tags": [
					"Customers"
				],
				"summary": "A customer's sites",
				"description": "Scope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The customer's ConnectWise id (`psaId`)"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/Site"
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/documents": {
			"get": {
				"operationId": "listDocuments",
				"tags": [
					"Documents"
				],
				"summary": "List document sections",
				"description": "Scope: `templates:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"templates:read"
						]
					}
				],
				"x-scopes": [
					"templates:read"
				],
				"parameters": [
					{
						"name": "limit",
						"in": "query",
						"schema": {
							"type": "integer",
							"minimum": 1,
							"maximum": 200,
							"default": 50
						},
						"description": "Up to 200 (default 50)"
					},
					{
						"name": "after",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The `nextCursor` from the previous page"
					},
					{
						"name": "updatedSince",
						"in": "query",
						"schema": {
							"type": "string",
							"format": "date-time"
						},
						"description": "Only ones changed at or after this time (ISO 8601), for syncing"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"required": [
										"data",
										"nextCursor"
									],
									"properties": {
										"data": {
											"type": "array",
											"items": {
												"$ref": "#/components/schemas/Document"
											}
										},
										"nextCursor": {
											"type": [
												"string",
												"null"
											],
											"description": "Pass as `after` for the next page; null on the last page"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"post": {
				"operationId": "createDocument",
				"tags": [
					"Documents"
				],
				"summary": "Create a document section",
				"description": "Scope: `templates:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"templates:write"
						]
					}
				],
				"x-scopes": [
					"templates:write"
				],
				"parameters": [
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Document"
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Created",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Document"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/documents/{id}": {
			"get": {
				"operationId": "getDocument",
				"tags": [
					"Documents"
				],
				"summary": "Get a document section",
				"description": "Scope: `templates:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"templates:read"
						]
					}
				],
				"x-scopes": [
					"templates:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Document"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"patch": {
				"operationId": "updateDocument",
				"tags": [
					"Documents"
				],
				"summary": "Change a document section",
				"description": "Scope: `templates:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"templates:write"
						]
					}
				],
				"x-scopes": [
					"templates:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "If-Match",
						"in": "header",
						"schema": {
							"type": "string"
						},
						"description": "The ETag or `version` you last read. A stale one is a 412."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/DocumentPatch"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Document"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"412": {
						"description": "It changed since you read it (`precondition_failed`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"delete": {
				"operationId": "deleteDocument",
				"tags": [
					"Documents"
				],
				"summary": "Remove a document section",
				"description": "Taken off templates; quotes already sent keep their own copy of the words.\n\nScope: `templates:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"templates:write"
						]
					}
				],
				"x-scopes": [
					"templates:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"204": {
						"description": "Done",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/items": {
			"get": {
				"operationId": "listItems",
				"tags": [
					"Saved items"
				],
				"summary": "List saved items",
				"description": "Scope: `catalog:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"catalog:read"
						]
					}
				],
				"x-scopes": [
					"catalog:read"
				],
				"parameters": [
					{
						"name": "limit",
						"in": "query",
						"schema": {
							"type": "integer",
							"minimum": 1,
							"maximum": 200,
							"default": 50
						},
						"description": "Up to 200 (default 50)"
					},
					{
						"name": "after",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The `nextCursor` from the previous page"
					},
					{
						"name": "updatedSince",
						"in": "query",
						"schema": {
							"type": "string",
							"format": "date-time"
						},
						"description": "Only ones changed at or after this time (ISO 8601), for syncing"
					},
					{
						"name": "kind",
						"in": "query",
						"schema": {
							"type": "string",
							"enum": [
								"product",
								"warranty",
								"stock",
								"labor",
								"license",
								"fee"
							]
						}
					},
					{
						"name": "q",
						"in": "query",
						"schema": {
							"type": "string",
							"maxLength": 100
						},
						"description": "Text in the name, part number or description"
					},
					{
						"name": "active",
						"in": "query",
						"schema": {
							"type": "boolean"
						},
						"description": "false: the ones turned off (default: active ones)"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"required": [
										"data",
										"nextCursor"
									],
									"properties": {
										"data": {
											"type": "array",
											"items": {
												"$ref": "#/components/schemas/Item"
											}
										},
										"nextCursor": {
											"type": [
												"string",
												"null"
											],
											"description": "Pass as `after` for the next page; null on the last page"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"post": {
				"operationId": "createItem",
				"tags": [
					"Saved items"
				],
				"summary": "Save an item",
				"description": "Scope: `catalog:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"catalog:write"
						]
					}
				],
				"x-scopes": [
					"catalog:write"
				],
				"parameters": [
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Item"
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Created",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Item"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/items/{id}": {
			"get": {
				"operationId": "getItem",
				"tags": [
					"Saved items"
				],
				"summary": "Get a saved item",
				"description": "Scope: `catalog:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"catalog:read"
						]
					}
				],
				"x-scopes": [
					"catalog:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Item"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"patch": {
				"operationId": "updateItem",
				"tags": [
					"Saved items"
				],
				"summary": "Change a saved item",
				"description": "Only the fields sent change. Items kept in step with the ConnectWise catalog (`fromPsa`) keep its cost, price and part number. `active: false` turns it off.\n\nScope: `catalog:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"catalog:write"
						]
					}
				],
				"x-scopes": [
					"catalog:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "If-Match",
						"in": "header",
						"schema": {
							"type": "string"
						},
						"description": "The ETag or `version` you last read. A stale one is a 412."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/ItemPatch"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Item"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"412": {
						"description": "It changed since you read it (`precondition_failed`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/me": {
			"get": {
				"operationId": "getMe",
				"tags": [
					"Meta"
				],
				"summary": "The key you're using",
				"description": "Its name, scopes and expiry, and the company. A good first call.\n\nAny valid key.",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": []
					}
				],
				"x-scopes": [],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Me"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/openapi.json": {
			"get": {
				"operationId": "getOpenApi",
				"tags": [
					"Meta"
				],
				"summary": "This description (OpenAPI 3.1)",
				"description": "No key needed.",
				"security": [],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					}
				}
			}
		},
		"/people": {
			"get": {
				"operationId": "listPeople",
				"tags": [
					"People"
				],
				"summary": "List your team",
				"description": "Scope: `people:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"people:read"
						]
					}
				],
				"x-scopes": [
					"people:read"
				],
				"parameters": [
					{
						"name": "limit",
						"in": "query",
						"schema": {
							"type": "integer",
							"minimum": 1,
							"maximum": 200,
							"default": 50
						},
						"description": "Up to 200 (default 50)"
					},
					{
						"name": "after",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The `nextCursor` from the previous page"
					},
					{
						"name": "updatedSince",
						"in": "query",
						"schema": {
							"type": "string",
							"format": "date-time"
						},
						"description": "Only ones changed at or after this time (ISO 8601), for syncing"
					},
					{
						"name": "role",
						"in": "query",
						"schema": {
							"type": "string",
							"enum": [
								"admin",
								"sales",
								"tech"
							]
						}
					},
					{
						"name": "active",
						"in": "query",
						"schema": {
							"type": "boolean"
						}
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"required": [
										"data",
										"nextCursor"
									],
									"properties": {
										"data": {
											"type": "array",
											"items": {
												"$ref": "#/components/schemas/Person"
											}
										},
										"nextCursor": {
											"type": [
												"string",
												"null"
											],
											"description": "Pass as `after` for the next page; null on the last page"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"post": {
				"operationId": "createPerson",
				"tags": [
					"People"
				],
				"summary": "Add a person",
				"description": "They're emailed a link to set their password (the link is never returned). Only your company's email domains. Admins can't be made over the API (403 `forbidden`).\n\nScope: `people:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"people:write"
						]
					}
				],
				"x-scopes": [
					"people:write"
				],
				"parameters": [
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Person"
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Created",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Person"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/people/{id}": {
			"get": {
				"operationId": "getPerson",
				"tags": [
					"People"
				],
				"summary": "Get a person",
				"description": "Scope: `people:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"people:read"
						]
					}
				],
				"x-scopes": [
					"people:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Person"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"patch": {
				"operationId": "updatePerson",
				"tags": [
					"People"
				],
				"summary": "Change a person",
				"description": "Name, role, title, phone, or `active: false` to turn their account off (they're signed out everywhere). An admin's account, or making someone an admin, is done in the app (403 `forbidden`). The email address can't be changed.\n\nScope: `people:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"people:write"
						]
					}
				],
				"x-scopes": [
					"people:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "If-Match",
						"in": "header",
						"schema": {
							"type": "string"
						},
						"description": "The ETag or `version` you last read. A stale one is a 412."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/PersonPatch"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Person"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"412": {
						"description": "It changed since you read it (`precondition_failed`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/people/{id}/reset": {
			"post": {
				"operationId": "resetPerson",
				"tags": [
					"People"
				],
				"summary": "Send a new password link",
				"description": "Signs them out everywhere and emails them a link to set a new password; with `authenticator: true` their authenticator is removed too (a lost phone). Not for admins (403 `forbidden`).\n\nScope: `people:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"people:write"
						]
					}
				],
				"x-scopes": [
					"people:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": false,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"properties": {
									"authenticator": {
										"type": "boolean"
									}
								}
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"properties": {
										"emailed": {
											"type": "boolean"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/products/search": {
			"get": {
				"operationId": "searchProducts",
				"tags": [
					"Products"
				],
				"summary": "Search distributors' catalogs",
				"description": "Keyword search at the connected distributors that support it.\n\nScope: `catalog:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"catalog:read"
						]
					}
				],
				"x-scopes": [
					"catalog:read"
				],
				"parameters": [
					{
						"name": "q",
						"in": "query",
						"required": true,
						"schema": {
							"type": "string",
							"minLength": 3,
							"maxLength": 100
						}
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/Product"
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/products/{mpn}/offers": {
			"get": {
				"operationId": "getProductOffers",
				"tags": [
					"Products"
				],
				"summary": "Live price and stock",
				"description": "Each connected distributor's cost, list price and stock for the part, now.\n\nScope: `catalog:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"catalog:read"
						]
					}
				],
				"x-scopes": [
					"catalog:read"
				],
				"parameters": [
					{
						"name": "mpn",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The manufacturer's part number"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/Offer"
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes": {
			"get": {
				"operationId": "listQuotes",
				"tags": [
					"Quotes"
				],
				"summary": "List quotes",
				"description": "Newest first. Filter by status, owner, customer and text; `updatedSince` for syncing.\n\nScope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "limit",
						"in": "query",
						"schema": {
							"type": "integer",
							"minimum": 1,
							"maximum": 200,
							"default": 50
						},
						"description": "Up to 200 (default 50)"
					},
					{
						"name": "after",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The `nextCursor` from the previous page"
					},
					{
						"name": "updatedSince",
						"in": "query",
						"schema": {
							"type": "string",
							"format": "date-time"
						},
						"description": "Only ones changed at or after this time (ISO 8601), for syncing"
					},
					{
						"name": "status",
						"in": "query",
						"schema": {
							"type": "string",
							"enum": [
								"draft",
								"sent",
								"accepted",
								"declined",
								"expired"
							]
						}
					},
					{
						"name": "owner",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The owner's id (a person, or a key)"
					},
					{
						"name": "customer",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The customer's ConnectWise id (`customer.psaId`)"
					},
					{
						"name": "q",
						"in": "query",
						"schema": {
							"type": "string",
							"maxLength": 100
						},
						"description": "Text in the title, customer, contact, a part number, or the quote number"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"required": [
										"data",
										"nextCursor"
									],
									"properties": {
										"data": {
											"type": "array",
											"items": {
												"$ref": "#/components/schemas/Quote"
											}
										},
										"nextCursor": {
											"type": [
												"string",
												"null"
											],
											"description": "Pass as `after` for the next page; null on the last page"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"post": {
				"operationId": "createQuote",
				"tags": [
					"Quotes"
				],
				"summary": "Create a quote",
				"description": "Starts from its ConnectWise sales ticket, which must be the customer's (the same check as the app). With `templateId` and no lines, the template's sections, lines and cover letter come in. It belongs to `ownerId` (an active person), or to this key; sending needs a person.\n\nScope: `quotes:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:write"
						]
					}
				],
				"x-scopes": [
					"quotes:write"
				],
				"parameters": [
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/QuoteCreate"
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Created",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							},
							"Location": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}": {
			"get": {
				"operationId": "getQuote",
				"tags": [
					"Quotes"
				],
				"summary": "Get a quote",
				"description": "Scope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"patch": {
				"operationId": "updateQuote",
				"tags": [
					"Quotes"
				],
				"summary": "Change a quote",
				"description": "Only the fields sent change. Draft, sent and expired quotes can be changed (a sent one keeps the revision the customer has until it's sent again); answered ones can't (409 `quote_not_editable`). While someone has it open in the app it's a 409 `quote_locked`. A line already on the quote keeps its part number, maker, kind, billing and tax (as for reps in the app).\n\nScope: `quotes:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:write"
						]
					}
				],
				"x-scopes": [
					"quotes:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "If-Match",
						"in": "header",
						"schema": {
							"type": "string"
						},
						"description": "The ETag or `version` you last read. A stale one is a 412."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/QuotePatch"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"412": {
						"description": "It changed since you read it (`precondition_failed`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"delete": {
				"operationId": "deleteQuote",
				"tags": [
					"Quotes"
				],
				"summary": "Delete a draft",
				"description": "Only a draft that was never sent (409 `quote_not_editable` otherwise).\n\nScope: `quotes:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:write"
						]
					}
				],
				"x-scopes": [
					"quotes:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"204": {
						"description": "Done",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/acceptance": {
			"get": {
				"operationId": "getQuoteAcceptance",
				"tags": [
					"Quote files"
				],
				"summary": "The customer's answer",
				"description": "The answer as recorded (online by the customer, or by a rep or key), and whether it still matches its fingerprint. 404 until there is one.\n\nScope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Acceptance"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/decision": {
			"post": {
				"operationId": "recordQuoteDecision",
				"tags": [
					"Quote actions"
				],
				"summary": "Record the customer's answer",
				"description": "Won (with how they said yes) or lost (with why), as a rep records it in the app. Won needs a sent revision; lost closes the ConnectWise opportunity as your settings say. The record is fingerprinted into the evidence ledger like any answer.\n\nScope: `quotes:decide`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:decide"
						]
					}
				],
				"x-scopes": [
					"quotes:decide"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Decision"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/evidence": {
			"get": {
				"operationId": "getQuoteEvidence",
				"tags": [
					"Quote files"
				],
				"summary": "The answer's evidence ledger entries",
				"description": "Each answer's entry in the company's evidence ledger: its fingerprints, and the hash of the entry before (a chain anyone can check).\n\nScope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/LedgerEntry"
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/evidence/package": {
			"get": {
				"operationId": "getQuoteEvidencePackage",
				"tags": [
					"Quote files"
				],
				"summary": "The evidence package (.zip)",
				"description": "Everything about the answer in one .zip (the record, the PDF as sent, the certificate, the ledger entry, the visits), with a README on checking each file by hand.\n\nScope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "The file",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/zip": {
								"schema": {
									"type": "string",
									"format": "binary"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/lines": {
			"post": {
				"operationId": "addQuoteLines",
				"tags": [
					"Quote lines"
				],
				"summary": "Add lines",
				"description": "Adds lines at the end of a draft, sent or expired quote (up to 500 lines in all).\n\nScope: `quotes:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:write"
						]
					}
				],
				"x-scopes": [
					"quotes:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"required": [
									"lines"
								],
								"properties": {
									"lines": {
										"type": "array",
										"minItems": 1,
										"maxItems": 500,
										"items": {
											"$ref": "#/components/schemas/Line"
										}
									}
								}
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Created",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"put": {
				"operationId": "replaceQuoteLines",
				"tags": [
					"Quote lines"
				],
				"summary": "Replace all lines",
				"description": "The quote's lines become exactly these (in this order). A line whose id is already on the quote keeps its part number, maker, kind, billing and tax.\n\nScope: `quotes:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:write"
						]
					}
				],
				"x-scopes": [
					"quotes:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "If-Match",
						"in": "header",
						"schema": {
							"type": "string"
						},
						"description": "The ETag or `version` you last read. A stale one is a 412."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"required": [
									"lines"
								],
								"properties": {
									"lines": {
										"type": "array",
										"minItems": 0,
										"maxItems": 500,
										"items": {
											"$ref": "#/components/schemas/Line"
										}
									}
								}
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"412": {
						"description": "It changed since you read it (`precondition_failed`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/lines/{lineId}": {
			"patch": {
				"operationId": "updateQuoteLine",
				"tags": [
					"Quote lines"
				],
				"summary": "Change a line",
				"description": "Only the fields sent change. The part number, maker, kind, billing and tax of a line already on the quote stay as they are, and so does the description of a line with a part number or catalog item (bring in the right product instead). Fields sent that stayed as they were are named in the `QuoteRunway-Note` header.\n\nScope: `quotes:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:write"
						]
					}
				],
				"x-scopes": [
					"quotes:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "lineId",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The line's id"
					},
					{
						"name": "If-Match",
						"in": "header",
						"schema": {
							"type": "string"
						},
						"description": "The ETag or `version` you last read. A stale one is a 412."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/LinePatch"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"412": {
						"description": "It changed since you read it (`precondition_failed`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"delete": {
				"operationId": "deleteQuoteLine",
				"tags": [
					"Quote lines"
				],
				"summary": "Remove a line",
				"description": "Scope: `quotes:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:write"
						]
					}
				],
				"x-scopes": [
					"quotes:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "lineId",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The line's id"
					},
					{
						"name": "If-Match",
						"in": "header",
						"schema": {
							"type": "string"
						},
						"description": "The ETag or `version` you last read. A stale one is a 412."
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"412": {
						"description": "It changed since you read it (`precondition_failed`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/mark-sent": {
			"post": {
				"operationId": "markQuoteSent",
				"tags": [
					"Quote actions"
				],
				"summary": "Mark the Outlook email sent",
				"description": "For companies that send from Outlook: the revision downloaded for Outlook went out.\n\nScope: `quotes:send`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:send"
						]
					}
				],
				"x-scopes": [
					"quotes:send"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/pdf": {
			"get": {
				"operationId": "getQuotePdf",
				"tags": [
					"Quote files"
				],
				"summary": "The quote's PDF",
				"description": "The revision the customer has (stamped approved or lost once answered); a preview of the quote as it is now if it was never sent.\n\nScope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "The file",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/pdf": {
								"schema": {
									"type": "string",
									"format": "binary"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/process-order": {
			"post": {
				"operationId": "processQuoteOrder",
				"tags": [
					"Quote actions"
				],
				"summary": "Turn an accepted quote into the order",
				"description": "The app's one click: won, sales order, then ticket, project or agreement as the quote's fulfilment says, as the owner (a person). Not limited to business hours (that's for the automatic conversion). 409 if it already went through.\n\nScope: `orders:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"orders:write"
						]
					}
				],
				"x-scopes": [
					"orders:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/psa-sync": {
			"post": {
				"operationId": "syncQuoteToPsa",
				"tags": [
					"Quote actions"
				],
				"summary": "Create or update the ConnectWise opportunity",
				"description": "As the owner (a person, as their ConnectWise member). Where ConnectWise writes are off for the company this is a preview and nothing is written.\n\nScope: `orders:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"orders:write"
						]
					}
				],
				"x-scopes": [
					"orders:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/recall": {
			"post": {
				"operationId": "recallQuote",
				"tags": [
					"Quote actions"
				],
				"summary": "Recall a sent quote",
				"description": "Takes it back from the customer (their link then says so); it's a draft again.\n\nScope: `quotes:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:write"
						]
					}
				],
				"x-scopes": [
					"quotes:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": false,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"properties": {
									"reason": {
										"type": "string",
										"maxLength": 500
									}
								}
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/remind": {
			"post": {
				"operationId": "remindQuote",
				"tags": [
					"Quote actions"
				],
				"summary": "Send a reminder",
				"description": "A reminder of a sent quote that's waiting for an answer, with its link and PDF: to the customer (default), the owner, or both.\n\nScope: `quotes:send`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:send"
						]
					}
				],
				"x-scopes": [
					"quotes:send"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": false,
					"content": {
						"application/json": {
							"schema": {
								"type": "object",
								"properties": {
									"to": {
										"type": "string",
										"enum": [
											"customer",
											"rep",
											"both"
										],
										"default": "customer"
									}
								}
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/reopen": {
			"post": {
				"operationId": "reopenQuote",
				"tags": [
					"Quote actions"
				],
				"summary": "Reopen an expired quote",
				"description": "An expired quote becomes a draft again (an answered one can't: copy it in the app).\n\nScope: `quotes:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:write"
						]
					}
				],
				"x-scopes": [
					"quotes:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/send": {
			"post": {
				"operationId": "sendQuote",
				"tags": [
					"Quote actions"
				],
				"summary": "Send it to the customer",
				"description": "Emails the next revision through your own mail system, from the owner's mailbox (the owner must be a person). Needs QuoteRunway sending turned on for the company (`settings.mail.directSend`; 409 `mail_not_connected` otherwise) and the same as the app: a cover letter, a contact, a valid date. Then the ConnectWise opportunity is brought up to date in the background. Send an Idempotency-Key: a retry must never send twice.\n\nScope: `quotes:send`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:send"
						]
					}
				],
				"x-scopes": [
					"quotes:send"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": false,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Send"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Quote"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"409": {
						"description": "The state doesn't allow it (`quote_locked`, `quote_not_editable`, `quote_restored`, `mail_not_connected`, `psa_not_connected`, `conflict`, `idempotency_conflict`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/versions": {
			"get": {
				"operationId": "listQuoteVersions",
				"tags": [
					"Quote files"
				],
				"summary": "Revisions sent to the customer",
				"description": "Scope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "array",
									"items": {
										"$ref": "#/components/schemas/Version"
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/quotes/{id}/versions/{n}/pdf": {
			"get": {
				"operationId": "getQuoteVersionPdf",
				"tags": [
					"Quote files"
				],
				"summary": "A sent revision's PDF",
				"description": "As sent; stamped approved or lost once that revision was answered, unless `original=true`.\n\nScope: `quotes:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"quotes:read"
						]
					}
				],
				"x-scopes": [
					"quotes:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "n",
						"in": "path",
						"required": true,
						"schema": {
							"type": "integer"
						},
						"description": "The revision number"
					},
					{
						"name": "original",
						"in": "query",
						"schema": {
							"type": "boolean"
						},
						"description": "The PDF exactly as sent, without the stamp"
					}
				],
				"responses": {
					"200": {
						"description": "The file",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/pdf": {
								"schema": {
									"type": "string",
									"format": "binary"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/settings": {
			"get": {
				"operationId": "getSettings",
				"tags": [
					"Settings"
				],
				"summary": "Company settings",
				"description": "What an integration needs to know about the company. Never keys, passwords or connection secrets.\n\nScope: `settings:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"settings:read"
						]
					}
				],
				"x-scopes": [
					"settings:read"
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Settings"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/tasks": {
			"get": {
				"operationId": "listTasks",
				"tags": [
					"Tasks"
				],
				"summary": "List follow-ups",
				"description": "Scope: `tasks:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"tasks:read"
						]
					}
				],
				"x-scopes": [
					"tasks:read"
				],
				"parameters": [
					{
						"name": "limit",
						"in": "query",
						"schema": {
							"type": "integer",
							"minimum": 1,
							"maximum": 200,
							"default": 50
						},
						"description": "Up to 200 (default 50)"
					},
					{
						"name": "after",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The `nextCursor` from the previous page"
					},
					{
						"name": "updatedSince",
						"in": "query",
						"schema": {
							"type": "string",
							"format": "date-time"
						},
						"description": "Only ones changed at or after this time (ISO 8601), for syncing"
					},
					{
						"name": "done",
						"in": "query",
						"schema": {
							"type": "boolean"
						},
						"description": "true: done ones; false: open ones; leave out for both"
					},
					{
						"name": "assignee",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The person's id"
					},
					{
						"name": "quote",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The quote's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"required": [
										"data",
										"nextCursor"
									],
									"properties": {
										"data": {
											"type": "array",
											"items": {
												"$ref": "#/components/schemas/Task"
											}
										},
										"nextCursor": {
											"type": [
												"string",
												"null"
											],
											"description": "Pass as `after` for the next page; null on the last page"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"post": {
				"operationId": "createTask",
				"tags": [
					"Tasks"
				],
				"summary": "Add a follow-up",
				"description": "Scope: `tasks:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"tasks:write"
						]
					}
				],
				"x-scopes": [
					"tasks:write"
				],
				"parameters": [
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Task"
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Created",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Task"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/tasks/{id}": {
			"get": {
				"operationId": "getTask",
				"tags": [
					"Tasks"
				],
				"summary": "Get a follow-up",
				"description": "Scope: `tasks:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"tasks:read"
						]
					}
				],
				"x-scopes": [
					"tasks:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Task"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"patch": {
				"operationId": "updateTask",
				"tags": [
					"Tasks"
				],
				"summary": "Change or finish a follow-up",
				"description": "`done: true` (with an `outcome`) finishes it; done ones on a quote go into its history.\n\nScope: `tasks:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"tasks:write"
						]
					}
				],
				"x-scopes": [
					"tasks:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "If-Match",
						"in": "header",
						"schema": {
							"type": "string"
						},
						"description": "The ETag or `version` you last read. A stale one is a 412."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/TaskPatch"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Task"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"412": {
						"description": "It changed since you read it (`precondition_failed`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"delete": {
				"operationId": "deleteTask",
				"tags": [
					"Tasks"
				],
				"summary": "Remove a follow-up",
				"description": "An automatic one is dismissed (kept, so it isn't made again).\n\nScope: `tasks:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"tasks:write"
						]
					}
				],
				"x-scopes": [
					"tasks:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"204": {
						"description": "Done",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/templates": {
			"get": {
				"operationId": "listTemplates",
				"tags": [
					"Templates"
				],
				"summary": "List templates",
				"description": "Scope: `templates:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"templates:read"
						]
					}
				],
				"x-scopes": [
					"templates:read"
				],
				"parameters": [
					{
						"name": "limit",
						"in": "query",
						"schema": {
							"type": "integer",
							"minimum": 1,
							"maximum": 200,
							"default": 50
						},
						"description": "Up to 200 (default 50)"
					},
					{
						"name": "after",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The `nextCursor` from the previous page"
					},
					{
						"name": "updatedSince",
						"in": "query",
						"schema": {
							"type": "string",
							"format": "date-time"
						},
						"description": "Only ones changed at or after this time (ISO 8601), for syncing"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"required": [
										"data",
										"nextCursor"
									],
									"properties": {
										"data": {
											"type": "array",
											"items": {
												"$ref": "#/components/schemas/Template"
											}
										},
										"nextCursor": {
											"type": [
												"string",
												"null"
											],
											"description": "Pass as `after` for the next page; null on the last page"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"post": {
				"operationId": "createTemplate",
				"tags": [
					"Templates"
				],
				"summary": "Create a template",
				"description": "Scope: `templates:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"templates:write"
						]
					}
				],
				"x-scopes": [
					"templates:write"
				],
				"parameters": [
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Template"
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Created",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Template"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/templates/{id}": {
			"get": {
				"operationId": "getTemplate",
				"tags": [
					"Templates"
				],
				"summary": "Get a template",
				"description": "Scope: `templates:read`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"templates:read"
						]
					}
				],
				"x-scopes": [
					"templates:read"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Template"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"patch": {
				"operationId": "updateTemplate",
				"tags": [
					"Templates"
				],
				"summary": "Change a template",
				"description": "Only the fields sent change. `active: false` turns it off (refused while open quotes use it).\n\nScope: `templates:write`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"templates:write"
						]
					}
				],
				"x-scopes": [
					"templates:write"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "If-Match",
						"in": "header",
						"schema": {
							"type": "string"
						},
						"description": "The ETag or `version` you last read. A stale one is a 412."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/TemplatePatch"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Template"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"412": {
						"description": "It changed since you read it (`precondition_failed`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/webhooks": {
			"get": {
				"operationId": "listWebhooks",
				"tags": [
					"Webhooks"
				],
				"summary": "List webhook endpoints",
				"description": "Up to 20, so always one page.\n\nScope: `webhooks:manage`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"webhooks:manage"
						]
					}
				],
				"x-scopes": [
					"webhooks:manage"
				],
				"parameters": [
					{
						"name": "limit",
						"in": "query",
						"schema": {
							"type": "integer",
							"minimum": 1,
							"maximum": 200,
							"default": 50
						},
						"description": "Up to 200 (default 50)"
					},
					{
						"name": "after",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The `nextCursor` from the previous page"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"required": [
										"data",
										"nextCursor"
									],
									"properties": {
										"data": {
											"type": "array",
											"items": {
												"$ref": "#/components/schemas/Webhook"
											}
										},
										"nextCursor": {
											"type": [
												"string",
												"null"
											],
											"description": "Pass as `after` for the next page; null on the last page"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"post": {
				"operationId": "createWebhook",
				"tags": [
					"Webhooks"
				],
				"summary": "Add a webhook endpoint",
				"description": "A custom endpoint gets a signing secret, returned here once (`secret`). Every event if `events` is left out.\n\nScope: `webhooks:manage`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"webhooks:manage"
						]
					}
				],
				"x-scopes": [
					"webhooks:manage"
				],
				"parameters": [
					{
						"name": "Idempotency-Key",
						"in": "header",
						"schema": {
							"type": "string",
							"minLength": 8,
							"maxLength": 100,
							"pattern": "^[\\w.:-]{8,100}$"
						},
						"description": "Any unique string. A retry with the same key gets the first answer for 24 hours; the same key with a different body is a 409 `idempotency_conflict`."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Webhook"
							}
						}
					}
				},
				"responses": {
					"201": {
						"description": "Created",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Webhook"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/webhooks/{id}": {
			"get": {
				"operationId": "getWebhook",
				"tags": [
					"Webhooks"
				],
				"summary": "Get a webhook endpoint",
				"description": "Scope: `webhooks:manage`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"webhooks:manage"
						]
					}
				],
				"x-scopes": [
					"webhooks:manage"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							},
							"ETag": {
								"schema": {
									"type": "string"
								}
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Webhook"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"patch": {
				"operationId": "updateWebhook",
				"tags": [
					"Webhooks"
				],
				"summary": "Change a webhook endpoint",
				"description": "`rotateSecret: true` makes a new signing secret, returned once in `secret`. `active: true` turns a failing one back on.\n\nScope: `webhooks:manage`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"webhooks:manage"
						]
					}
				],
				"x-scopes": [
					"webhooks:manage"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "If-Match",
						"in": "header",
						"schema": {
							"type": "string"
						},
						"description": "The ETag or `version` you last read. A stale one is a 412."
					}
				],
				"requestBody": {
					"required": true,
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/WebhookPatch"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Webhook"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"412": {
						"description": "It changed since you read it (`precondition_failed`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			},
			"delete": {
				"operationId": "deleteWebhook",
				"tags": [
					"Webhooks"
				],
				"summary": "Remove a webhook endpoint",
				"description": "Scope: `webhooks:manage`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"webhooks:manage"
						]
					}
				],
				"x-scopes": [
					"webhooks:manage"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"204": {
						"description": "Done",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/webhooks/{id}/deliveries": {
			"get": {
				"operationId": "listWebhookDeliveries",
				"tags": [
					"Webhooks"
				],
				"summary": "Recent deliveries",
				"description": "Kept 30 days, newest first.\n\nScope: `webhooks:manage`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"webhooks:manage"
						]
					}
				],
				"x-scopes": [
					"webhooks:manage"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "limit",
						"in": "query",
						"schema": {
							"type": "integer",
							"minimum": 1,
							"maximum": 200,
							"default": 50
						},
						"description": "Up to 200 (default 50)"
					},
					{
						"name": "after",
						"in": "query",
						"schema": {
							"type": "string"
						},
						"description": "The `nextCursor` from the previous page"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"type": "object",
									"required": [
										"data",
										"nextCursor"
									],
									"properties": {
										"data": {
											"type": "array",
											"items": {
												"$ref": "#/components/schemas/Delivery"
											}
										},
										"nextCursor": {
											"type": [
												"string",
												"null"
											],
											"description": "Pass as `after` for the next page; null on the last page"
										}
									}
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"422": {
						"description": "Fields need fixing (`validation_failed`): see `errors`",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/webhooks/{id}/deliveries/{d}/retry": {
			"post": {
				"operationId": "retryWebhookDelivery",
				"tags": [
					"Webhooks"
				],
				"summary": "Send a delivery again",
				"description": "Scope: `webhooks:manage`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"webhooks:manage"
						]
					}
				],
				"x-scopes": [
					"webhooks:manage"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					},
					{
						"name": "d",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The delivery's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Delivery"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		},
		"/webhooks/{id}/test": {
			"post": {
				"operationId": "testWebhook",
				"tags": [
					"Webhooks"
				],
				"summary": "Send a test event",
				"description": "A signed `quote.accepted` test event (`test: true`) to the endpoint, now; not retried.\n\nScope: `webhooks:manage`",
				"security": [
					{
						"apiKey": []
					},
					{
						"oauth2": [
							"webhooks:manage"
						]
					}
				],
				"x-scopes": [
					"webhooks:manage"
				],
				"parameters": [
					{
						"name": "id",
						"in": "path",
						"required": true,
						"schema": {
							"type": "string"
						},
						"description": "The object's id"
					}
				],
				"responses": {
					"200": {
						"description": "OK",
						"headers": {
							"RateLimit-Limit": {
								"schema": {
									"type": "integer"
								},
								"description": "Requests allowed in the current minute"
							},
							"RateLimit-Remaining": {
								"schema": {
									"type": "integer"
								}
							},
							"RateLimit-Reset": {
								"schema": {
									"type": "integer"
								},
								"description": "Seconds until the window resets"
							}
						},
						"content": {
							"application/json": {
								"schema": {
									"$ref": "#/components/schemas/Delivery"
								}
							}
						}
					},
					"400": {
						"description": "Bad request (`bad_request`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"401": {
						"description": "Missing, unknown, expired or revoked key or token (`unauthorized`, `key_expired`); `WWW-Authenticate` names the protected resource metadata (RFC 9728)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"402": {
						"description": "The company's subscription needs a payment (`account_locked`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"403": {
						"description": "Not allowed: a missing scope, an address not on the key's allowlist, or the plan (`scope_missing`, `ip_not_allowed`, `plan_required`, `forbidden`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"404": {
						"description": "Not found (`not_found`)",
						"content": {
							"application/problem+json": {
								"schema": {
									"$ref": "#/components/schemas/Problem"
								}
							}
						}
					},
					"429": {
						"$ref": "#/components/responses/RateLimited"
					}
				}
			}
		}
	},
	"webhooks": {
		"quote.created": {
			"post": {
				"operationId": "event_quote_created",
				"summary": "quote.created",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"quote.updated": {
			"post": {
				"operationId": "event_quote_updated",
				"summary": "quote.updated",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"quote.sent": {
			"post": {
				"operationId": "event_quote_sent",
				"summary": "quote.sent",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"quote.viewed": {
			"post": {
				"operationId": "event_quote_viewed",
				"summary": "quote.viewed",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"quote.accepted": {
			"post": {
				"operationId": "event_quote_accepted",
				"summary": "quote.accepted",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"quote.declined": {
			"post": {
				"operationId": "event_quote_declined",
				"summary": "quote.declined",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"quote.expired": {
			"post": {
				"operationId": "event_quote_expired",
				"summary": "quote.expired",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"quote.recalled": {
			"post": {
				"operationId": "event_quote_recalled",
				"summary": "quote.recalled",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"order.processed": {
			"post": {
				"operationId": "event_order_processed",
				"summary": "order.processed",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"order.failed": {
			"post": {
				"operationId": "event_order_failed",
				"summary": "order.failed",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"task.created": {
			"post": {
				"operationId": "event_task_created",
				"summary": "task.created",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"task.done": {
			"post": {
				"operationId": "event_task_done",
				"summary": "task.done",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"person.created": {
			"post": {
				"operationId": "event_person_created",
				"summary": "person.created",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		},
		"person.disabled": {
			"post": {
				"operationId": "event_person_disabled",
				"summary": "person.disabled",
				"requestBody": {
					"content": {
						"application/json": {
							"schema": {
								"$ref": "#/components/schemas/Event"
							}
						}
					}
				},
				"responses": {
					"200": {
						"description": "Return any 2xx within 10 seconds"
					}
				}
			}
		}
	},
	"components": {
		"securitySchemes": {
			"apiKey": {
				"type": "http",
				"scheme": "bearer",
				"bearerFormat": "qr_live_<id>_<secret>",
				"description": "An API key from Settings > API. Its scopes:\n\n- `quotes:read`: List and read quotes, their lines, versions, PDFs, the customer's answer and the evidence\n- `quotes:write`: Create, change and delete drafts, lines and sections; recall; reopen expired quotes\n- `quotes:send`: Send a quote, send a reminder, mark it sent (mail goes through your own mail system)\n- `quotes:decide`: Record the customer's answer for them (won or lost)\n- `orders:write`: Push a quote to ConnectWise and process a won order\n- `catalog:read`: Saved items, product search, distributor price and stock\n- `catalog:write`: Add and change saved items\n- `templates:read`: Templates and document sections\n- `templates:write`: Add and change templates and document sections\n- `tasks:read`: Follow-ups\n- `tasks:write`: Add, change and remove follow-ups\n- `people:read`: People (never passwords or authenticators)\n- `people:write`: Add, change or turn off people (not admins), and send them a set-password link\n- `settings:read`: Company settings (never keys or secrets)\n- `audit:read`: The audit log\n- `webhooks:manage`: Webhook endpoints and their deliveries",
				"x-scopes": [
					"quotes:read",
					"quotes:write",
					"quotes:send",
					"quotes:decide",
					"orders:write",
					"catalog:read",
					"catalog:write",
					"templates:read",
					"templates:write",
					"tasks:read",
					"tasks:write",
					"people:read",
					"people:write",
					"settings:read",
					"audit:read",
					"webhooks:manage"
				]
			},
			"oauth2": {
				"type": "oauth2",
				"description": "OAuth 2.1 access tokens (`qr_at_...`), for apps acting for a person (authorization code with PKCE S256; the person agrees on a consent screen, and the token never does more than their role allows) and for a company's own servers (client credentials, from Settings > API). Tokens last an hour and are bound to the resource asked for (RFC 8707: `/api/v1`, `/mcp` or the whole address); refresh tokens are rotated on every use. Clients register with a Client ID Metadata Document (an https `client_id`) or Dynamic Client Registration (`/oauth/register`). Metadata: `/.well-known/oauth-authorization-server`.",
				"flows": {
					"authorizationCode": {
						"authorizationUrl": "https://app.quoterunway.com/oauth/authorize",
						"tokenUrl": "https://app.quoterunway.com/oauth/token",
						"refreshUrl": "https://app.quoterunway.com/oauth/token",
						"scopes": {
							"quotes:read": "List and read quotes, their lines, versions, PDFs, the customer's answer and the evidence",
							"quotes:write": "Create, change and delete drafts, lines and sections; recall; reopen expired quotes",
							"quotes:send": "Send a quote, send a reminder, mark it sent (mail goes through your own mail system)",
							"quotes:decide": "Record the customer's answer for them (won or lost)",
							"orders:write": "Push a quote to ConnectWise and process a won order",
							"catalog:read": "Saved items, product search, distributor price and stock",
							"catalog:write": "Add and change saved items",
							"templates:read": "Templates and document sections",
							"templates:write": "Add and change templates and document sections",
							"tasks:read": "Follow-ups",
							"tasks:write": "Add, change and remove follow-ups",
							"people:read": "People (never passwords or authenticators)",
							"people:write": "Add, change or turn off people (not admins), and send them a set-password link",
							"settings:read": "Company settings (never keys or secrets)",
							"audit:read": "The audit log",
							"webhooks:manage": "Webhook endpoints and their deliveries"
						}
					},
					"clientCredentials": {
						"tokenUrl": "https://app.quoterunway.com/oauth/token",
						"scopes": {
							"quotes:read": "List and read quotes, their lines, versions, PDFs, the customer's answer and the evidence",
							"quotes:write": "Create, change and delete drafts, lines and sections; recall; reopen expired quotes",
							"quotes:send": "Send a quote, send a reminder, mark it sent (mail goes through your own mail system)",
							"quotes:decide": "Record the customer's answer for them (won or lost)",
							"orders:write": "Push a quote to ConnectWise and process a won order",
							"catalog:read": "Saved items, product search, distributor price and stock",
							"catalog:write": "Add and change saved items",
							"templates:read": "Templates and document sections",
							"templates:write": "Add and change templates and document sections",
							"tasks:read": "Follow-ups",
							"tasks:write": "Add, change and remove follow-ups",
							"people:read": "People (never passwords or authenticators)",
							"people:write": "Add, change or turn off people (not admins), and send them a set-password link",
							"settings:read": "Company settings (never keys or secrets)",
							"audit:read": "The audit log",
							"webhooks:manage": "Webhook endpoints and their deliveries"
						}
					}
				},
				"x-scopes": [
					"quotes:read",
					"quotes:write",
					"quotes:send",
					"quotes:decide",
					"orders:write",
					"catalog:read",
					"catalog:write",
					"templates:read",
					"templates:write",
					"tasks:read",
					"tasks:write",
					"people:read",
					"people:write",
					"settings:read",
					"audit:read",
					"webhooks:manage"
				]
			}
		},
		"responses": {
			"RateLimited": {
				"description": "Over the rate limit (`rate_limited`)",
				"headers": {
					"Retry-After": {
						"schema": {
							"type": "integer"
						}
					},
					"RateLimit-Limit": {
						"schema": {
							"type": "integer"
						},
						"description": "Requests allowed in the current minute"
					},
					"RateLimit-Remaining": {
						"schema": {
							"type": "integer"
						}
					},
					"RateLimit-Reset": {
						"schema": {
							"type": "integer"
						},
						"description": "Seconds until the window resets"
					}
				},
				"content": {
					"application/problem+json": {
						"schema": {
							"$ref": "#/components/schemas/Problem"
						}
					}
				}
			}
		},
		"schemas": {
			"Acceptance": {
				"type": "object",
				"properties": {
					"version": {
						"type": "integer"
					},
					"outcome": {
						"type": "string",
						"enum": [
							"accepted",
							"declined"
						]
					},
					"name": {
						"type": "string"
					},
					"email": {
						"type": "string"
					},
					"title": {
						"type": "string"
					},
					"po": {
						"type": "string"
					},
					"comment": {
						"type": "string"
					},
					"chosenOptions": {
						"type": "array",
						"items": {
							"type": "string"
						}
					},
					"at": {
						"type": "string",
						"format": "date-time"
					},
					"ip": {
						"type": "string",
						"description": "Empty when recorded by a rep"
					},
					"recordedBy": {
						"type": "string",
						"description": "Set when a rep (or a key) recorded it, not the customer online"
					},
					"how": {
						"type": "string"
					},
					"reason": {
						"type": "string"
					},
					"evidenceHash": {
						"type": "string",
						"description": "The record's SHA-256, as in the evidence ledger"
					},
					"intact": {
						"type": "boolean",
						"description": "The record still matches its SHA-256"
					}
				}
			},
			"Address": {
				"type": "object",
				"properties": {
					"line1": {
						"type": "string"
					},
					"line2": {
						"type": "string"
					},
					"city": {
						"type": "string"
					},
					"state": {
						"type": "string"
					},
					"zip": {
						"type": "string"
					}
				}
			},
			"AuditEntry": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string"
					},
					"at": {
						"type": "string",
						"format": "date-time"
					},
					"actor": {
						"type": "object",
						"properties": {
							"kind": {
								"type": "string",
								"enum": [
									"person",
									"apiKey",
									"system"
								]
							},
							"id": {
								"type": "string"
							},
							"name": {
								"type": "string"
							},
							"email": {
								"type": "string"
							},
							"keyId": {
								"type": "string"
							}
						}
					},
					"action": {
						"type": "string",
						"example": "quote.send"
					},
					"label": {
						"type": "string",
						"example": "Sent a quote"
					},
					"target": {
						"type": "string",
						"description": "What it was done to, in words"
					},
					"targetType": {
						"type": "string"
					},
					"targetId": {
						"type": "string"
					},
					"fields": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "The names of the fields sent (never their values)"
					},
					"ip": {
						"type": "string"
					},
					"ok": {
						"type": "boolean"
					},
					"status": {
						"type": [
							"integer",
							"null"
						]
					}
				}
			},
			"Contact": {
				"type": "object",
				"properties": {
					"psaId": {
						"type": "string",
						"description": "The ConnectWise contact id"
					},
					"name": {
						"type": "string",
						"example": "Dana Ortiz"
					},
					"email": {
						"type": "string",
						"format": "email",
						"example": "dana@northwind.example"
					},
					"phone": {
						"type": "string"
					},
					"title": {
						"type": "string",
						"readOnly": true
					},
					"primary": {
						"type": "boolean",
						"readOnly": true
					}
				}
			},
			"Customer": {
				"type": "object",
				"description": "The customer, from ConnectWise. Creating a quote needs `psaId` and `name`.",
				"properties": {
					"psaId": {
						"type": "string",
						"description": "The ConnectWise company id",
						"example": "19297"
					},
					"name": {
						"type": "string",
						"example": "Northwind Dental"
					},
					"siteId": {
						"type": "string",
						"description": "The ConnectWise site the quote is for (its address is printed)"
					},
					"site": {
						"type": "string",
						"example": "Main office"
					}
				}
			},
			"Decision": {
				"type": "object",
				"required": [
					"outcome"
				],
				"properties": {
					"outcome": {
						"type": "string",
						"enum": [
							"won",
							"lost"
						]
					},
					"how": {
						"type": "string",
						"enum": [
							"phone",
							"email",
							"po",
							"in-person"
						],
						"description": "Won: how they said yes (required)"
					},
					"reason": {
						"type": "string",
						"enum": [
							"price",
							"competitor",
							"timing",
							"no-response",
							"changed-plans",
							"other"
						],
						"description": "Lost: why (required)"
					},
					"competitor": {
						"type": "string"
					},
					"name": {
						"type": "string",
						"description": "Who said yes (default: the quote's contact)"
					},
					"email": {
						"type": "string",
						"format": "email"
					},
					"title": {
						"type": "string"
					},
					"po": {
						"type": "string",
						"description": "Their PO number (required when how is po)"
					},
					"comment": {
						"type": "string"
					}
				}
			},
			"Delivery": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string"
					},
					"event": {
						"type": "string"
					},
					"at": {
						"type": "string",
						"format": "date-time"
					},
					"status": {
						"type": "string",
						"enum": [
							"ok",
							"failed",
							"pending"
						]
					},
					"responseCode": {
						"type": [
							"integer",
							"null"
						]
					},
					"responseBody": {
						"type": "string",
						"description": "The first 1 KB"
					},
					"attempt": {
						"type": "integer"
					}
				}
			},
			"Document": {
				"type": "object",
				"required": [
					"title"
				],
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"title": {
						"type": "string"
					},
					"body": {
						"type": "string",
						"description": "The text (paragraphs, # headings, - bullets)"
					},
					"where": {
						"type": "string",
						"enum": [
							"before",
							"after"
						],
						"description": "Before or after the pricing"
					},
					"kind": {
						"type": "string",
						"enum": [
							"text",
							"agreement"
						],
						"description": "agreement: signed with the quote"
					},
					"pageBreak": {
						"type": "boolean"
					},
					"file": {
						"type": [
							"object",
							"null"
						],
						"readOnly": true,
						"description": "An uploaded PDF used in place of the text (upload in the app)",
						"properties": {
							"name": {
								"type": "string"
							},
							"pages": {
								"type": "integer"
							}
						}
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				}
			},
			"DocumentPatch": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"title": {
						"type": "string"
					},
					"body": {
						"type": "string",
						"description": "The text (paragraphs, # headings, - bullets)"
					},
					"where": {
						"type": "string",
						"enum": [
							"before",
							"after"
						],
						"description": "Before or after the pricing"
					},
					"kind": {
						"type": "string",
						"enum": [
							"text",
							"agreement"
						],
						"description": "agreement: signed with the quote"
					},
					"pageBreak": {
						"type": "boolean"
					},
					"file": {
						"type": [
							"object",
							"null"
						],
						"readOnly": true,
						"description": "An uploaded PDF used in place of the text (upload in the app)",
						"properties": {
							"name": {
								"type": "string"
							},
							"pages": {
								"type": "integer"
							}
						}
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				},
				"description": "Document fields to change: only the ones sent change"
			},
			"Event": {
				"type": "object",
				"required": [
					"id",
					"type",
					"createdAt",
					"data"
				],
				"properties": {
					"id": {
						"type": "string",
						"description": "Unique; use it to drop duplicates"
					},
					"type": {
						"type": "string",
						"example": "quote.accepted"
					},
					"createdAt": {
						"type": "string",
						"format": "date-time"
					},
					"data": {
						"type": "object",
						"properties": {
							"object": {
								"type": "object",
								"description": "The quote, task or person"
							}
						}
					}
				}
			},
			"Item": {
				"type": "object",
				"required": [
					"name"
				],
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"name": {
						"type": "string"
					},
					"kind": {
						"type": "string",
						"enum": [
							"product",
							"warranty",
							"stock",
							"labor",
							"license",
							"fee"
						],
						"default": "product"
					},
					"mpn": {
						"type": "string"
					},
					"manufacturer": {
						"type": "string"
					},
					"description": {
						"type": "string"
					},
					"cost": {
						"$ref": "#/components/schemas/Money"
					},
					"price": {
						"$ref": "#/components/schemas/Money"
					},
					"taxable": {
						"type": [
							"boolean",
							"null"
						]
					},
					"recurring": {
						"type": "string",
						"enum": [
							"none",
							"monthly",
							"annual"
						]
					},
					"term": {
						"type": "string"
					},
					"active": {
						"type": "boolean"
					},
					"fromPsa": {
						"type": "boolean",
						"readOnly": true,
						"description": "Kept in step with the ConnectWise catalog: its cost, price and part number are changed there"
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				}
			},
			"ItemPatch": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"name": {
						"type": "string"
					},
					"kind": {
						"type": "string",
						"enum": [
							"product",
							"warranty",
							"stock",
							"labor",
							"license",
							"fee"
						],
						"default": "product"
					},
					"mpn": {
						"type": "string"
					},
					"manufacturer": {
						"type": "string"
					},
					"description": {
						"type": "string"
					},
					"cost": {
						"$ref": "#/components/schemas/Money"
					},
					"price": {
						"$ref": "#/components/schemas/Money"
					},
					"taxable": {
						"type": [
							"boolean",
							"null"
						]
					},
					"recurring": {
						"type": "string",
						"enum": [
							"none",
							"monthly",
							"annual"
						]
					},
					"term": {
						"type": "string"
					},
					"active": {
						"type": "boolean"
					},
					"fromPsa": {
						"type": "boolean",
						"readOnly": true,
						"description": "Kept in step with the ConnectWise catalog: its cost, price and part number are changed there"
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				},
				"description": "Item fields to change: only the ones sent change"
			},
			"LedgerEntry": {
				"type": "object",
				"properties": {
					"seq": {
						"type": "integer"
					},
					"at": {
						"type": "string",
						"format": "date-time"
					},
					"kind": {
						"type": "string"
					},
					"decision": {
						"type": "string"
					},
					"version": {
						"type": "integer"
					},
					"recordHash": {
						"type": "string"
					},
					"pdfHash": {
						"type": "string"
					},
					"certificateHash": {
						"type": [
							"string",
							"null"
						]
					},
					"prev": {
						"type": "string"
					},
					"hash": {
						"type": "string"
					}
				}
			},
			"Line": {
				"type": "object",
				"required": [
					"kind",
					"description",
					"qty",
					"price"
				],
				"properties": {
					"id": {
						"type": "string",
						"description": "Yours to choose when adding (up to 20 characters), or made for you"
					},
					"sectionId": {
						"type": [
							"string",
							"null"
						],
						"description": "The section it's in; null for none"
					},
					"kind": {
						"type": "string",
						"enum": [
							"product",
							"warranty",
							"stock",
							"labor",
							"license",
							"fee",
							"note"
						],
						"default": "product"
					},
					"mpn": {
						"type": "string",
						"example": "20VE0057US"
					},
					"manufacturer": {
						"type": "string",
						"example": "Lenovo"
					},
					"description": {
						"type": "string",
						"example": "ThinkPad T14 Gen 5, 16 GB, 512 GB SSD"
					},
					"details": {
						"type": "string",
						"description": "A few spec lines under the description"
					},
					"qty": {
						"type": "number",
						"minimum": 0,
						"example": 5
					},
					"cost": {
						"$ref": "#/components/schemas/Money"
					},
					"price": {
						"$ref": "#/components/schemas/Money",
						"description": "Unit sell price"
					},
					"taxable": {
						"type": [
							"boolean",
							"null"
						],
						"description": "null: the default (taxed unless labor)"
					},
					"recurring": {
						"type": "string",
						"enum": [
							"none",
							"monthly",
							"annual"
						],
						"default": "none"
					},
					"term": {
						"type": "string",
						"description": "For licences: \"1 month\", \"1 year\", \"3 years\""
					},
					"optional": {
						"type": "boolean"
					},
					"preselected": {
						"type": "boolean"
					},
					"source": {
						"type": "string",
						"description": "Where the cost came from: a distributor (`ingram`, `tdsynnex`, `dh`…), `cw` or `manual`",
						"example": "ingram"
					},
					"sourceSku": {
						"type": "string",
						"description": "The source's own part number"
					}
				}
			},
			"LinePatch": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"description": "Yours to choose when adding (up to 20 characters), or made for you"
					},
					"sectionId": {
						"type": [
							"string",
							"null"
						],
						"description": "The section it's in; null for none"
					},
					"kind": {
						"type": "string",
						"enum": [
							"product",
							"warranty",
							"stock",
							"labor",
							"license",
							"fee",
							"note"
						],
						"default": "product"
					},
					"mpn": {
						"type": "string",
						"example": "20VE0057US"
					},
					"manufacturer": {
						"type": "string",
						"example": "Lenovo"
					},
					"description": {
						"type": "string",
						"example": "ThinkPad T14 Gen 5, 16 GB, 512 GB SSD"
					},
					"details": {
						"type": "string",
						"description": "A few spec lines under the description"
					},
					"qty": {
						"type": "number",
						"minimum": 0,
						"example": 5
					},
					"cost": {
						"$ref": "#/components/schemas/Money"
					},
					"price": {
						"$ref": "#/components/schemas/Money",
						"description": "Unit sell price"
					},
					"taxable": {
						"type": [
							"boolean",
							"null"
						],
						"description": "null: the default (taxed unless labor)"
					},
					"recurring": {
						"type": "string",
						"enum": [
							"none",
							"monthly",
							"annual"
						],
						"default": "none"
					},
					"term": {
						"type": "string",
						"description": "For licences: \"1 month\", \"1 year\", \"3 years\""
					},
					"optional": {
						"type": "boolean"
					},
					"preselected": {
						"type": "boolean"
					},
					"source": {
						"type": "string",
						"description": "Where the cost came from: a distributor (`ingram`, `tdsynnex`, `dh`…), `cw` or `manual`",
						"example": "ingram"
					},
					"sourceSku": {
						"type": "string",
						"description": "The source's own part number"
					}
				},
				"description": "Line fields to change: only the ones sent change"
			},
			"Me": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"description": "The key's id (the <id> in qr_live_<id>_…)"
					},
					"name": {
						"type": "string",
						"example": "Accounting sync"
					},
					"last4": {
						"type": "string"
					},
					"scopes": {
						"type": "array",
						"items": {
							"type": "string"
						}
					},
					"company": {
						"type": "string"
					},
					"plan": {
						"type": "string"
					},
					"expiresAt": {
						"type": "string",
						"format": "date-time"
					},
					"rateLimit": {
						"type": "object",
						"properties": {
							"perMinute": {
								"type": "integer"
							},
							"companyPerMinute": {
								"type": "integer"
							}
						}
					}
				}
			},
			"Money": {
				"type": "number",
				"description": "In the company's currency, rounded to cents",
				"example": 1249.5
			},
			"Offer": {
				"type": "object",
				"properties": {
					"distributor": {
						"type": "string",
						"example": "ingram"
					},
					"sku": {
						"type": "string"
					},
					"mpn": {
						"type": "string"
					},
					"description": {
						"type": "string"
					},
					"cost": {
						"type": [
							"number",
							"null"
						]
					},
					"msrp": {
						"type": [
							"number",
							"null"
						]
					},
					"stock": {
						"type": "integer"
					},
					"eta": {
						"type": [
							"string",
							"null"
						]
					},
					"warehouses": {
						"type": "array",
						"items": {
							"type": "object",
							"properties": {
								"name": {
									"type": "string"
								},
								"qty": {
									"type": "integer"
								}
							}
						}
					},
					"at": {
						"type": "string",
						"format": "date-time"
					}
				}
			},
			"Person": {
				"type": "object",
				"required": [
					"name",
					"email"
				],
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"name": {
						"type": "string"
					},
					"email": {
						"type": "string",
						"format": "email"
					},
					"role": {
						"type": "string",
						"enum": [
							"admin",
							"sales",
							"tech"
						],
						"description": "Admins can't be made, changed or reset over the API"
					},
					"active": {
						"type": "boolean"
					},
					"title": {
						"type": "string"
					},
					"phone": {
						"type": "string"
					},
					"authenticator": {
						"type": "boolean",
						"readOnly": true
					},
					"lastSignInAt": {
						"type": [
							"string",
							"null"
						],
						"format": "date-time",
						"readOnly": true
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				}
			},
			"PersonPatch": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"name": {
						"type": "string"
					},
					"email": {
						"type": "string",
						"format": "email"
					},
					"role": {
						"type": "string",
						"enum": [
							"admin",
							"sales",
							"tech"
						],
						"description": "Admins can't be made, changed or reset over the API"
					},
					"active": {
						"type": "boolean"
					},
					"title": {
						"type": "string"
					},
					"phone": {
						"type": "string"
					},
					"authenticator": {
						"type": "boolean",
						"readOnly": true
					},
					"lastSignInAt": {
						"type": [
							"string",
							"null"
						],
						"format": "date-time",
						"readOnly": true
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				},
				"description": "Person fields to change: only the ones sent change"
			},
			"PersonRef": {
				"type": "object",
				"description": "A person, or an API key acting as one (`kind: apiKey`, name \"API: <key name>\")",
				"properties": {
					"id": {
						"type": "string"
					},
					"name": {
						"type": "string"
					},
					"email": {
						"type": "string"
					},
					"kind": {
						"type": "string",
						"enum": [
							"person",
							"apiKey",
							"system"
						]
					}
				}
			},
			"Problem": {
				"type": "object",
				"description": "RFC 9457 problem details. `code` is stable: never renamed or given a new meaning within v1 (new codes may be added: treat an unknown one by its HTTP status). The codes:\n\n- `bad_request` (400: the request can't be done as sent (a business rule refused it, or it's malformed))\n- `validation_failed` (422: fields are missing or the wrong type; `errors` lists them)\n- `unauthorized` (401: no key, an unknown key, a revoked one, or a key for another company's address)\n- `key_expired` (401: the key is past its expiry)\n- `scope_missing` (403: the key lacks the scope this needs)\n- `ip_not_allowed` (403: the key's IP allowlist doesn't include the caller)\n- `plan_required` (403: the company's plan doesn't include the API (Enterprise); keys are kept and work again when it does)\n- `forbidden` (403: not allowed for this key (for example changing an admin))\n- `account_locked` (402: the company's subscription needs a payment; an admin can update it in the app)\n- `not_found` (404: no such object (or not this company's))\n- `conflict` (409: it changed just now, or clashes with something that exists)\n- `quote_locked` (409: someone is editing the quote in the app right now)\n- `quote_not_editable` (409: the quote's state doesn't allow this (answered, already a draft, not sent…))\n- `quote_restored` (409: someone restored an earlier revision in the app; changing or sending it would delete the later revisions, so it's finished in the app)\n- `mail_not_connected` (409: the company's mail isn't connected, or quotes go out from Outlook there)\n- `psa_not_connected` (409: ConnectWise isn't connected for this company)\n- `idempotency_conflict` (409: the Idempotency-Key was used with a different request)\n- `idempotency_in_progress` (409: the first request with this Idempotency-Key is still running)\n- `precondition_failed` (412: If-Match didn't match: it changed since you read it)\n- `payload_too_large` (413: the body is over 2 MB)\n- `rate_limited` (429: over the rate limit; wait `Retry-After` seconds)\n- `internal` (500: our fault; quote the requestId to support)\n- `unavailable` (503: maintenance (a restore) or a dependency is down; retry after `Retry-After`)",
				"x-error-codes": [
					"bad_request",
					"validation_failed",
					"unauthorized",
					"key_expired",
					"scope_missing",
					"ip_not_allowed",
					"plan_required",
					"forbidden",
					"account_locked",
					"not_found",
					"conflict",
					"quote_locked",
					"quote_not_editable",
					"quote_restored",
					"mail_not_connected",
					"psa_not_connected",
					"idempotency_conflict",
					"idempotency_in_progress",
					"precondition_failed",
					"payload_too_large",
					"rate_limited",
					"internal",
					"unavailable"
				],
				"required": [
					"type",
					"title",
					"status",
					"code"
				],
				"properties": {
					"type": {
						"type": "string",
						"format": "uri"
					},
					"title": {
						"type": "string"
					},
					"status": {
						"type": "integer"
					},
					"detail": {
						"type": "string"
					},
					"code": {
						"type": "string"
					},
					"requestId": {
						"type": "string"
					},
					"errors": {
						"type": "array",
						"items": {
							"type": "object",
							"properties": {
								"field": {
									"type": "string"
								},
								"message": {
									"type": "string"
								}
							}
						}
					}
				},
				"example": {
					"type": "https://quoterunway.com/docs/api/errors#validation_failed",
					"title": "validation failed",
					"status": 422,
					"detail": "Some fields need fixing",
					"code": "validation_failed",
					"requestId": "q7Hc2kLm",
					"errors": [
						{
							"field": "lines[0].qty",
							"message": "must be >= 0"
						}
					]
				}
			},
			"Product": {
				"type": "object",
				"properties": {
					"mpn": {
						"type": "string"
					},
					"manufacturer": {
						"type": "string"
					},
					"description": {
						"type": "string"
					},
					"image": {
						"type": "string",
						"format": "uri"
					},
					"upc": {
						"type": "string"
					},
					"distributor": {
						"type": "string"
					},
					"sku": {
						"type": "string"
					}
				}
			},
			"PsaCustomer": {
				"type": "object",
				"properties": {
					"psaId": {
						"type": "string"
					},
					"name": {
						"type": "string"
					},
					"identifier": {
						"type": "string"
					},
					"place": {
						"type": "string",
						"description": "City, state"
					},
					"address": {
						"$ref": "#/components/schemas/Address"
					}
				}
			},
			"Quote": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"number": {
						"type": "integer",
						"readOnly": true,
						"example": 10042
					},
					"title": {
						"type": "string",
						"example": "Laptop refresh, front office"
					},
					"status": {
						"type": "string",
						"readOnly": true,
						"enum": [
							"draft",
							"sent",
							"accepted",
							"declined",
							"expired"
						],
						"description": "accepted is won and declined is lost (by the customer online, or recorded by the rep). A recalled quote is a draft again. New values may be added."
					},
					"customer": {
						"$ref": "#/components/schemas/Customer"
					},
					"contact": {
						"$ref": "#/components/schemas/Contact"
					},
					"salesTicket": {
						"$ref": "#/components/schemas/SalesTicket"
					},
					"owner": {
						"$ref": "#/components/schemas/PersonRef"
					},
					"updatedBy": {
						"$ref": "#/components/schemas/PersonRef",
						"readOnly": true
					},
					"sections": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Section"
						}
					},
					"lines": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Line"
						}
					},
					"totals": {
						"$ref": "#/components/schemas/Totals"
					},
					"notes": {
						"type": "string",
						"description": "The cover letter: what the email and PDF say to the customer (needed to send)"
					},
					"internalNotes": {
						"type": "string",
						"description": "For your team; never shown to the customer"
					},
					"taxRate": {
						"type": "number",
						"minimum": 0,
						"maximum": 20,
						"description": "Percent"
					},
					"shipping": {
						"$ref": "#/components/schemas/Money"
					},
					"templateId": {
						"type": [
							"string",
							"null"
						]
					},
					"validUntil": {
						"type": "string",
						"format": "date",
						"description": "A day, YYYY-MM-DD"
					},
					"sentVersion": {
						"type": [
							"integer",
							"null"
						],
						"readOnly": true,
						"description": "The revision the customer has now"
					},
					"acceptedVersion": {
						"type": [
							"integer",
							"null"
						],
						"readOnly": true
					},
					"version": {
						"type": "integer",
						"readOnly": true,
						"description": "Changes whenever the quote is changed; send it (or the ETag) as If-Match"
					},
					"createdAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					},
					"links": {
						"type": "object",
						"readOnly": true,
						"properties": {
							"app": {
								"type": "string",
								"format": "uri",
								"description": "The quote in QuoteRunway, for people"
							},
							"pdf": {
								"type": "string",
								"format": "uri",
								"description": "GET it with your key"
							}
						}
					}
				}
			},
			"QuoteCreate": {
				"type": "object",
				"required": [
					"customer",
					"salesTicket"
				],
				"properties": {
					"title": {
						"type": "string"
					},
					"customer": {
						"$ref": "#/components/schemas/Customer"
					},
					"contact": {
						"$ref": "#/components/schemas/Contact"
					},
					"salesTicket": {
						"$ref": "#/components/schemas/SalesTicket"
					},
					"templateId": {
						"type": "string",
						"description": "Start from this template (its sections, lines and cover letter, when you send no lines)"
					},
					"ownerId": {
						"type": "string",
						"description": "The person it belongs to (default: this key). Sending needs a person."
					},
					"validUntil": {
						"type": "string",
						"format": "date",
						"description": "A day, YYYY-MM-DD"
					},
					"notes": {
						"type": "string"
					},
					"internalNotes": {
						"type": "string"
					},
					"taxRate": {
						"type": "number",
						"minimum": 0,
						"maximum": 20
					},
					"shipping": {
						"$ref": "#/components/schemas/Money"
					},
					"sections": {
						"type": "array",
						"maxItems": 100,
						"items": {
							"$ref": "#/components/schemas/Section"
						}
					},
					"lines": {
						"type": "array",
						"maxItems": 500,
						"items": {
							"$ref": "#/components/schemas/Line"
						}
					}
				}
			},
			"QuotePatch": {
				"type": "object",
				"properties": {
					"title": {
						"type": "string"
					},
					"customer": {
						"$ref": "#/components/schemas/Customer"
					},
					"contact": {
						"$ref": "#/components/schemas/Contact"
					},
					"salesTicket": {
						"$ref": "#/components/schemas/SalesTicket"
					},
					"ownerId": {
						"type": "string"
					},
					"validUntil": {
						"type": "string",
						"format": "date",
						"description": "A day, YYYY-MM-DD"
					},
					"notes": {
						"type": "string"
					},
					"internalNotes": {
						"type": "string"
					},
					"taxRate": {
						"type": "number",
						"minimum": 0,
						"maximum": 20
					},
					"shipping": {
						"$ref": "#/components/schemas/Money"
					},
					"templateId": {
						"type": "string"
					},
					"sections": {
						"type": "array",
						"maxItems": 100,
						"items": {
							"$ref": "#/components/schemas/Section"
						},
						"description": "Replaces the sections; lines in a section that's gone move out of it"
					}
				}
			},
			"SalesTicket": {
				"type": "object",
				"description": "The ConnectWise sales ticket the quote is for (its activity is written there). It must be the customer's.",
				"properties": {
					"psaId": {
						"type": "string",
						"example": "566015"
					},
					"summary": {
						"type": "string"
					}
				}
			},
			"Section": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"description": "Yours to choose when adding (up to 20 characters), or made for you"
					},
					"title": {
						"type": "string",
						"example": "Hardware"
					},
					"qty": {
						"type": "integer",
						"minimum": 1,
						"description": "The section is quoted this many times"
					},
					"optional": {
						"type": "boolean",
						"description": "The customer chooses whether it's in"
					},
					"preselected": {
						"type": "boolean",
						"description": "Optional, and in unless the customer takes it out"
					},
					"pickOne": {
						"type": "boolean",
						"description": "The customer picks one of its lines"
					},
					"choice": {
						"type": "string",
						"description": "Sections with the same choice name are alternatives"
					}
				}
			},
			"Send": {
				"type": "object",
				"properties": {
					"to": {
						"type": "array",
						"maxItems": 20,
						"items": {
							"type": "string",
							"format": "email"
						},
						"description": "Default: the quote's contact"
					},
					"cc": {
						"type": "array",
						"maxItems": 20,
						"items": {
							"type": "string",
							"format": "email"
						}
					}
				}
			},
			"Settings": {
				"type": "object",
				"description": "Company settings, never keys or secrets",
				"properties": {
					"company": {
						"type": "object",
						"properties": {
							"name": {
								"type": "string"
							},
							"address": {
								"type": "string"
							},
							"phone": {
								"type": "string"
							},
							"website": {
								"type": "string"
							}
						}
					},
					"currency": {
						"type": "string",
						"example": "USD"
					},
					"timeZone": {
						"type": "string",
						"example": "America/New_York"
					},
					"pdfStyle": {
						"type": "string"
					},
					"plan": {
						"type": "string"
					},
					"mail": {
						"type": "object",
						"properties": {
							"ready": {
								"type": "boolean"
							},
							"mode": {
								"type": "string"
							},
							"directSend": {
								"type": "boolean",
								"description": "Quotes can be emailed by QuoteRunway (needed for POST /quotes/{id}/send)"
							}
						}
					},
					"psa": {
						"type": "object",
						"properties": {
							"kind": {
								"type": "string",
								"example": "connectwise"
							},
							"connected": {
								"type": "boolean"
							}
						}
					}
				}
			},
			"Site": {
				"type": "object",
				"properties": {
					"psaId": {
						"type": "string"
					},
					"name": {
						"type": "string"
					},
					"main": {
						"type": "boolean"
					},
					"address": {
						"$ref": "#/components/schemas/Address"
					}
				}
			},
			"Task": {
				"type": "object",
				"required": [
					"title"
				],
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"title": {
						"type": "string"
					},
					"notes": {
						"type": "string"
					},
					"kind": {
						"type": "string",
						"enum": [
							"call",
							"email",
							"meeting",
							"todo"
						]
					},
					"quoteId": {
						"type": [
							"string",
							"null"
						]
					},
					"due": {
						"type": [
							"string",
							"null"
						],
						"format": "date-time"
					},
					"assigneeId": {
						"type": "string",
						"description": "The person it's for (default: the quote's owner)"
					},
					"done": {
						"type": "boolean"
					},
					"outcome": {
						"type": "string"
					},
					"auto": {
						"type": "boolean",
						"readOnly": true,
						"description": "Made by QuoteRunway from what the customer did"
					},
					"doneAt": {
						"type": [
							"string",
							"null"
						],
						"format": "date-time",
						"readOnly": true
					},
					"createdAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				}
			},
			"TaskPatch": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"title": {
						"type": "string"
					},
					"notes": {
						"type": "string"
					},
					"kind": {
						"type": "string",
						"enum": [
							"call",
							"email",
							"meeting",
							"todo"
						]
					},
					"quoteId": {
						"type": [
							"string",
							"null"
						]
					},
					"due": {
						"type": [
							"string",
							"null"
						],
						"format": "date-time"
					},
					"assigneeId": {
						"type": "string",
						"description": "The person it's for (default: the quote's owner)"
					},
					"done": {
						"type": "boolean"
					},
					"outcome": {
						"type": "string"
					},
					"auto": {
						"type": "boolean",
						"readOnly": true,
						"description": "Made by QuoteRunway from what the customer did"
					},
					"doneAt": {
						"type": [
							"string",
							"null"
						],
						"format": "date-time",
						"readOnly": true
					},
					"createdAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				},
				"description": "Task fields to change: only the ones sent change"
			},
			"Template": {
				"type": "object",
				"required": [
					"name"
				],
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"name": {
						"type": "string"
					},
					"description": {
						"type": "string"
					},
					"sections": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Section"
						}
					},
					"lines": {
						"type": "array",
						"maxItems": 500,
						"items": {
							"$ref": "#/components/schemas/Line"
						}
					},
					"notes": {
						"type": "string",
						"description": "The cover letter new quotes start with"
					},
					"terms": {
						"type": "string",
						"description": "Printed on its quotes; empty: the standard terms"
					},
					"term": {
						"type": "string",
						"description": "The term its recurring lines run for"
					},
					"documentIds": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Document sections its quotes carry, in order"
					},
					"isDefault": {
						"type": "boolean"
					},
					"active": {
						"type": "boolean"
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				}
			},
			"TemplatePatch": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"name": {
						"type": "string"
					},
					"description": {
						"type": "string"
					},
					"sections": {
						"type": "array",
						"items": {
							"$ref": "#/components/schemas/Section"
						}
					},
					"lines": {
						"type": "array",
						"maxItems": 500,
						"items": {
							"$ref": "#/components/schemas/Line"
						}
					},
					"notes": {
						"type": "string",
						"description": "The cover letter new quotes start with"
					},
					"terms": {
						"type": "string",
						"description": "Printed on its quotes; empty: the standard terms"
					},
					"term": {
						"type": "string",
						"description": "The term its recurring lines run for"
					},
					"documentIds": {
						"type": "array",
						"items": {
							"type": "string"
						},
						"description": "Document sections its quotes carry, in order"
					},
					"isDefault": {
						"type": "boolean"
					},
					"active": {
						"type": "boolean"
					},
					"updatedAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				},
				"description": "Template fields to change: only the ones sent change"
			},
			"Totals": {
				"type": "object",
				"readOnly": true,
				"properties": {
					"oneTime": {
						"$ref": "#/components/schemas/Money",
						"description": "One-time lines, before tax and shipping"
					},
					"monthly": {
						"$ref": "#/components/schemas/Money",
						"description": "Billed every month"
					},
					"annual": {
						"$ref": "#/components/schemas/Money",
						"description": "Billed every year"
					},
					"tax": {
						"$ref": "#/components/schemas/Money"
					},
					"shipping": {
						"$ref": "#/components/schemas/Money"
					},
					"total": {
						"$ref": "#/components/schemas/Money",
						"description": "One-time + tax + shipping"
					}
				}
			},
			"Version": {
				"type": "object",
				"properties": {
					"n": {
						"type": "integer"
					},
					"sentAt": {
						"type": "string",
						"format": "date-time"
					},
					"sentTo": {
						"type": "array",
						"items": {
							"type": "string"
						}
					},
					"current": {
						"type": "boolean",
						"description": "The revision the customer's link opens now"
					},
					"recalled": {
						"type": "boolean"
					},
					"totals": {
						"$ref": "#/components/schemas/Totals"
					},
					"links": {
						"type": "object",
						"properties": {
							"pdf": {
								"type": "string",
								"format": "uri"
							}
						}
					}
				}
			},
			"Webhook": {
				"type": "object",
				"required": [
					"url"
				],
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"name": {
						"type": "string"
					},
					"kind": {
						"type": "string",
						"enum": [
							"custom",
							"slack",
							"teams",
							"gchat"
						],
						"default": "custom",
						"description": "custom: signed JSON events to your endpoint; the others post readable messages to a chat channel"
					},
					"url": {
						"type": "string",
						"format": "uri",
						"description": "HTTPS, public address only (a chat address shows only its host once saved)"
					},
					"events": {
						"type": "array",
						"items": {
							"type": "string",
							"enum": [
								"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"
							]
						}
					},
					"active": {
						"type": "boolean"
					},
					"rotateSecret": {
						"type": "boolean",
						"writeOnly": true,
						"description": "PATCH: make a new signing secret (returned once in `secret`)"
					},
					"failing": {
						"type": "integer",
						"readOnly": true,
						"description": "Deliveries in a row that gave up"
					},
					"secret": {
						"type": "string",
						"readOnly": true,
						"description": "Only returned when created or rotated"
					},
					"createdAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				}
			},
			"WebhookPatch": {
				"type": "object",
				"properties": {
					"id": {
						"type": "string",
						"readOnly": true
					},
					"name": {
						"type": "string"
					},
					"kind": {
						"type": "string",
						"enum": [
							"custom",
							"slack",
							"teams",
							"gchat"
						],
						"default": "custom",
						"description": "custom: signed JSON events to your endpoint; the others post readable messages to a chat channel"
					},
					"url": {
						"type": "string",
						"format": "uri",
						"description": "HTTPS, public address only (a chat address shows only its host once saved)"
					},
					"events": {
						"type": "array",
						"items": {
							"type": "string",
							"enum": [
								"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"
							]
						}
					},
					"active": {
						"type": "boolean"
					},
					"rotateSecret": {
						"type": "boolean",
						"writeOnly": true,
						"description": "PATCH: make a new signing secret (returned once in `secret`)"
					},
					"failing": {
						"type": "integer",
						"readOnly": true,
						"description": "Deliveries in a row that gave up"
					},
					"secret": {
						"type": "string",
						"readOnly": true,
						"description": "Only returned when created or rotated"
					},
					"createdAt": {
						"type": "string",
						"format": "date-time",
						"readOnly": true
					}
				},
				"description": "Webhook fields to change: only the ones sent change"
			}
		}
	}
}
