# Contact support
Source: https://docs.qwoty.io/contact-support
Get in touch with the Qwoty support team — by chat, ticket, or email.
## How would you like to reach us?
Get instant answers from our team — anytime.
Submit a detailed request and track its progress.
Drop us a line at [support@qwoty.io](mailto:support@qwoty.io) for non-urgent questions.
***
## Chat
Our live chat is the fastest way to reach us. Click the bubble in the bottom-right corner of the screen, or use the button below to open it directly.
**Available 24/7.** Whenever you need us, we're here.
Screenshots and screen recordings are the best way to get your issue solved quickly. Drop them right into the chat to help us understand what's happening.
***
## Ticket
For complex issues that require investigation — bugs, account problems, billing questions — open a ticket. You'll get a confirmation email and can track the progress from your inbox.
**Screenshots and screen recordings are the best way to get your issue solved quickly.** Attach them to your ticket along with:
* The browser and OS you're using
* Steps to reproduce the issue
* The browser and OS you're using
* Steps to reproduce the issue
***
## Email
For non-urgent questions, partnership inquiries, or any feedback you'd like to share, email us directly.
We typically reply within one business day.
For **security disclosures**, please use [security@qwoty.io](mailto:security@qwoty.io) instead. See our [security policy](/developers/policies/security) for details.
# Key CPQ concepts
Source: https://docs.qwoty.io/getting-started/cpq-key-concepts
Learn the core CPQ concepts you'll meet throughout Qwoty — from catalogs to quotes to contracts.
CPQ stands for Configure, Price, Quote. It describes the process of assembling the right products, applying the right pricing, and generating a proposal — accurately and quickly.
In Qwoty, CPQ is the foundation of a longer flow that continues through e-signature, order generation, and buyer collaboration. Understanding the core concepts makes every part of the platform easier to use.
## The quote-to-revenue flow
```mermaid theme={null}
graph LR
A[Template] --> B[Quote]
B --> C[Approval]
C --> D[Publish]
D --> E[Dealroom]
E --> F[Signature]
F --> G[Order]
G --> H[Contract]
```
A Template defines the starting configuration for a Quote. The Quote goes through approval if required, then gets published to a Dealroom where the buyer reviews and signs. Signature triggers automatic Order generation. The Order is grouped under a Contract alongside any related quotes.
## Core concepts
### Catalog
A Catalog is a group of products available to sell. You create separate catalogs to reflect different markets, regions, or business units — for example, "France Catalog" or "Enterprise Software."
Catalogs define *what* your reps can sell. Pricing is handled separately in a Pricebook.
### Product and variant
A Product is an item, service, or subscription in your catalog. Variants let you define different configurations of the same product — such as different sizes, durations, or tiers — without creating separate products for each.
### Bundle
A Bundle is a pack of products sold together as a single line item. Bundles simplify quoting for common product combinations and can carry bundle-level pricing or discounts.
### Pricebook
A Pricebook is a price list applied to a catalog. You can maintain multiple pricebooks for the same catalog — for example, one per customer segment, region, or channel.
A Quote uses exactly one Pricebook. Products not listed in that Pricebook won't appear when building the Quote.
### Template
A Template is a funnel configuration that combines a Catalog, a Pricebook, payment terms, and a Contract Model into a reusable starting point. Reps select a Template when creating a Quote — it sets the rules for the entire deal.
Templates are the primary tool for standardizing your sales process across products, regions, or customer types.
### Quote
A Quote is a priced proposal sent to a customer. It lists selected products and variants, applies the Pricebook, and carries the payment terms and contract model from its Template.
Quotes move through a defined set of statuses from `Draft` to `Accepted`. See [Quote statuses](#quote-statuses) below.
### Approval workflow
An Approval Workflow is a set of rules that determine when a Quote needs manager validation before it can be published. Rules can trigger on discount level, deal size, product category, or custom conditions.
Approval workflows protect margin and ensure commercial policy is enforced consistently.
### Dealroom
The Dealroom is the client-facing space where buyers view the quote, ask questions, and sign. It replaces static PDF attachments with a live, interactive environment.
Every comment, view, and signature is recorded and visible to your team in real time.
### Order
An Order is generated automatically when a Quote reaches `Accepted` status. It captures the final products, quantities, pricing, and payment terms — and becomes the source of truth for fulfillment and revenue recognition.
### Sales Agreement
A Sales Agreement is a framework contract that governs an ongoing commercial relationship. It captures volume commitments, negotiated pricing, and special terms that apply across multiple quotes and orders over time.
### Contract
A Contract is a container that groups related Quotes and Orders for the same deal. It provides a single reference for the full engagement — including amendments, renewals, and multi-phase projects.
The Contract Model selected in the Template populates the legal document with live quote data using variables like `{{customer.name}}` and `{{quote.total}}`.
## Quote statuses
| Status | What it means |
| ------------------- | -------------------------------------------------------------------- |
| `Draft` | The Quote is being built by the Sales Rep |
| `Pending Approval` | The Quote has been submitted and is waiting for manager review |
| `Changes Requested` | The approver has sent the Quote back for revision |
| `Approved` | The manager has authorized the Quote for delivery |
| `Published` | The Quote is live in the Dealroom, visible to the buyer |
| `Pending Signature` | The buyer has reviewed and is in the process of signing |
| `Accepted` | The Quote is signed and closed — an Order is generated automatically |
| `Expired` | The Quote passed its expiration date without being accepted |
| `Lost` | The deal did not close |
| `Canceled` | The Quote was withdrawn before acceptance |
## Dive deeper
Statuses, approval flows, and quote management in detail
Products, variants, bundles, and pricebooks
Configure your sales funnels
The buyer experience from publish to signature
# Qwoty data model
Source: https://docs.qwoty.io/getting-started/data-model
Understand how Qwoty's entities connect — from workspace setup to the final signed deal.
Before you configure your workspace, it helps to understand how Qwoty is structured. This page walks you through the five domains that make up the platform and how they connect across the quote-to-revenue flow.
You don't need to know every entity to start using Qwoty. This page is for admins and technical leads who want to model their business correctly from day one.
## The five domains
Qwoty's data model is organized around five functional domains. Each domain owns a set of entities, and together they form the backbone of every deal you run.
Your organization, teams, users, and the settings that apply everywhere: currencies, taxes, languages.
What you sell and how you price it: products, catalogs, bundles, pricebooks, discounts.
Companies you sell to and the people inside them: accounts, contacts, addresses.
How a quote is pre-configured: templates, payment terms, contracts, approval workflows.
The deal itself: quotes, line items, dealrooms.
## How the domains connect
Here's a simplified view of how data flows from your workspace setup all the way to a signed deal.
```mermaid theme={null}
flowchart TB
subgraph WS["Workspace & users"]
Workspace[Workspace]
Users[Users]
Teams[Teams]
end
subgraph CP["Catalog & pricing"]
Catalog[Catalog]
Products[Products]
Bundle[Bundle]
Pricebook[Pricebook]
Price[Price]
end
subgraph C["Customers"]
Customer[Customer]
Contact[Contact]
end
subgraph TC["Template"]
Template[Template]
PaymentTerm[Payment term]
Contract[Contract]
end
subgraph SC["Sales cycle"]
Quote[Quote]
LineItem[Line item]
Dealroom[Dealroom]
end
Workspace --> Users
Workspace --> Catalog
Catalog --> Products
Catalog --> Bundle
Catalog --> Pricebook
Products --> Price
Bundle --> Products
Pricebook --> Price
Template --> Catalog
Template --> PaymentTerm
Template --> Contract
Customer --> Contact
Customer --> Quote
Users --> Quote
Template --> Quote
Quote --> LineItem
LineItem --> Products
LineItem --> Price
Quote --> Dealroom
```
## Key relationships to remember
Four relationships drive almost everything in Qwoty:
**A Template bundles your sales configuration.** When a sales rep creates a new quote, the Template they pick pre-configures the Catalog, Pricebook, Payment terms, Contract, Approval workflow, and Discount rules. One click replaces dozens of manual selections.
**A Customer can own its own Catalogs, Pricebooks, and Templates.** If you set these directly on a Customer (or a Customer segment), Qwoty will use them first and fall back to the Template defaults only if nothing is defined. This gives you per-client pricing and branding without duplicating anything.
**A Quote is composed of Line items.** Each Line item links to one Product and one Price. The Price comes from the Pricebook selected at quote creation — if the product has no price in that pricebook, Qwoty falls back to the default pricebook.
**A Dealroom is the customer-facing view of a Quote.** Publishing a quote to a Dealroom is what makes it visible, commentable, and signable by the buyer. One Quote has one Dealroom.
The Customer-level override pattern (Customer > Segment > Template default) is used throughout Qwoty. See the quote creation logic reference for a full breakdown.
## Explore each domain
### Workspace & users
The Workspace is the top-level container for everything in Qwoty. It defines the currencies you operate in, the taxes you apply, and the languages your data can be translated to. Users are grouped into Teams, and optionally split into Business units when your company has multiple entities.
```mermaid theme={null}
erDiagram
Workspace ||--|{ Language : "supports"
Workspace ||--|{ Taxes : "defines"
Workspace ||--|{ Currency : "defines"
Workspace ||--|{ Teams : "contains"
Workspace ||--|{ BusinessUnit : "contains"
Users }|--|{ Teams : "belong to"
```
| Entity | Description |
| ---------------- | --------------------------------------------------- |
| **Workspace** | Your Qwoty instance — all data lives here |
| **BusinessUnit** | Separate entity within your organization (optional) |
| **Users** | People who log in and work in Qwoty |
| **Teams** | Groups of users (Sales, Ops, Finance...) |
| **Language** | Available locales for translations |
| **Currency** | Currencies you accept on quotes and orders |
| **Taxes** | Tax rates you apply (VAT, sales tax...) |
### Catalog & pricing
This domain describes what you sell and at what price. A Catalog groups your Products and Bundles. Each Product has one or more Prices, organized into Pricebooks — one Pricebook per customer segment or geography. A Bundle is a ready-made package of multiple Products sold as a single unit. Discounts can be applied on top of any price.
```mermaid theme={null}
erDiagram
Catalog }|--|{ Products : "contains"
Catalog }|--|{ Bundle : "contains"
Bundle }|--|{ Products : "groups"
Products }|--|{ Price : "has"
Price }|--|{ Pricebook : "belongs to"
Catalog }|--|{ Pricebook : "linked to"
Currency }|--|{ Pricebook : "denominated in"
Taxes }|--|{ Pricebook : "apply to"
Discount }|--|{ Products : "applies to"
```
| Entity | Description |
| ------------- | ------------------------------------------------------ |
| **Catalog** | Group of Products and Bundles available to sell |
| **Products** | What you sell (goods, services, subscriptions) |
| **Bundle** | Package of multiple Products sold together as one unit |
| **Pricebook** | Price list for a segment, geography, or customer |
| **Price** | A Product's price in a specific Pricebook |
| **Discount** | Price reduction rule applied to one or more Products |
Product variants exist as a feature in Qwoty but are not yet represented as a separate entity in this model. A variant is stored as a Product with shared parent metadata.
### Customers
Customers are the companies you sell to. Each Customer has one or more Contacts (the people you email and invite to Dealrooms), plus billing and shipping addresses used on quotes and orders.
```mermaid theme={null}
erDiagram
Customer ||--|{ Contact : "has"
Customer ||--|{ BillingAddress : "has"
Customer ||--|{ ShippingAddress : "has"
```
| Entity | Description |
| -------------------- | ----------------------------- |
| **Customer** | The company you sell to |
| **Contact** | An individual at the customer |
| **Billing address** | Where invoices are sent |
| **Shipping address** | Where products are delivered |
### Template & sales configuration
A Template is the single most important object for admins. It bundles everything a quote needs: which Catalog to use, which Pricebook applies, which Payment terms, which Contract text, which Approval workflow, which PDF layout. Sales reps pick a Template at quote creation and the rest is pre-configured.
```mermaid theme={null}
erDiagram
Template }|--|{ Catalog : "uses"
Template }|--|{ PaymentTerm : "applies"
Template }|--|{ Contract : "applies"
Template }|--|{ ApprovalWorkflow : "triggers"
Template }|--|{ Discount : "applies"
Template }|--|{ ContentLibrary : "uses"
Template }|--|{ PDFTemplate : "renders with"
Template }|--|{ CheckoutForm : "uses"
Template }|--|{ BusinessUnit : "scoped to"
```
| Entity | Description |
| --------------------- | ---------------------------------------------------------------- |
| **Template** | Sales funnel configuration (bundles Catalog + Pricebook + rules) |
| **Payment term** | Billing frequency and due dates (Net 30, monthly...) |
| **Contract** | Legal document template with dynamic variables |
| **Approval workflow** | Rules that govern when a quote needs validation |
| **Content library** | Reusable text and media blocks |
| **PDF template** | Layout and branding of exported PDF quotes |
| **Checkout form** | Custom form shown to the buyer in the Dealroom |
### Sales cycle
The sales cycle is where deals actually happen. A Quote is created for a Customer using a Template. It contains one or more Line items — each tied to a Product and a Price. Once published, the Quote becomes visible to the buyer through a Dealroom, where they can review, comment, and sign.
```mermaid theme={null}
erDiagram
Users ||--|{ Quote : "creates"
Customer ||--|{ Quote : "receives"
Template }|--|{ Quote : "pre-configures"
Quote ||--|{ LineItem : "composed of"
LineItem }|--|{ Products : "references"
LineItem }|--|{ Price : "references"
Quote ||--|| Dealroom : "publishes to"
```
| Entity | Description |
| ------------- | --------------------------------------------------------- |
| **Quote** | Priced proposal sent to a Customer |
| **Line item** | One product entry in a Quote, with quantity and price |
| **Dealroom** | Buyer-facing space to review, comment, and sign the Quote |
## What's not in this model yet
The current data model (v1) does not include:
* **Order** — Orders are automatically generated from accepted quotes. They share most of their structure with Quote and Line item, and will be modeled explicitly in the next iteration.
* **Sales Agreement** — A framework contract with volume commitments and negotiated pricing. Sales agreements apply to multiple Orders and will be added to the model in a future release.
Orders and Sales Agreements are live in the product — you can create and manage them today. Their omission here is purely in the diagram, which will be updated as the model evolves.
## Next steps
Configure currencies, taxes, languages, and business units before importing data.
Start modeling what you sell with products and categories.
Learn how Templates bundle catalog, pricebook, and contract configuration.
Follow a quote from draft to signed deal.
# Introduction
Source: https://docs.qwoty.io/getting-started/introduction
Overview of Qwoty, the AI-powered CPQ platform for the full quote-to-revenue process.
Qwoty is an AI-powered CPQ platform that covers the full quote-to-revenue stream — from product configuration and smart pricing through e-signature, Dealroom collaboration, and order generation.
It's built for sales teams in manufacturing, retail, and tech who need accurate quotes, margin control, and a single place to manage every deal.
This documentation covers everything from initial setup to advanced configuration.
## What's in this documentation
**Getting Started** — You are here. Key concepts, platform overview, and quickstart guides for Sales Reps and Admins.
**User Guide** — In-depth reference for every feature: quotes, catalog, templates, workflows, Dealroom, and settings.
**Developers** — API reference, webhooks, and integration guides, hosted at [docs.qwoty.io](https://docs.qwoty.io).
## Start here
Learn the vocabulary — catalogs, pricebooks, templates, quotes ...
Understand the problems Qwoty solves and how it's different
Create and send your first quote in minutes
Understand how Qwoty's entities connect
# Customize your brand kit
Source: https://docs.qwoty.io/getting-started/setup/brand-kit
Make your quotes and Dealrooms match your brand with custom fonts, colors, and your logo.
Your brand kit controls how quotes and Dealrooms look to your buyers — fonts, colors, button styling, content blocks, and your logo. A polished brand kit is what turns a generic quote into a document that feels unmistakably yours.
**Time:** \~15 minutes
**Where it lives:** the Brand kit is a section of your business unit, not a separate page.
This guide focuses on the visual customization. For the full business unit form (legal entity, tax IDs, identifiers), see [Business units](/user-guide/settings/workspace/business-units).
## Before you start
You'll need:
* Your **logo** as a PNG, JPEG, PJG, WEBP, or GIF file (max 10 MB)
* Your **brand colors** in hex format (for example, `#0066FF`)
* Optionally, a preferred **font** for headings (Qwoty includes common web fonts)
If your designer maintains a brand guideline document, keep it open in a tab — you'll copy hex codes and font names from it as you go.
## Open your brand kit
A default business unit is created automatically at sign-up. You configure its brand kit in Settings.
In the left sidebar, click **Settings**.
Under the **Workspace** section, click **Business Units**.
Click your default business unit to open its configuration form.
The form has four sections — scroll to **Brand kit** to start customizing your visual identity.
## Set up live preview before you edit
Before you start changing colors and fonts, open one of your quotes in a separate browser window. As you save changes to the Brand kit, you'll refresh the quote window to see your updates applied in real time.
In a new browser window or tab, navigate to **Projects → Quotes** and open any draft quote. If you don't have one yet, create a quick test quote with a single line item.
Arrange the two windows so you can see both at once: the Brand kit form on one side, the quote on the other. Split-screen view works well on a single monitor.
Each time you click **Save** in the Brand kit, refresh the quote window. The new colors, fonts, and logo apply instantly.
This live preview workflow saves you from blindly tweaking values, saving, navigating to a quote, navigating back, and so on. You see the result of every change in seconds.
## Customize fonts and colors
The Brand kit splits into three groups of styling: theme, buttons, and blocks. Adjust them in this order — theme first sets the foundation, buttons and blocks refine the details.
### Theme
The theme controls the base look of every quote and Dealroom page.
* **Title Font** — font family for headings. `Poppins` is a clean, modern default. Use a font that matches your other brand materials.
* **Theme Background Color** — the main page background. Most brands use white (`#FFFFFF`) or a very light gray.
* **Theme Text Color** — the default text color. Black (`#000000`) or a very dark gray gives the best readability.
### Buttons
Buttons appear when buyers approve, comment, or sign quotes in the Dealroom.
* **Button Background Color** — fill color of buttons. Often your primary brand color.
* **Button Text Color** — text color inside buttons. Make sure it contrasts strongly with the background.
* **Button Corner** — sets the corner roundness. Choose **None** for sharp corners, or pick a curved option for a softer feel.
### Blocks
Blocks are the content boxes that group quote line items, terms, and signatures.
* **Block Stroke** — the border style around each block (none, thin, thick)
* **Block Stroke Color** — color of the border
* **Block Corner** — corner roundness for blocks (matches your overall aesthetic)
* **Block Shadow** — drop shadow style for added depth
If you ever want to start over, click **Reset** at the top of the Brand kit section to revert to Qwoty defaults.
## Upload your logo
The logo appears at the top of every quote and Dealroom.
Either drag a file directly into the upload zone or click **Select existing files** to pick from your media library.
* **Formats:** PNG, JPEG, PJG, WEBP, GIF
* **Maximum size:** 10 MB
* **Recommended dimensions:** max height 50 px, max width 150 px
* **Display:** images render at their original size, so size your file before upload
Refresh the quote window to confirm the logo renders cleanly at the top of the document. If it looks pixelated or oversized, upload a smaller, higher-resolution file.
Avoid uploading a logo larger than the recommended 50 × 150 px. Since images display at their original size, oversized files crowd the quote header and push content down.
## Save and verify
At the bottom of the form, click **Save** to apply your brand kit.
Switch to your quote window and refresh. All your changes — fonts, colors, logo — should appear at once.
Click **Publish** on the quote (or open an already-published one) to see the buyer-facing Dealroom. The brand kit applies here too.
Your business unit is now branded. Every new quote and Dealroom will use this look automatically.
## What's next
You've customized the look. Now use it to create something real.
Create your first quote and see your branding live in a Dealroom.
Configure tax IDs, legal entity details, and external identifiers.
Link your business unit to templates so quotes inherit the branding.
Sync your customers and deals before scaling up.
# Connect your CRM
Source: https://docs.qwoty.io/getting-started/setup/connect-crm
Sync customers and deals from HubSpot or Pipedrive into your Qwoty workspace.
Connecting a CRM lets Qwoty pull in your customers and contacts so reps can build quotes without re-entering data. The connection uses OAuth — Qwoty never stores your CRM password.
**Time:** \~5 minutes per CRM **Supported CRMs:** HubSpot, Pipedrive
You must have the **Admin** role to connect or disconnect integrations.
## Before you start
You'll need:
* Admin access to your Qwoty workspace
* A HubSpot or Pipedrive account with permission to authorize third-party apps
## Connect a CRM
Go to **Settings → Integrations**. You'll see a grid of available providers.
Locate the **HubSpot** or **Pipedrive** card in the list.
Click **Connect** on the CRM card. A new browser tab opens with your CRM's OAuth authorization page.
Log in to your CRM if prompted, then approve the access request. Qwoty requests access to companies, contacts, and deals.
Authorize with an account that has access to the data you want to sync — typically a CRM admin or the account owner.
After you approve, you're redirected back to the Integrations page. The CRM card now shows **Connected**.
CRM connected. Qwoty will start syncing customers and contacts into your workspace.
## What syncs
Once connected, Qwoty imports the following records from your CRM.
| CRM record | Qwoty record |
| ---------- | ----------------------------------------- |
| Companies | Customers |
| Contacts | Contacts linked to customers |
| Deals | Available as context when building quotes |
For full field mapping and sync frequency, see the [HubSpot integration reference](https://docs.qwoty.io/hubspot).
| CRM record | Qwoty record |
| ------------- | ----------------------------------------- |
| Organizations | Customers |
| Persons | Contacts linked to customers |
| Deals | Available as context when building quotes |
For full field mapping and sync frequency, see the [Pipedrive integration reference](https://docs.qwoty.io/pipedrive).
## Disconnect a CRM
To disconnect, go to **Settings → Integrations**, find the connected CRM card, and use the card menu to disconnect.
Existing customer data already imported into Qwoty is not deleted when you disconnect.
## What's next
Upload your logo, pick your colors, and brand your quotes.
Create a quote and send it to a customer.
# Create your account
Source: https://docs.qwoty.io/getting-started/setup/create-account
Sign up for Qwoty and get your workspace ready in minutes.
Welcome to Qwoty. This page walks you through creating your account and starting your 14-day free trial. Your workspace is created automatically at sign-up, with currencies, taxes, and languages pre-configured based on your country.
**Time:** \~5 minutes **What you'll get:** a ready-to-use Qwoty workspace with your admin account.
No credit card is required to start your free trial. You can invite teammates and explore every feature for 14 days before choosing a plan.
## Before you start
You'll need:
* A work email address
* Your company name and country
* Optionally, a Google or Microsoft account to sign in faster
## Sign up
Open [qwoty.app/signin](https://qwoty.app/signin) in your browser and click **Sign up**.
Qwoty offers three sign-up methods. Pick the one that fits your team:
* **Continue with Google** — recommended if your team uses Google Workspace
* **Continue with Microsoft** — recommended if your team uses Microsoft 365
* **Continue with email** — a classic email and password flow
Using Google or Microsoft sign-in also simplifies inviting teammates later — they can log in with one click using the same provider.
Enter your **first name**, **last name**, **work email**, **company name**, and **country**. If you chose the email sign-up method, create a password.
The country you select determines the default currencies, tax rates, and languages pre-configured in your workspace. You can add more later if your team operates in multiple regions.
Qwoty sends a verification link to your inbox. Click it to activate your account. If you don't see the email within a minute, check your spam folder.
Once verified, you'll arrive directly on your Qwoty dashboard. Your workspace is ready.
Your Qwoty account is live. You're now the Admin of a fresh workspace with default configuration.
## What's set up automatically
Qwoty pre-configures your workspace so you can focus on your business, not on IT setup. Defaults are localized to the country you provide at sign-up.
| Item | Default |
| ----------------- | -------------------------------------------------------------------------- |
| **Workspace** | Created using your company name |
| **Business unit** | One default business unit, ready to customize |
| **Currencies** | Your country's main currency (for example, EUR for France, USD for the US) |
| **Taxes** | Local tax rates matching your country (VAT, sales tax, GST, and so on) |
| **Languages** | Your country's primary language, plus English |
| **Your role** | Admin, with full access to everything |
| **PDF template** | Default Qwoty-branded layout |
You can review or edit any of these defaults later under **Settings → Workspace** or **Settings → Workspace → Business units**.
Operating in multiple countries? Add more currencies, taxes, and languages later from **Settings → Workspace**. Qwoty supports multi-currency pricing and multi-language quotes out of the box.
## What the dashboard shows you
When you first land on the dashboard, you'll see:
* **Your user menu** in the top-right corner, with access to Profile and Settings
* **The left sidebar** with the main sections: Projects, Products & Pricing, Customers, Workflow, Documents, Templates
* **Empty dashboard widgets** — they'll fill with data as you start creating quotes, customers, and products
Feel free to click around and explore. The guides in this section will walk you through the next steps in order.
## What's next
Now that your account is live, follow the setup path in order. Each step builds on the previous one.
Add your sales, ops, and finance teammates and assign roles.
Sync customers and deals from HubSpot, Salesforce, or Pipedrive.
Upload your logo, pick your colors, and brand your quotes.
Skip ahead and create a quote right now to see Qwoty in action.
# Create your first catalog
Source: https://docs.qwoty.io/getting-started/setup/first-catalog
Set up a catalog to group products, configure pricing, and organize your sales offerings.
A catalog groups your products and configured products into a single collection. You can set validity dates, link pricebooks, and track where the catalog is used across your Qwoty configuration.
This guide walks you through creating your first catalog and configuring its core settings.
## Prerequisites
You must have the **Admin** role to create and configure catalogs.
## Access the catalog screen
In the left side menu, click **Products & Pricing**.
Click **Catalogs** to view the catalog list.
Click **+ New catalog** at the top right.
## Configure general information
Enter a **Name** that describes the catalog's purpose, such as `France Catalog` or `Premium Catalog`.
Enter an internal **Reference** code to identify the catalog in reports and integrations.
Define **Valid from** and **Valid until** dates if the catalog applies to a specific time period. Leave blank for permanent catalogs.
Validity dates are useful for seasonal catalogs or time-limited promotional offers.
## Add products to the catalog
In the **Collections** section, click the **Configured product** field and select any configured products you want to include. Selected products appear as tags.
In the **Related products** section, click **+ Add a product** and select products from the list. Each product displays its variant, reference, and billing type (One-off or Recurring).
You can also import products in bulk using the **Import** button, or export the current product list with **Export**.
## Configure external identifiers
If you integrate Qwoty with external systems, enter the corresponding identifiers:
* **CRM ID**: Catalog identifier in your CRM
* **ERP ID**: Identifier in your ERP system
* **Accounting ID**: Identifier in your accounting software
These fields enable seamless data sync across your tech stack.
## Set catalog status
In the right-hand panel:
1. Toggle **Status** to `Active` to make the catalog available for use in templates and quotes
2. Review the **API name** (auto-generated technical identifier)
The **API name** can only be changed at creation. Once saved, it cannot be modified to ensure integration stability.
## Save your catalog
Click **Save** at the top right to create the catalog.
## What happens next
Once saved, your catalog becomes available for selection when creating quote templates. You can associate one or more pricebooks with the catalog to define pricing for different customer segments.
The **Catalog used in** block will display all templates, customers, and customer segments using this catalog.
## Troubleshooting
You need the **Admin** role to create catalogs. Contact your Qwoty administrator to request access.
Configured products must be created in the **Product Configurator** before they can be added to a catalog. Navigate to **Products & Pricing** > **Configured products** to create them first.
Yes. Open the catalog in edit mode and use **+ Add a product** or remove products from the **Related products** list. Changes take effect immediately after you click **Save**.
## Related resources
Associate pricing rules with your catalog
Build your product library
Create product packages
Combine catalogs with pricing and terms
# Configure your first pricebook
Source: https://docs.qwoty.io/getting-started/setup/first-pricebook
Set up pricing rules and link them to catalogs and customer segments
A pricebook defines the prices for products in a catalog. You can create multiple pricebooks for different customer segments, regions, or discount tiers. Qwoty automatically applies the right pricebook based on the customer, segment, or template.
## Prerequisites
You must have the **Admin** role to create and configure pricebooks.
## Access the pricebook settings
In the left side menu, click **Settings**.
In the **Products & Pricing** section, click **Pricebooks**.
## Create your first pricebook
Click **New pricebook** in the top right corner.
Fill in the required fields:
* **Name**: Enter a descriptive name (e.g., `Standard Pricing 2026`, `Enterprise Discount Tier`)
* **Catalog**: Select the catalog this pricebook applies to
* **Currency**: Choose the currency for all prices in this pricebook
* **Valid from** (optional): Set the start date for this pricebook
* **Valid until** (optional): Set the expiration date for this pricebook
Click **Add products** to select items from the linked catalog. Enter the price for each product in the **Unit price** column.
You can also set:
* **Discount %**: Apply a percentage discount to the list price
* **Minimum quantity**: Define the minimum order quantity for this price
* **Maximum quantity**: Define the maximum order quantity for this price
Click **Save** to create your pricebook.
## Link the pricebook to customers or segments
Once created, you can associate your pricebook with specific customers or customer segments.
Navigate to **Customers** and select a customer record, or go to **Settings > Customers > Segments** to edit a segment.
In the **Pricing** section, click **Add pricebook** and select your newly created pricebook from the dropdown.
Click **Save** to confirm the link.
## Configure association resolution mode
The association resolution mode determines which pricebooks, catalogs, and payment terms are available when creating a quote.
### Access the quote settings
In the left side menu, click **Settings**.
In the **Workspace** section, click **Quotes**.
### Choose your resolution mode
Qwoty checks associations in this priority order:
1. **Customer** (highest priority)
2. **Customer segment**
3. **Template** (lowest priority)
Select one of the following modes:
**Inclusive Mode**
Elements from **all levels** are combined and available when creating a quote.
*Example: If the customer has associated pricebooks, those from the segment and the template remain available as well.*
**Exclusive Mode**
Only elements from the **first configured level** are available. Other levels are ignored.
*Example: If the customer has associated pricebooks, those from the segment and the template are ignored.*
Select the desired mode from the dropdown menu, then click **Save**.
This setting applies only to quotes. Orders and contracts are not affected.
## What happens next
When a Sales Rep creates a quote for a customer with an associated pricebook, Qwoty automatically applies the correct prices based on the resolution mode. If multiple pricebooks match the criteria, the system follows the priority order (Customer > Segment > Template).
## Troubleshooting
Verify that the pricebook is linked to the customer, customer segment, or template. Check that the **Valid from** and **Valid until** dates include today's date. Confirm the pricebook uses the same catalog as the template.
This happens in **Inclusive Mode**. If you want only the customer's pricebook to apply, switch to **Exclusive Mode** in **Settings > Workspace > Quotes**.
Only **Admin** users can edit pricebooks. Check your role in **Settings > Users**. If a pricebook is used in active quotes, some fields may be locked to prevent data conflicts.
## Related resources
Set up a catalog of products before creating pricebooks
Group customers and apply pricebooks at scale
Combine catalogs, pricebooks, and payment terms
Automate discounts and tiered pricing
# Send your first quote
Source: https://docs.qwoty.io/getting-started/setup/first-quote
Create a quote from a template, add products, and send it to your first customer.
This page walks you through the full flow: creating a quote from a template, adding products, and sharing it with a customer through the Dealroom.
**Time:** \~10 minutes
**What you'll get:** a live quote delivered to a customer's inbox with a link to sign.
You need at least one **Template** configured before you can create a quote. If your catalog and templates aren't set up yet, complete the [catalog setup](/user-guide/catalog/introduction) first.
## Before you start
You'll need:
* A Template with a Catalog and Pricebook attached
* A customer name and at least one email address to send to
## Create a new quote
In the left sidebar, click **Project**, then select **Quote**.
Click **New quote** at the top right. The **New project** panel opens.
The panel is titled "New project" because every quote belongs to a project in Qwoty. Qwoty creates the project automatically in the background — you don't need to manage it separately.
In the **Customer** field, search for an existing customer or click **New** to create one inline.
In the **Primary contact** field, select the contact you're addressing the quote to. Click **New** to add a contact if none exist yet.
Select a **Template**. The template sets the catalog, pricebook, payment terms, and contract model for this quote. Qwoty fills in the **Catalog** automatically based on the template you choose.
Choose the **Pricebook** to apply. The pricebook controls which products appear in this quote and at what base price.
Click **Create**. The quote editor opens with the status set to `Draft`.
Quote created. You're now in the quote editor.
## Add products
In the quote editor, add products from the catalog attached to your template.
Click **Add product** in the quote body to open the product picker. Search or browse for the product, select a variant if applicable, then confirm the quantity and pricing. Repeat for each product in this quote.
Click **Save** at any point to save your work.
## Send the quote
Click **Share** in the top-right toolbar. The **Share new quote** dialog opens.
The dialog has two tabs:
* **Share Dealroom** — sends the quote by email and opens it in an interactive Dealroom where the buyer can review, comment, and sign.
* **Get link** — generates a direct link you can paste or share manually.
Select **Share Dealroom** to deliver by email.
Fill in the fields:
| Field | Notes |
| ---------------------- | ----------------------------------------------------------------------------------- |
| **Sender** | The name that appears as the quote sender |
| **Recipients** | One or more buyer email addresses |
| **Email subject** | Optional — customize the subject line |
| **Message** | A personal note included in the email body |
| **Automatic reminder** | Toggle on to send a follow-up if the buyer hasn't opened within the reminder period |
Click **Send quote by email**. Qwoty sends the notification and publishes the Dealroom.
Quote sent. The status moves to `Published`. Your customer receives an email with a link to their personalized Dealroom.
## What happens next
Your customer opens the Dealroom link, views the quote, asks questions, and signs electronically — all in one place. You'll see their activity in real time from the quote detail view.
When the buyer signs, the quote status moves to `Accepted` and Qwoty generates an Order automatically. No manual steps required.
## What's next
Statuses, approval flows, and managing your quote pipeline.
What the buyer sees and how to collaborate in the Dealroom.
# Create your first template
Source: https://docs.qwoty.io/getting-started/setup/first-template
Set up your first sales funnel template to start creating quotes in Qwoty
A template is the foundation of your sales workflow in Qwoty. It combines a catalog of products, pricing rules, payment terms, and contract settings into a reusable configuration. Once created, your sales team can generate quotes from this template with consistent pricing and terms.
## Prerequisites
Before creating your first template, ensure you have:
* At least one **Catalog** with products added
* At least one **Pricebook** configured (or use the default pricebook)
* Admin or Manager role permissions
## Access the template builder
Navigate to **\[MENU PATH]** and click **\[BUTTON LABEL]**.
## Create the template
Enter a **\[FIELD NAME]** for your template (e.g., `Enterprise Sales - North America`).
Add an optional **\[FIELD NAME]** to help your team understand when to use this template.
Choose the **\[FIELD NAME]** that contains the products you want to sell.
Select the **\[FIELD NAME]** that defines pricing for this template. If you serve different customer segments, create separate templates with different pricebooks.
Define the **\[FIELD NAME]** for quotes generated from this template.
Common options include net payment periods (e.g., `Net 30`, `Net 60`) or custom payment schedules.
Payment terms configured here will appear as defaults on quotes, but sales reps can modify them per deal if needed.
Select a **\[FIELD NAME]** to automatically generate contracts with dynamic quote data.
If you don't attach a contract model now, you can add one later or use Qwoty without automated contract generation.
Click **\[BUTTON LABEL]** to make the template available to your sales team.
## What happens next
Your template is now ready for use. Sales reps can create quotes by selecting this template, which will automatically apply:
* The configured catalog and pricebook pricing
* Default payment terms
* Contract generation rules (if a contract model was attached)
The template appears in the template selector when creating new quotes.
## Troubleshooting
Verify that the template status is active. Navigate to **\[MENU PATH]** and check the **\[FIELD NAME]** column. Inactive templates won't appear in the quote creation flow.
Check that the selected pricebook contains pricing rules for all products in your catalog. Go to **\[MENU PATH]** and confirm each product has a defined price.
Ensure you have at least one contract model created in **\[MENU PATH]** . Contract models must be created separately before attaching to templates.
## Related resources
Learn how to organize products into catalogs for different markets or regions
Set up pricing rules and customer segment pricing
Use your template to generate a quote for a customer
Understand how to create dynamic contract templates
# Import your data
Source: https://docs.qwoty.io/getting-started/setup/import-data
Bring your existing products, prices, and customers into Qwoty using CSV files.
If you already have a product catalog, price list, or customer base in another tool, importing into Qwoty is faster than re-entering everything by hand. Qwoty's CSV import wizard handles the three core objects you need to start quoting.
**Time:** \~30 minutes for a typical migration (small catalog, few hundred customers)
You must have the **Admin** role with **Import CSV** authorized on your Apps permissions. See [Manage roles](/user-guide/settings/users/manage-roles).
## What to import — and in what order
Qwoty enforces a strict import order because objects reference each other. Run them sequentially:
Master products are the **parents** that group variants. Even single-variant products go through this object first. Import them via **Settings → Data → Import/Export Data → Master products**.
See the [full guide](/user-guide/data-migration/how-tos/import-master-products).
Once masters exist, import the variants that reference them — sizes, colors, configurations, or just the single variant of a simple product.
See the [full guide](/user-guide/data-migration/how-tos/import-products).
With your products in place, attach prices to one or more pricebooks. Qwoty supports flat, tiered, recurring, percent, cost-based, and placeholder pricing.
See the [full guide](/user-guide/data-migration/how-tos/import-prices).
Customers don't depend on the catalog and can be imported any time, before or after products. Use the **Customers** import on the customers page.
See the [Import customers guide](/user-guide/customers/how-tos/import-csv).
**Don't skip the order for the catalog.** Importing prices before products fails because there's nothing to attach the prices to. Importing variants before masters fails because there's no parent. Always run Master products → Products → Prices.
## Before you start
A 10-minute checklist that prevents 90% of import errors:
* [ ] Export your data from your existing tool as CSV
* [ ] Make sure your file uses **UTF-8** encoding (avoid Excel's default regional encoding)
* [ ] Use **comma `,`** as the field separator
* [ ] Create **Catalogs**, **Categories**, and **Pricebooks** in Qwoty first (through the UI) — your CSVs reference them by API name
* [ ] Add any **custom fields** you need under **Settings → Data → Data model** before importing — uploading a CSV creates records but doesn't create fields
For a complete preparation guide, including field formats, validation rules, and the final checklist, read [Prepare your CSV files](/user-guide/data-migration/how-tos/prepare-csv-files).
## The import wizard
Every import follows the same 4-step flow:
1. **Upload** — drop your CSV. Download the template if you don't have one.
2. **Mapping** — match your headers to Qwoty fields. Auto-mapped when names match.
3. **Confirm** — review the count of rows that will import vs rows with errors.
4. **Result** — see what succeeded. Download unimported rows as a CSV if any failed.
Errors don't block valid rows. Fix the failed rows in your source file and re-upload.
## Tips for a clean migration
**Test with a small batch first.** Import 5–10 rows from a sample before uploading thousands. Fix any systematic issue once, then run the full batch.
**Keep your source file as the source of truth.** Save a copy before each import. If something goes wrong, you can compare against the unimported rows that Qwoty returns.
**Use API names, not display names.** When linking products to catalogs or prices to pricebooks, the API name is the stable identifier — the display name might change, the API name shouldn't.
**Migrating from a CRM?** If you connect a CRM later (HubSpot, Salesforce, Pipedrive), customer records can sync automatically. You can choose to import customers via CSV now and let the CRM take over later via [CRM mapping](/user-guide/data-model/reference/crm-mapping).
## What's next
Once your data is in Qwoty, you have everything you need to send your first quote.
Use your imported products and customers to create and publish a quote.
Connect Qwoty to your CRM, billing, and messaging tools.
All imports, every option, error handling, and reference docs.
Add custom fields to capture business-specific data.
# Set up integrations
Source: https://docs.qwoty.io/getting-started/setup/integrations
Connect Qwoty to your CRM, messaging tools, billing software, and payment providers.
Qwoty connects to the tools your team already uses. Integrations are managed from a single page and use OAuth — no API keys to copy, no credentials to store.
**Time:** \~5 minutes per integration
You must have the **Admin** role to connect or disconnect integrations.
## Available integrations
Go to **Settings → Integrations** to see all available providers. Integrations are grouped by category.
### CRM
Connect your CRM to sync customers and deals into Qwoty automatically. Reps can then build quotes directly from existing CRM records without re-entering data.
| Integration | Notes |
| ------------- | --------------------------------------- |
| **HubSpot** | Syncs companies, contacts, and deals |
| **Pipedrive** | Syncs organizations, persons, and deals |
For a step-by-step connection guide, see [Connect your CRM](/getting-started/setup/connect-crm).
### Messaging
Send quote activity notifications directly to your team's messaging channels. Useful for alerting reps when a buyer opens a Dealroom or signs a quote.
| Integration | Notes |
| ------------------- | ------------------------------------- |
| **Slack** | Post notifications to channels or DMs |
| **Google Chat** | Post notifications to spaces |
| **Microsoft Teams** | Post notifications to channels |
Messaging integrations are in **Beta**. Features and configuration options may change.
### Billing
Connect your billing software to pass finalized order data downstream and avoid double-entry.
| Integration | Notes |
| -------------------- | ---------------------------------------- |
| **Pennylane** | Sync orders and invoicing data |
| **Stripe - Invoice** | Create Stripe invoices from Qwoty orders |
Stripe - Invoice is in **Beta**.
### Payment
Enable buyers to pay directly from the Dealroom after signing.
| Integration | Notes |
| ------------------------- | ------------------------------------------------------- |
| **Stripe - payment link** | Generate a Stripe payment link embedded in the Dealroom |
Stripe - payment link is in **Beta**.
## Connect an integration
The connection flow is the same for all integrations that use OAuth.
Go to **Settings → Integrations**.
Locate the provider card in the grid.
Click **Connect** on the card. A new browser tab opens with the provider's authorization page.
Log in to the provider if prompted and approve the access request.
You're redirected back to the Integrations page. The card shows **Connected**.
Integration active.
For integrations without a **Connect** button — such as Pennylane — refer to the **Documentation** link on the card for provider-specific setup steps.
## What's next
Step-by-step guide for HubSpot and Pipedrive.
Create a quote and send it to a customer.
# Invite your team
Source: https://docs.qwoty.io/getting-started/setup/invite-team
Add teammates to your Qwoty workspace and configure their access with licences, roles, and teams.
Once your account is live, invite your sales, ops, and finance teammates. Each user gets a **licence** that controls what they can create or edit, and a **role** that determines which features they can access.
**Time:** \~5 minutes
You must have the **Admin** role to invite and manage users.
## Before you start
You'll need:
* The work email address of each teammate you want to invite
* A clear idea of the role each person will play: sales rep, manager, admin, or read-only
## Understand licences and roles
Qwoty uses two independent settings to control user access. You set both when sending an invitation.
**Licences** control what a user can create or edit:
| Licence | What the user can do |
| ------------- | ------------------------------------------------------------------------------ |
| **Core** | Full access to Qwoty according to their role |
| **Support** | Same as Core, except cannot create or edit quotes, orders, or Sales Agreements |
| **View only** | Read-only access across the entire application |
**Roles** control which features and data a user can reach:
| Role | Access level |
| ------------- | ----------------------------------------------------- |
| **Admin** | Full access to all settings, users, billing, and data |
| **Manager** | Team management, approval workflows, and reporting |
| **Sales Rep** | Create and manage quotes, customers, and Dealrooms |
| **Viewer** | Read-only access to quotes and reports |
Assign **Core** + **Sales Rep** for most of your sales team. Use **Support** for back-office users who need visibility but should not touch quotes. Use **View only** for stakeholders who only need to read reports.
## (Optional) Organize teams
Teams group users and link them to specific templates. When a user belongs to a team, they can only create quotes from the templates assigned to that team.
Create teams when you have:
* Multiple regions or business units with different catalogs
* Separate product lines with different pricing rules
* Groups of reps that should not have access to each other's templates
To create a team, go to **Settings** → **Users & Team** → **Team**, then click **New team**. Enter a **Name**, optionally add a **Reference**, and assign the **Templates** and **Users** that belong to this team.
You can skip teams for now if everyone works from the same catalog and templates. Teams are easy to add later once your workspace is configured.
## Invite users
Go to **Settings** → **Users & Team** → **User**.
Click **New** at the top right. The **New user** panel opens.
Type one or more addresses in the **Email** field. Separate multiple emails with a comma to invite several people at once.
Choose the **Licence** that matches what this user needs to do. See the table above for a summary of each licence's restrictions.
Choose the **Role** that reflects this user's responsibilities. See the table above for a summary of each role's access level.
Click **Save**. Qwoty sends an invitation email to each address automatically. The users appear in the list with invitation status `sent`.
Invitations sent. Each teammate receives an email to activate their account and set their password.
## Manage pending invitations
After sending invitations, track and manage them from the user list. The **Invitation status** column shows the current state for each user.
| Status | Meaning |
| ---------- | ---------------------------------------------------------- |
| `sent` | Invitation sent — user has not yet activated their account |
| `accepted` | User clicked the link and activated their account |
| `-` | No invitation was sent (user created directly) |
**To resend an invitation:** find the user in the list, click the **Open menu** (⋯) on their row, and select **Resend invitation**.
**To edit a pending user:** click their row to open their profile. You can update their name, licence, role, teams, and other details before they log in for the first time.
## What's next
Sync customers and deals from HubSpot, Salesforce, or Pipedrive.
Upload your logo, pick your colors, and brand your quotes.
# Configure your workspace
Source: https://docs.qwoty.io/getting-started/setup/workspace
Set workspace name, language, timezone, and unit system for your team
Workspace settings define the default configuration for all users in your Qwoty account. You can set the workspace name, default interface language, timezone, and unit system. Users can override language and format preferences in their individual profiles.
## Prerequisites
You must have the **Admin** role to access and modify workspace settings.
## Access workspace settings
In the left side menu, click **Settings**.
In the **Workspace** section, click **General**.
## Configure general settings
In the **Workspace name** field, enter the name of your workspace. This name is visible to all team members.
In the **Back-office language** dropdown, select the default language for the Qwoty interface. This applies to all users unless they override it in their profile.
The **Display format** field shows a preview of number and currency formatting based on the selected language. This field is automatically calculated and cannot be edited.
In the **Time zone** dropdown, select the reference timezone for your workspace. This affects timestamps in quotes, orders, and activity logs.
Choose the **Unit system** for your workspace:
* **Metric system**: kilometers, kilograms, liters
* **Imperial system**: miles, pounds, gallons
Click **Save** to apply your changes.
Each user can customize their language and display format in their profile. Workspace settings serve as default values for new users.
## Technical identifiers
At the bottom of the General settings screen, two read-only fields display unique identifiers:
* **Workspace ID**: Technical identifier of your workspace
* **Organization ID**: Technical identifier of your organization
These identifiers are required for Qwoty technical support requests and API integrations. You can copy and share them when needed.
## Related resources
Learn how to update individual user settings and preferences
Add users, assign roles, and organize your team structure
Set up API access and manage integration credentials
Configure currency, tax, and localization options
# Why Qwoty
Source: https://docs.qwoty.io/getting-started/why-qwoty
Understand why modern revenue teams choose Qwoty over legacy CPQ or lightweight quoting tools.
The way sales teams quote has barely changed in 20 years. Spreadsheets, email threads, and disconnected tools still drive most of the process — and they cost deals.
Qwoty is an AI-powered CPQ platform built to unify the full quote-to-revenue stream. It's designed for teams that need to move fast without sacrificing accuracy or margin control.
## The problems we solve
### Quotes take too long
Building a complex quote manually takes 2 to 3 hours per deal. Reps hunt for the right product, check pricing with Finance, format a PDF, and send it via email.
Qwoty eliminates that loop. Guided configuration, smart pricing, and pre-built templates let reps generate accurate quotes in minutes.
### Pricing errors and margin leakage
Manual pricing means mistakes — wrong discounts, outdated rates, or products priced below the floor. Those errors eat into margin before anyone notices.
Qwoty enforces pricing rules at the source. Pricebooks, approval workflows, and margin controls ensure every quote stays within your commercial policy.
### Poor visibility across the deal lifecycle
When quotes live in inboxes and spreadsheets, no one knows where deals stand. Sales managers can't spot what's stuck, and Finance can't forecast with confidence.
Qwoty gives every stakeholder structured data in one place — from the first quote through signed order.
### Friction between teams and buyers
Email attachments, version conflicts, and separate signature tools all slow the final stretch of a deal. Buyers lose context, and reps lose momentum.
Qwoty replaces that friction with a Dealroom where buyers review, comment, and sign in one place — and where every interaction is visible to your team in real time.
## How Qwoty is different
### End-to-end coverage
Most CPQ tools stop at the quote. Qwoty covers the full journey: Quote, Sales Agreement, E-sign, Order, and Dealroom. There's no hand-off to a separate signature tool or a manual step to generate the order — it's all connected.
### Fast adoption
Legacy CPQ platforms take 6 to 12 months to deploy. Qwoty is up and running in roughly one month. Configuration is built for Ops and Finance teams, not outside consultants.
### Native CRM integration
Qwoty connects natively with HubSpot, Salesforce, and Pipedrive. Your reps keep working where they already are — Qwoty syncs quotes, deals, and customer data automatically.
### AI-powered quote generation
Qwoty can extract quote requests from emails, PDFs, and Excel files to pre-populate quotes. It's one of several ways the platform accelerates the quoting process without adding manual steps.
## Who uses Qwoty
Qwoty is built for mid-market companies with 5 to 200 sales reps. The primary fit is manufacturing and retail/wholesale, where product complexity and pricing structures are high. SaaS, tech, and consulting teams use it too.
It works internationally — multi-currency, multi-language, and multi-business-unit setups are supported from day one.
"Thanks to Qwoty, within just three months, we cut our sales cycles in half and increased our conversion rate by 34%." — Jean Metzger, CEO
## Ready to start?
Create and send your first quote in minutes
Understand how Qwoty's entities connect
# Welcome to help center
Source: https://docs.qwoty.io/user-guide
Qwoty Help Center: guides, API references, and resources to succeed with your CPQ.
**New to Qwoty?** Follow our [quickstart guide](/getting-started/introduction)
to set up your workspace in minutes.
## Explore the documentation
How to use Qwoty day-to-day: quotes, catalog, contracts, and dealroom.
Set up your account, import your data, and create your first quote.
Complete REST endpoint documentation to integrate Qwoty into your systems.
Connect Qwoty to your CRM, ERP, messaging tools, and payment providers.
Import your customers, products, and pricing from your existing tools.
eIDAS, GDPR, electronic signature, and platform certifications.
## Popular topics
Build a quote step by step: products, options, discounts, and terms.
Define validation rules and escalation levels.
Products, options, bundles, pricebooks, and pricing types.
Objects, custom fields, and CRM mapping.
Generate a token, manage permissions, and secure your calls.
Receive Qwoty events in real time within your applications.
## Additional resources
All Qwoty and CPQ terms clearly explained.
Discover upcoming features and their timeline.
API limits, quotas, and usage best practices.
## Need more help?
Our team is available Monday to Friday, 9am-6pm CET.
How Qwoty protects and processes your personal data.
# Configure discounts
Source: https://docs.qwoty.io/user-guide/catalog/how-tos/configure-discounts
Add percentage or fixed amount discounts to products, bundles, or entire quotes
Discounts in Qwoty allow you to reduce pricing at the product, bundle, or quote level. You can apply percentage-based or fixed amount discounts, set approval thresholds, and configure visibility rules for your sales team.
This guide covers how to create and configure discount rules within your catalog.
## Prerequisites
* Admin or Manager role
* An active Catalog with products configured
* A Pricebook assigned to the Catalog (optional, depending on your pricing setup)
## Access the discount configuration
Navigate to **Products & pricing** → **Discount** in the left side menu to access the discount management screen.
## Configure a discount rule
Click **+ New** to open the **New discount** dialog.
Select the discount type:
* **Amount off products**: discount specific products or collections of products
* **Amount off order (one off product only)**: discount the total order amount
Click **Continue** to open the discount creation form. Enter a **Name** to identify the discount rule (e.g., `Q1 Volume Discount` or `Early Bird 10%`). Add an optional **Reference** for internal tracking.
In the **Discount Value** section:
* **Value Type**: Select **Percentage** (reduce price by a %) or a fixed amount option
* **Percentage** (required when Percentage is selected): enter the percentage value (e.g., `10` for 10% off)
In the **Conditions** section, configure which products the discount applies to:
* **Attribute**: choose the matching attribute (e.g., **Product**)
* **Operator**: set the comparison operator (e.g., **Equal**)
* **Values**: select the specific products
Click **+ Add condition** to add more conditions.
In the **Linked Resources** section, restrict the discount's availability:
* **Templates**: only available on specific quote templates
* **Customers**: only available for specific customers
* **Customer Segments**: only available for specific customer segments
Leave blank to make the discount available everywhere.
Use the **Start Date** and **End Date** fields in **General Information** for seasonal or time-limited discounts.
Click **Save** at the top right to create the discount.
The discount is now available for Sales Reps to apply when creating quotes.
## What happens next
Once configured, the discount appears in the discount selector when creating or editing quotes. If the discount requires approval and exceeds the threshold, the quote status changes to **Pending approval** and a Manager or Admin must review it before it can be published.
## Troubleshooting
Check that the discount is active and that the current date falls within any configured date range. Verify that your user role has permission to apply the discount.
Confirm that the approval threshold is configured correctly and that the discount value exceeds the threshold. Check that at least one Manager or Admin user is assigned to the sales team.
Review the discount scope settings. Ensure the correct products or bundles are selected if using product-level discounts. Quote-level discounts apply to all line items.
## Related resources
Learn how to apply discounts when building quotes
Understand how discount approvals route to managers
Set up products before applying discounts
Manage base pricing that discounts apply to
# Create a bundle
Source: https://docs.qwoty.io/user-guide/catalog/how-tos/create-bundle
Group multiple products together into a single sellable bundle
A bundle lets you package multiple products into a single item that your sales team can add to quotes. You can set bundle-level pricing, define product quantities, and apply rules to control configuration.
You must have the **Admin** role to create and manage bundles.
## Access the bundle creation screen
In the left side menu, click **Products & pricing**.
Click **Bundle** in the submenu.
Click **+ New** to open the bundle creation form.
## Configure the bundle
In the **General Information** section:
* **Name** (required): Enter a descriptive name for the bundle (e.g., `Starter Pack`, `Enterprise Suite`)
* **Reference**: Add an internal reference code to identify the bundle
* **Description** (optional): Add details about what the bundle includes
In the **Bundle** section, use the available buttons to add content:
* Click **+ Product section** to add a section of standard products
* Click **+ Plan section** to add a section of plan-type products
In the **Bundle used in** section, use the **Catalogs** field:
1. Click to open the catalog selector
2. Select one or more catalogs where this bundle should be available
3. The selected catalogs appear as tags
A bundle must be associated with at least one catalog to appear in quote templates.
If you integrate with external systems, enter the corresponding identifiers:
* **CRM ID**: Bundle identifier in your CRM
* **ERP ID**: Identifier in your ERP system
* **Accounting ID**: Identifier in your accounting software
In the right-hand side panel:
* **Status** (required): Set to **Active** to enable the bundle or **Inactive** to disable it
* **API Name** (required): Automatically generated technical identifier
The **API name** can only be changed at creation. Once saved, it cannot be modified to ensure integration stability.
Click **Save** at the top right to create the bundle.
## What happens next
Once saved, the bundle appears in the bundles list and becomes available in any associated catalogs. Sales reps can add the bundle to quotes using templates that reference those catalogs.
The **Activity** panel in the side area tracks all changes made to the bundle, including creation, modifications, and status changes.
## Troubleshooting
Verify that the bundle is associated with the same catalog as the template. Check that the bundle status is set to **Active** and that validity dates include the current date.
The API name is locked after the first save to protect integrations. If you need a different API name, create a new bundle and archive the old one.
By default, bundle products use fixed quantities. To allow sales reps to adjust quantities, enable quantity overrides in the bundle configuration rules.
## Related resources
Create and configure individual products
Organize products into catalogs
Set up pricing rules for catalogs
Build configurable products with rules
# Create a pricebook
Source: https://docs.qwoty.io/user-guide/catalog/how-tos/create-pricebook
Set up a new pricebook to define pricing for products in a catalog
A pricebook defines the prices applied to products in a catalog. You can create multiple pricebooks for different customer segments, currencies, or pricing strategies.
## Prerequisites
You must have the **Admin** role to create and manage pricebooks.
## Access the pricebook screen
In the left side menu, click **Products & pricing**.
Click **Pricebook** in the submenu.
Click **+ New pricebook** to open the creation dialog.
## Configure the pricebook
In the **Create a new pricebook** dialog:
* **Pricebook type** (required): Select **Fixed** (use set prices from the linked catalog) or **Dynamic** (automatically calculate prices using rules based on a base pricebook)
* **Catalog** (required): Select the catalog this pricebook applies to. Each pricebook must be linked to exactly one catalog.
* **Currency** (required): Choose the currency (e.g., EUR, USD, GBP)
Click **Continue** to open the full pricebook form.
In the pricebook form, enter:
* **Name**: Descriptive name for the pricebook (e.g., "Enterprise Pricing", "EMEA Standard Rates")
* **Reference**: Internal reference code to identify the pricebook
Define prices for products in the catalog. The list displays **Product**, **Variant**, **Reference**, **Type** (One off or Recurring), and **Price**.
If you integrate with external systems, fill in the relevant IDs:
* **CRM ID**: Pricebook identifier in your CRM
* **ERP ID**: Identifier in your ERP
* **Accounting ID**: Identifier in your accounting software
In the right-hand side panel:
* **Status**: Toggle to **Active** to enable the pricebook or **Inactive** to disable it
* **API name**: Automatically generated technical identifier for API integrations
The **API name** can only be changed during creation. Once saved, it becomes permanent to ensure integration stability.
Click **Save** at the top right to create the pricebook.
## What happens next
Once saved, the pricebook becomes available for selection in templates and customer configurations. If set to **Active**, it will apply pricing when sales reps create quotes using the associated catalog.
You can view where the pricebook is used in the **Pricebook used in** section, which displays linked templates, customers, and customer segments.
## Troubleshooting
Ensure the catalog is created and set to **Active** status. Only active catalogs appear in the dropdown. Navigate to **Products & pricing** > **Catalog** to verify.
Check that:
* The pricebook status is set to **Active**
* The pricebook is associated with the correct catalog
* The template or customer is configured to use this pricebook
* Validity dates (if set) include the quote creation date
Create a separate pricebook for each currency. You cannot mix currencies within a single pricebook. Link each to the same catalog if needed.
## Related resources
Edit, archive, and organize your pricebooks
Set up a catalog to group products
Add and configure products in your catalog
Build a quote template using your pricebook
# Create a product
Source: https://docs.qwoty.io/user-guide/catalog/how-tos/create-product
Add a new product to your catalog with pricing, billing, and configuration details.
Creating a product allows you to define items that sales reps can add to quotes. You can set billing type, pricing details, variants, and associate the product with one or more catalogs.
## Prerequisites
You must have the **Admin** role to create products.
## Access the product creation screen
In the left side menu, click **Products & pricing**.
Click **Products**.
Click **+ New** to open the **New product** dialog. Select **One off product** (for a one-time payment) or **Recurring product** (for subscription billing), then click **Create** to open the creation form.
## Fill in general information
In the **General Information** section, enter the following details:
* **Name** (required): Product name (e.g., "Professional License", "Premium Support")
* **Reference**: Internal reference to identify the product
* **Description** (optional): Short description visible to sales reps and customers
* **Primary image** (optional): Upload a product image
* **Product Category** (optional): Assign to a category for catalog organization
Choose the billing type carefully when opening the New product dialog—it determines how the product appears in quotes and how revenue is recognized.
## Configure advanced settings
Expand **Advanced settings** to set additional attributes:
* **Product type**: Further classification of the product (e.g., software, service)
* **Unit of measure**: The unit used for this product
* **Units per pack**: Number of units included per pack
* **Language**: Language for the product content
## Associate the product with catalogs
In the **Linked catalogs** section:
1. Click the **Catalog** field to open the list of available catalogs
2. Select one or more catalogs to associate with this product
3. The selected catalogs appear as tags
A product must be associated with at least one catalog to appear in quote templates using that catalog.
## Enter external identifiers
If you integrate with external systems (CRM, ERP, accounting), enter the corresponding identifiers:
* **CRM ID**: Product identifier in your CRM
* **ERP ID**: Identifier in your ERP
* **Accounting ID**: Identifier in your accounting software
External IDs are optional but recommended for bidirectional sync with other platforms.
## Define status and API name
In the right-hand side panel:
* **Active** (required): Set to **Active** to make the product available for quotes, or **Inactive** to hide it
* **API name** (required): Automatically generated technical identifier based on the product name
The **API name** can only be changed at creation. Once saved, it cannot be modified to ensure integration stability.
## Save the product
Click **Save** at the top right to create the product.
## What happens next
Once saved, the product becomes available in catalogs and can be added to quotes. To set pricing, you must create or update a pricebook associated with the catalog.
## Troubleshooting
Verify that the product is associated with the catalog used by your template. Check the **Collections** block and add the relevant catalog if needed.
The API name is locked after creation to maintain integration consistency. If you need a different API name, create a new product and archive the old one.
Pricing is managed separately in pricebooks. Navigate to **Products & pricing** > **Pricebook**, then add or edit a pricebook associated with your catalog.
## Related resources
Edit, archive, and organize your product library
Group products into catalogs for specific markets or segments
Define pricing for products in a catalog
Package multiple products together as a bundle
# Manage variants
Source: https://docs.qwoty.io/user-guide/catalog/how-tos/manage-variants
Create and organize product variants with different attributes like size, color, or configuration options
Product variants let you offer the same base product with different options such as size, color, tier, or billing frequency. Each variant has its own SKU, price, and inventory tracking while sharing common product details.
You can create variants manually or generate them automatically from attribute combinations. Variants appear as selectable options when sales reps add the product to a quote.
## Prerequisites
* **Admin** or **Manager** role
* At least one product in your Catalog
## Access the variants section
Navigate to **Products & pricing** → **Products** → select a product → **\[VARIANTS TAB]**.
## Create variants manually
Click **\[ADD VARIANT BUTTON]**.
Fill in the required fields:
* **\[VARIANT NAME FIELD]**: descriptive name (e.g., "Premium - Monthly")
* **SKU**: unique identifier for this variant
* **Price**: base price for this variant
* **\[ATTRIBUTE FIELDS]**: custom attributes like size, color, or tier
Click **\[SAVE BUTTON]** to add the variant to the product.
## Generate variants from attributes
Click **\[GENERATE VARIANTS BUTTON]**.
Choose which attributes to use for variant generation (e.g., Size: Small, Medium, Large; Color: Red, Blue).
Qwoty creates all possible combinations automatically.
Review the generated variants. Update pricing or remove unwanted combinations before saving.
Click **\[SAVE ALL BUTTON]** to create the variants in your catalog.
Generating variants from 3 sizes and 4 colors creates 12 unique variants. Plan your attribute structure before generating to avoid duplicate cleanup.
## Edit an existing variant
Click the **\[EDIT ICON]** next to any variant in the list. Update the fields and click **\[SAVE BUTTON]** to apply changes.
Changes to variant pricing or attributes do not affect existing quotes. Only new quotes reflect the updated configuration.
## Delete a variant
Click the **\[DELETE ICON]** next to the variant you want to remove. Confirm the deletion in the modal.
You cannot delete a variant that appears in any existing quote, even if the quote is in Draft status. Archive the variant instead to hide it from new quotes.
## What happens next
Once you create variants, sales reps see them as selectable options when adding the product to a quote. The selected variant's SKU and price automatically populate the quote line item.
If you're using the Product Configurator, you can map variants to visual configuration rules for an interactive selection experience.
## Troubleshooting
Verify that:
* The product is active in your Catalog
* The Pricebook includes the product
* At least one variant is active and has a price defined
The variant may be locked because it appears in an existing quote. Check the **\[USAGE TAB]** to see where the variant is referenced.
Archive the variant to prevent new usage while preserving historical data.
Delete variants individually or use **\[BULK DELETE OPTION]** to select multiple variants at once. Consider using fewer attributes or attribute values to simplify your catalog.
## Related resources
Add products to your Catalog before creating variants
Build visual configuration rules for variant selection
Learn how Pricebooks apply pricing to product variants
Use variants when building quotes for customers
# Build a product configuration
Source: https://docs.qwoty.io/user-guide/catalog/how-tos/product-configurator
Create a configurable product with option groups, rules, and formulas.
## Introduction
This guide walks you through building a product configuration from scratch: attaching it to a base product, adding the three group types, setting up rules and formulas, and getting it ready for your sales reps.
## Prerequisites
Before you start, make sure you have:
* A base product already created in your catalog.
* A pricebook set up if you plan to use the **Inherited from pricebook** price mode for any option.
* Access to the **Products & pricing** section of your workspace.
## Access
From the left sidebar, go to **Products & pricing > Product Configuration**.
## Steps
Click **+ New configuration**. In the **New configuration** dialog, choose the **Product** you want to configure, then click **Continue**.
Click the pencil icon next to the configuration's name to open **General information**. Here you can set:
* **Name** — the configuration's display name.
* **Internal reference** — an optional reference for your team.
* **Base product** — the catalog product this configuration is attached to.
* **Hide price details** — turn this on to hide every option's price from the quote and DealRoom.
In the **Configuration** panel, click **+ Add**, then choose **Item**. Set a **Selection type** (Radio buttons, Checkboxes, Dropdown, Multi-select, or one of the two card layouts), and turn on **Required** if a choice is mandatory.
Add options to the group — each one is either a catalog product or a custom attribute. For each option, set:
* **Price**: Inherited from pricebook, Formula, or Fixed amount.
* **Quantity**: Same as base product, Formula, or Fixed amount.
Click **+ Add**, then choose **Question**. Pick a **Selection type** — in addition to the Item group types, Question groups support Toggle, Number input, Free text, and Date.
If you want the answer stored somewhere, set **Save as** to **Custom field** and pick the field. Leave it on **None** if the answer is only used to drive rules.
Click **+ Add**, then choose **Calculated**. Give the group a name, then click **Define formula** on its first calculated item to open the **Formula editor**.
Build your expression using the ARITHMETIC, COMPARISON, and FUNCTIONS buttons, and insert variables from **INSERT VARIABLE** instead of typing them by hand. A green border and "Valid formula" message confirm the expression is correct.
Set a **Display format** (for example, Number), and turn off **Display calculated field** if you only need the value as an input to other formulas. Add more calculated items to the same group if you need several related values.
Select a group or an option, then click **Add rule** in its **Rules** section. In the **Edit rule** dialog:
1. Build your **IF** condition — pick a field, an operator (`is`, `is not`, `>`, `<`, `>=`, `<=`), and a value. Use **+ AND** or **+ OR** to add more conditions.
2. Choose the **THEN** action, and optionally an **ELSE** action.
3. Click **Save**.
Switch to **Raw text** if you prefer to write the condition as an expression directly. Available actions depend on what the rule is attached to: groups support Show, Hide, Warning banner, and Error banner; Item group options support Show, Hide, Required, and Exclude; Question group options support Show and Hide.
A rule can only reference groups that come before it in the order, so build your groups top to bottom before wiring up rules between them.
Drag a group by its handle (⠿) in the **Configuration** panel to move it up or down. Reordering changes which groups are "upstream" and available to rules and formulas in the groups below.
Use the live preview in the center panel to test your selections, rules, and calculated values exactly as a sales rep would see them. Adjust any group, option, rule, or formula until the behavior matches what you expect.
Once everything looks right, save your changes. Your configuration shows as **Active** in the Product Configuration list, which makes it available to add to quotes.
## What happens next
Once a configuration is active, sales reps can add its base product to a quote and open the configurator to make selections. Their choices generate the matching line items, with prices and quantities calculated automatically. If the quote reaches a DealRoom, the customer can adjust any option you've marked optional or made editable.
## Troubleshooting
Check that every variable was inserted using **INSERT VARIABLE** rather than typed by hand, and that parentheses and quotes are balanced. The formula editor only turns green once the expression is valid.
Confirm the group or option the rule depends on comes before the rule's own group in the order. Rules can't reference anything downstream.
Custom attribute options can't use the **Inherited from pricebook** mode, since there's no catalog product to inherit from. Switch to **Formula** or **Fixed amount**.
Remember the resolution order: Hide always overrides Show, and Required always overrides Exclude.
Check the **Display calculated field** setting for that formula. If it's turned off, the value is only available as a variable for other formulas, not shown directly.
## Related
Full reference for group types, rules, formulas, and DealRoom behavior.
Group a fixed set of products under one price.
Manage the prices that configured products can inherit from.
See how customers interact with a configured product online.
# Catalog and Products
Source: https://docs.qwoty.io/user-guide/catalog/introduction
Organize your products into catalogs and manage your commercial offering
This section covers how to structure your product catalog in Qwoty. You'll learn how to create and manage catalogs, configure products, bundle them together, and define pricing rules.
## Reference
Create and manage catalogs to organize products by geography, segment, or product line
Add, configure, and manage individual products in your catalogs
Create product bundles to sell multiple items together as a package
Build configurable products with dynamic options and business rules
## Guides
Step-by-step guide to creating and configuring a new catalog
Use CSV import to add multiple products at once
Learn how to package products together for bundled offers
Use the Product Configurator to build products with custom options
## Key concepts
**Catalog** groups your products in a logical way—by country, product range, or customer segment. Each catalog links to one or more pricebooks to control pricing.
**Standard catalog** is created automatically in every workspace. You cannot delete it, and all products remain part of this catalog. You can rename it and edit its reference.
**Products** are the items you sell. Add them to catalogs, configure their attributes, and control their availability. Products can exist in multiple catalogs simultaneously.
**Bundles** combine multiple products into a single package. You can apply special pricing rules and sell them as a unit.
You must have the **Admin** role to create and manage catalogs. Sales Reps can view catalogs and add products to quotes.
# Catalog vs Pricebook vs Bundle
Source: https://docs.qwoty.io/user-guide/catalog/reference/catalog-vs-pricebook-vs-bundle
Understand the differences between catalogs, pricebooks, and bundles and how they work together in Qwoty.
Catalogs, pricebooks, and bundles are the building blocks of your product offering in Qwoty. They work together but serve distinct purposes: catalogs organize products, pricebooks define pricing, and bundles package multiple products for sale.
Understanding how these three concepts relate helps you configure flexible, scalable pricing strategies.
## What each concept does
**Catalog** groups products into a single collection. You use catalogs to organize products by market, region, or business unit. Example: "France Catalog" or "Premium Catalog."
**Pricebook** defines the prices for products in a catalog. Multiple pricebooks can apply to the same catalog, enabling segment-specific pricing. Example: "Enterprise Pricebook" and "SMB Pricebook" for the same catalog.
**Bundle** packages multiple products together for sale as a single item. Bundles let you sell preconfigured sets at a fixed or discounted price. Example: "Starter Pack" containing three products.
## How they work together
```mermaid theme={null}
graph TD
A[Catalog] -->|contains| B[Products]
A -->|has one or more| C[Pricebooks]
C -->|defines prices for| B
B -->|can be grouped into| D[Bundles]
E[Template] -->|uses| A
E -->|applies| C
```
A **Catalog** holds your products. A **Pricebook** assigns prices to those products. A **Bundle** combines products from the catalog into a package. When you create a quote template, you select a catalog and the applicable pricebook.
## Key differences
| Aspect | Catalog | Pricebook | Bundle |
| ----------------- | ------------------------------ | ---------------------------------- | ----------------------------- |
| **Purpose** | Organize products | Define pricing | Package products |
| **Scope** | Contains many products | Applies to one catalog | Contains selected products |
| **Multiplicity** | One catalog per template | Multiple pricebooks per catalog | Many bundles per catalog |
| **Main use case** | Segment by geography or market | Segment by customer tier | Simplify product selection |
| **Contains** | Products, configured products | Prices, currencies, discount rules | Multiple products sold as one |
## Catalog details
Catalogs group products and configured products into collections. You define validity dates, associate pricebooks, and track usage across templates and customers.
**Key fields:**
* **Name**: Display name (e.g., "France Catalog", "Premium Catalog")
* **Reference**: Internal identifier
* **Valid from / Valid until**: Optional date range for seasonal or promotional catalogs
* **Status**: Active or Inactive
**Where catalogs are used:**
* **Template**: Quote templates using this catalog
* **Customer**: Customers assigned this catalog
* **Customer segment**: Segments associated with this catalog
Click on items in the **Catalog used in** block to navigate directly to related templates, customers, or segments.
## Pricebook details
Pricebooks set the prices for products in a catalog. You can create multiple pricebooks per catalog to support different customer segments, currencies, or pricing models.
**Key attributes:**
* **Status**: Active or Inactive
* **Type**: Pricing model (**Fixed** or **Dynamic**)
* **Currency**: EUR, USD, or other currencies
* **Reference**: Internal pricebook identifier
The **Related pricebooks** section in the catalog editor displays all pricebooks linked to that catalog. You manage individual prices within each pricebook, not directly in the catalog.
Use separate pricebooks for different customer tiers (Enterprise, SMB) or regions (EMEA, AMER) while sharing the same catalog.
## Bundle details
Bundles package multiple products from a catalog into a single sellable item. You define which products are included, set bundle pricing, and optionally allow customization.
Bundles simplify the sales process by reducing the number of line items on a quote. Instead of adding five individual products, a sales rep adds one bundle.
**Common bundle use cases:**
* Starter packs for new customers
* Seasonal promotional offers
* Preconfigured solutions for specific industries
Products in a bundle must already exist in the catalog. You cannot bundle products from different catalogs.
## Common questions
No. Each pricebook is tied to a single catalog. If you need the same pricing across catalogs, create separate pricebooks with identical pricing.
Disabling a catalog prevents it from being used in new templates or quotes. Existing quotes continue to reference the catalog, but sales reps cannot select it for new quotes.
No. Bundles can only contain individual products or configured products, not other bundles.
Validity dates control when a catalog is available for use. Sales reps cannot select a catalog outside its validity range. Use this for seasonal catalogs or time-limited promotions.
Create the catalog first, then associate pricebooks. You need a catalog to define which products the pricebook will price.
## Related
Step-by-step guide to creating and configuring catalogs
Create and configure pricebooks for different customer segments
Package products together into bundles
Build configurable products with rules and dependencies
# Pricing types
Source: https://docs.qwoty.io/user-guide/catalog/reference/pricing-types
Learn about the different pricing models available for products in Qwoty
Qwoty supports multiple pricing types to accommodate different sales models and billing scenarios. Each pricing type determines how the unit price is calculated and displayed to customers in quotes and the Dealroom.
You configure the pricing type at the product level. The pricing type controls whether the price is fixed, volume-based, tiered, or calculated dynamically.
## Available pricing types
Qwoty supports the following pricing types:
* **Flat**: Single fixed price per unit, regardless of quantity
* **Volume**: Unit price decreases based on total quantity purchased
* **Tiered**: Different prices apply to quantity ranges within a single order
* **Per-unit**: Price varies per individual unit based on configuration
* **Custom formula**: Price calculated using a custom expression with variables
The pricing type is set when you create a product. You can change it later, but existing quotes will retain the original pricing logic.
## How pricing types compare
| Pricing type | Use case | Price calculation | Example |
| ------------------ | ---------------------------------------------------------------- | ----------------------------------------- | --------------------------------------------------------- |
| **Flat** | Standard products with consistent pricing | Fixed price × quantity | Software license at \$100/month |
| **Volume** | Bulk discounts where all units get the same lower price | Entire quantity uses one bracket price | 1-10 units = $50 each, 11-50 units = $40 each (all units) |
| **Tiered** | Progressive pricing where different ranges have different prices | Each tier priced separately | First 10 units = $50, next 40 units = $40 |
| **Per-unit** | Configurable products with variable costs | Sum of base price + configuration options | Base server $1000 + RAM upgrade $200 |
| **Custom formula** | Complex pricing with dependencies | User-defined calculation | `base_price * quantity * (1 - discount_tier)` |
## Flat pricing
Flat pricing applies a single unit price to all quantities. This is the simplest and most common pricing model.
The total price is calculated as: **unit price × quantity**
Use flat pricing for:
* Standard catalog items with fixed prices
* Products without volume discounts
* Simple subscription plans
## Volume pricing
Volume pricing applies a single discounted unit price to all units based on the total quantity ordered. All units receive the same price from the applicable volume bracket.
When you configure volume pricing, you define quantity ranges and corresponding unit prices. The system selects the bracket that matches the total quantity and applies that unit price to all units.
**Example:**
* 1-10 units: \$50 per unit
* 11-50 units: \$40 per unit
* 51+ units: \$30 per unit
If a customer orders 25 units, all 25 are priced at $40 each (total: $1,000).
Volume pricing differs from tiered pricing. With volume pricing, the entire order uses one price. With tiered pricing, different portions of the order use different prices.
## Tiered pricing
Tiered pricing applies different unit prices to different quantity ranges within a single order. Each tier is priced independently.
When you configure tiered pricing, you define quantity ranges with corresponding unit prices. The system calculates the price for each tier separately and sums them.
**Example:**
* First 10 units: \$50 per unit
* Next 40 units (11-50): \$40 per unit
* Units beyond 50: \$30 per unit
If a customer orders 25 units:
* First 10 units: 10 × $50 = $500
* Next 15 units: 15 × $40 = $600
* **Total: \$1,100**
Use tiered pricing for:
* Usage-based billing (e.g., API calls, storage)
* Progressive volume discounts
* Utility-style pricing models
## Per-unit pricing
Per-unit pricing calculates the price based on individual unit configuration. This pricing type is used with the Product Configurator.
The base price starts with a foundation amount, then adds or subtracts values based on selected options, features, or attributes.
**Example:**
A configurable laptop:
* Base model: \$1,000
* RAM upgrade (16GB → 32GB): +\$200
* Storage upgrade (512GB → 1TB): +\$150
* **Total per unit: \$1,350**
Use per-unit pricing for:
* Highly configurable products
* Products with multiple optional features
* Custom builds or assemblies
## Custom formula pricing
Custom formula pricing uses a user-defined expression to calculate the final price. You can reference product fields, quote variables, and customer attributes in the formula.
Formulas support:
* Arithmetic operations (`+`, `-`, `*`, `/`)
* Conditional logic
* Quote-level variables (e.g., `quote.discount_percentage`)
* Product attributes (e.g., `product.base_cost`)
* Customer fields (e.g., `customer.segment`)
**Example formula:**
```
base_price * quantity * (1 - customer.volume_discount) + setup_fee
```
Custom formulas are powerful but can make quotes harder to audit. Document your formulas clearly and test edge cases.
Use custom formula pricing for:
* Complex pricing logic not covered by standard types
* Industry-specific calculations
* Dynamic pricing based on multiple variables
## Pricing type and Pricebooks
Pricing types work alongside Pricebooks. The Pricebook defines which products are available and their base prices for a specific customer segment or region. The pricing type controls how those base prices scale with quantity or configuration.
When you create a Pricebook entry:
1. Select the product (which has an assigned pricing type)
2. Set the base unit price or volume brackets
3. The product's pricing type determines how the base price is applied
You can use the same product with different pricing types across multiple Pricebooks. For example, Enterprise customers might see volume pricing while SMB customers see flat pricing.
## Common questions
No. Each product uses one pricing type at a time. However, you can use Bundles to combine multiple products with different pricing types into a single offer.
Existing quotes retain the pricing logic that was active when they were created. Only new quotes will use the updated pricing type. This prevents retroactive changes to quoted prices.
Yes. The Dealroom automatically displays tiered pricing breakdowns when applicable, so customers can see how their total is calculated across tiers.
Volume pricing is calculated per product within the Bundle, not on the Bundle as a whole. If you need volume discounts at the Bundle level, use custom formula pricing.
Yes. When creating or updating products in bulk, you can include tiered or volume pricing data in your import file.
## Related
Learn how to create and configure products
Manage price lists for different customer segments
Create product packages with combined pricing
Build configurable products with dynamic pricing
# Product configurator
Source: https://docs.qwoty.io/user-guide/catalog/reference/product-configurator
Reference for building configurable products with option groups, rules, and formulas.
## Overview
The product configurator turns a single catalog product into a product with options. You attach a configuration to a base product, then add ordered **groups** that let the sales rep or the customer choose products, answer questions, or see calculated values.
As selections are made, rules show, hide, require, or exclude elements in real time, and formulas recalculate price, quantity, and any calculated fields. The result is a validated set of line items added to the quote.
Use the product configurator when a product has many interdependent options. For a fixed set of products sold together at one price, use a [Bundle](/user-guide/catalog/reference/catalog-vs-pricebook-vs-bundle) instead.
## How it works
A configuration is always attached to one base product. You add **groups** to it in order, and each group contains either selectable options or a calculated value. Some groups can stay hidden until a rule reveals them.
```mermaid theme={null}
flowchart TD
A[Base product] --> B{Has variants?}
B -->|Yes| C[Variant selection step]
B -->|No| D[Group A]
C --> D
D --> E[Group B]
E --> F[Group C, D, E...]
F --> G[Rules evaluate in real time]
G --> H[Price and quantity recalculate]
H --> I[Validate and add to quote]
```
If the base product has variants, the sales rep selects a variant before seeing any group. From there, groups appear in the order you defined (A, B, C, and so on). Each time a selection changes, only the rules and formulas in groups **below** the one that changed are re-evaluated.
## The 3 group types
Add a group from the **Configuration** panel by clicking **+ Add**, then choosing one of three types.
| Group type | Purpose | Generates a quote line |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------- |
| **Item** | The sales rep or customer chooses one or more catalog products or custom attributes. | Yes |
| **Question** | The sales rep or customer answers a question. The answer can drive rules and formulas, and can optionally be saved to a field. | No |
| **Calculated** | Shows one or more values computed automatically from a formula. No options to select. | No, unless the value is saved to a field |
An Item or Question group lets you pick a **selection type**: Radio buttons, Checkboxes, Dropdown, Multi-select, Cards (single choice), or Cards (multiple choice). Question groups add three more: Toggle, Number input, Free text, and Date.
A Calculated group can hold more than one formula. Each formula has its own name, display format, and visibility, so one group can show several related values (for example, a chassis weight and a total weight).
## Option types
Inside an **Item** group, each option is one of:
* **Catalog product** — linked to an existing product. Its price can inherit from the pricebook.
* **Custom attribute** — a choice with no underlying catalog product (for example, "Standard finish" with no separate line item value). It has its own name, image, and description, and cannot use the pricebook-inherited price.
Inside a **Question** group, each option is a possible answer with a name and a value, plus an optional image for card-style display.
Every catalog product or custom attribute option configures its own price and quantity:
| Setting | Modes |
| ------------ | ----------------------------------------------- |
| **Price** | Inherited from pricebook, Formula, Fixed amount |
| **Quantity** | Same as base product, Formula, Fixed amount |
A formula-based price or quantity can be a flat value (`350`) or an expression that references other parts of the configuration (`[Price] * 0.35`). Each option can also be marked editable by the sales rep, and its price details can be hidden from the quote.
## Rules
A rule follows the pattern **IF \[condition] → THEN \[action]**, with an optional **ELSE** action. Rules attach to a group or to an individual option, and they evaluate in real time as selections change.
A rule can only reference groups that come **before** it in the order. A rule on group D can use groups A, B, and C, but never E or later.
### Actions
| Action | Available on groups | Available on options |
| ------------------------------------------ | ------------------- | ----------------------------- |
| **Show** | Yes | Yes |
| **Hide** | Yes | Yes |
| **Required** | No | Yes (Item group options only) |
| **Exclude** | No | Yes (Item group options only) |
| **Alert** (Warning banner or Error banner) | Yes | No |
### Examples
| Rule | Effect |
| ------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| IF **Power supply** is `480V` THEN Show **Transformer** | The transformer group only appears for 480V installations. |
| IF **Environment** is `Outdoor` THEN Required **Stainless steel chassis** | Stainless steel is forced for outdoor installations. |
| IF **Chassis** is `Carbon` THEN Exclude **Engraving** | Engraving is grayed out when carbon is chosen, since it isn't compatible. |
### Conflict resolution
When two rules target the same element with conflicting actions:
* **Hide** wins over **Show**.
* **Required** wins over **Exclude**.
## Formulas
A formula is an expression that produces a number. The formula editor offers three contexts:
| Context | Used for | Result |
| -------------- | ----------------------------------- | ------------------------------------------------------------------------- |
| **Unit price** | An option's price | A currency amount |
| **Quantity** | An option's quantity | A whole number |
| **Calculated** | A value shown in a Calculated group | A number, currency amount, or percentage, depending on the display format |
### Variables
Click **INSERT VARIABLE** in the formula editor to insert a variable without typing it manually.
| Source | Variables |
| ------------------- | --------------------------------------------------------------------------------------------------- |
| **Base product** | Price, Quantity |
| **Option group** | Value (the current selection), Total (price × quantity for the whole group), Price, Quantity, Count |
| **Specific option** | Selection, Total, Price |
### Functions
| Function | Description |
| ---------------------------------------------- | --------------------------------------------------- |
| `IF(condition, value if true, value if false)` | Returns one of two values depending on a condition. |
| `AND(a, b, ...)` | True only if every condition is true. |
| `OR(a, b, ...)` | True if at least one condition is true. |
| `ROUND(value, decimals)` | Rounds a number to a given number of decimals. |
There's no separate "sum" function. To total the prices or quantities across an option group, insert that group's **Total** variable.
### Examples
| Formula | Result |
| ------------------------------------------------------------- | ---------------------------------------------------------------- |
| `350` | A flat surcharge of 350. |
| `[Price] * 0.35` | 35% of the base product's price. |
| `IF([Selection] = "Premium chassis", [Price] * 0.9, [Price])` | A 10% discount when the premium chassis is selected. |
| `ROUND([Total] + [Total], 1)` | The combined total of two option groups, rounded to one decimal. |
## DealRoom behavior
When a configured product reaches the DealRoom, your customer sees the same groups and selections as the sales rep, with a few differences:
* Sales tips are hidden — they're for your team only.
* Calculated values marked as hidden stay hidden.
* An option marked optional can be added or removed by the customer.
* An option marked preselected starts checked, but the customer can uncheck it if it's also optional.
* An option with an editable quantity lets the customer adjust it within the configured minimum and maximum.
If none of these settings apply to an option, the customer sees it as read-only.
## When to use Configurator vs Bundle
| | Product configurator | Bundle |
| ----------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------- |
| Best for | Products with many interdependent options, conditional logic, and calculated values | A fixed or lightly customizable set of products sold together |
| Pricing | Per-option, often formula-driven | Usually a package price with simple add-ons |
| Conditional rules | Yes — show, hide, require, exclude, alert | No |
| Calculated fields | Yes | No |
| Setup effort | Higher — suited to complex, configurable products | Lower — suited to simple groupings |
## Common questions
No. Rules and formulas can only reference groups that come before them in the order. If you need the opposite relationship, move the referenced group earlier.
Hide always wins over Show, and Required always wins over Exclude.
No. Question groups never create a quote line on their own. They collect an answer that can drive rules and formulas, and can optionally be saved to a field.
Yes. A Calculated group can hold multiple formulas, each with its own name, display format, and visibility.
Yes. Turn off the calculated field's display setting to use it only as an internal variable for other formulas, without showing it on the configuration screen.
## Related
Step-by-step guide to creating a configuration from scratch.
Group a fixed set of products under one price.
Manage the prices that configured products can inherit from.
See how customers interact with a configured product online.
# Variants and options
Source: https://docs.qwoty.io/user-guide/catalog/reference/variants-and-options
Configure product categories, variant options, and billing frequencies to structure your catalog.
Product options centralize three types of configurations that structure your catalog: product categories, variant options, and billing frequencies. You define these options once and reuse them across products.
## Product categories
Categories group similar products into a hierarchical tree structure. You can create parent categories and subcategories to organize your catalog for easier browsing and management.
**Key attributes:**
* **Name**: Category label
* **Parent category**: Optional parent to create nested subcategories
* **Description**: Category details
* **Status**: Active or Inactive
### Access categories
1. Click **Products & pricing** in the left menu
2. Click **Product options**
3. Click **Manage** in the **Setup Product category** block
### Create a category
Click **New** at the top right.
* **Name**: Enter the category name
* **Parent category**: (Optional) Select an existing category to create a subcategory
* **Description**: Add details about the category
In the right panel, set **Status** to Active or Inactive, then click **Save**.
Create hierarchical structures for easier navigation. Example: Electronics → Computers → Laptops.
### Edit a category
1. Click the **⋮** menu next to the category
2. Select **Edit**
3. Make your changes
4. Click **Save**
## Variant options
Variant options define the variable characteristics of products. Each option (e.g., Color) contains multiple values (e.g., Red, Blue, Green). You use these options when creating product variants.
**Key attributes:**
* **Name**: Option name (Color, Size, Material)
* **Value**: List of possible values with name and API name
### Access variant options
From the **Product options** page, click **Manage** in the **Setup Product variant options** block.
### Create a variant option
Click **New** at the top right.
Enter the option **Name** (e.g., Color, Size, Material).
In the **Option** block, add values:
* **Name**: Value label (e.g., Red, Blue)
* **API Name**: Technical identifier
Click **Add an option** to add more values.
Click **Save** to create the option.
Example: Create a "Size" option with values S, M, L, XL. Associate this option with apparel products to generate variants automatically.
### Edit a variant option
1. Click the **⋮** menu next to the option
2. Select **Edit**
3. Add, edit, or delete values
4. Click **Save**
Deleting an option value that's already used by existing variants may impact those products.
## Billing frequencies
Billing frequencies define recurring intervals for subscription products. You configure these frequencies once and select them when setting up product pricing.
**Key attributes:**
* **Period**: Number of units (1, 2, 3, 6, 12)
* **Unit**: Time unit (Day, Week, Month, Year)
### Access billing frequencies
From the **Product options** page, click **Manage** in the **Setup Billing Frequency** block.
### Create a billing frequency
Click **New** at the top right.
* **Period**: Enter the number of units
* **Unit**: Select Day, Week, Month, or Year
Click **Save** to create the frequency.
**Common examples:**
* Monthly: Period = `1`, Unit = Month
* Quarterly: Period = `3`, Unit = Month
* Annual: Period = `1`, Unit = Year
* Bimonthly: Period = `2`, Unit = Month
### Edit a billing frequency
1. Click the **⋮** menu next to the frequency
2. Select **Edit**
3. Make your changes
4. Click **Save**
## Common questions
Yes. Select a parent category when creating a new category to build nested subcategories. This creates a hierarchical tree structure.
Deactivated categories remain in the system but won't appear when filtering or browsing the catalog. Products already assigned to that category retain the assignment.
There's no hard limit, but keep the number manageable. Too many values can make variant creation complex and slow down product configuration.
Yes. That's the purpose of variant options. Define an option once (e.g., Size) and reuse it across all relevant products.
Create only the frequencies you offer. Common options include monthly, quarterly, and annual. Add custom frequencies (e.g., every 6 months) if your business needs them.
## Related
Learn how to create and configure products in your catalog
Create and edit product variants using your variant options
Set up prices for products and recurring subscriptions
Apply different price lists to customer segments
# Create a customer
Source: https://docs.qwoty.io/user-guide/customers/how-tos/create-customer
Add a new customer record to manage contacts, billing details, and quotes
A customer record stores all account information, contacts, and billing details you need to generate quotes. You must create or select a customer before building a quote.
## Prerequisites
* You must have a Sales Rep, Manager, or Admin role
* You need at least the customer's company name to create a record
## Access the customer creation screen
In the left side menu, click **Customer** → **Customer**, then click **+ New**.
## Create the customer
In the **General Information** section, fill in:
* **Name** (required): Enter the company name
* **Type** (required): Select the customer type (e.g., **Company**) from the dropdown
* **Segment** (optional): Assign the customer to an existing customer segment
* **Logo** (optional): Upload a company logo
* **Tax** / **Tax Value** (optional): Set the applicable tax type and value; click **+ Add Tax ID** to add additional tax identifiers
In the **Linked Resources** section, associate any of the following with this customer. Only linked resources will be available when creating a quote for this customer:
* **Templates**, **Catalogs**, **Pricebooks**, **Discounts**, **Contract Models**, **Payment Terms**, **Payment Methods**
In the **Contacts** section, click **+ New contact** to add a contact person. Each contact has a **Full Name**, **Email**, and **Role**.
You can add more contacts after saving the customer.
Click **Save** at the top right to create the customer record.
## What happens next
After you create the customer:
* The customer appears in your customer list and is available when creating quotes
* You can add additional contacts, upload documents, and view quote history
* The primary contact can receive Dealroom invitations when you publish quotes
## Troubleshooting
Use the search bar at the top of the customer list to search by company name or contact email. Check that no filters are applied that might hide the record.
No. Currency is locked after customer creation to maintain data integrity across quotes and orders. Create a new customer record if you need a different currency.
Only **Name** and **Type** are required. All other fields, including contacts, are optional at creation. Add details as needed for your sales process.
## Related resources
Update company information and settings
Add, edit, or remove customer contacts
Build a new quote for this customer
Learn about customer management features
# Create a segment
Source: https://docs.qwoty.io/user-guide/customers/how-tos/create-segment
Create a customer segment to apply targeted pricing and filter your customer list
Customer segments let you group customers by shared attributes like region, industry, or contract size. Use segments to apply specific Pricebooks or filter your customer database.
## Prerequisites
* **Admin** or **Manager** role
* At least one customer in your Qwoty account
## Access the segments page
In the left side menu, click **Customer** → **Customer Segment**.
## Create a new segment
Click **+ New** in the top-right corner.
In the **General Information** section:
* **Name** (required): Enter a name for your segment (e.g., `Enterprise - France` or `SMB - Tech`)
* **Reference** (optional): Add an internal reference code
* **Description** (optional): Clarify the segment's purpose for your team
In the **Associated Customers** section, click **Manage Customers** to select which customers belong to this segment.
In the **Linked Resources** section, associate Templates, Discounts, and other resources. Only linked resources will be available when creating quotes for customers in this segment.
Click **Save** at the top right to create the segment.
## What happens next
Your new segment appears in the **Customer Segment** list and becomes available when creating or editing a Pricebook to apply targeted pricing.
A single customer can belong to multiple segments simultaneously.
## Troubleshooting
Open the segment and click **Manage Customers** to add customers. Customers are assigned to segments manually — there is no automatic rule-based assignment.
Ensure the segment is saved. Refresh your browser and verify you have the correct permissions to link segments to Pricebooks.
## Related resources
Update segment criteria and membership rules
Remove segments no longer in use
Apply segment-specific pricing to your products
Learn about customer management in Qwoty
# Import customers from CSV
Source: https://docs.qwoty.io/user-guide/customers/how-tos/import-csv
Bulk upload customer records into Qwoty using a CSV file
Importing customers from a CSV file lets you quickly add multiple customer records to Qwoty without manual entry. You can import basic customer information, billing details, and custom fields in a single operation.
## Prerequisites
Before you start, make sure you have:
* A CSV file with customer data
* Admin or Manager role permissions
* Column headers that match Qwoty's customer field names
Download the CSV template from Qwoty to ensure your file has the correct format and required columns.
## Access the import tool
Navigate to **Customer** → **Customer** in the left side menu, then click **\[BUTTON LABEL]** in the top right corner.
## Prepare your CSV file
Click **\[BUTTON LABEL]** to get a CSV template with all available fields and the correct column format.
Open the template and fill in your customer information. Required columns include:
* **\[FIELD NAME]**
* **\[FIELD NAME]**
Optional columns may include billing address, phone number, tax ID, customer segment, and custom fields.
Save your file as CSV format (UTF-8 encoding recommended) and close any programs that have it open.
Make sure email addresses are unique. Duplicate emails will cause the import to skip those rows or update existing records depending on your settings.
## Import the file
Click **\[BUTTON LABEL]** and select your prepared CSV file. Qwoty validates the file format and displays a preview.
Review the field mapping screen. Qwoty automatically maps CSV columns to customer fields based on header names. Adjust any incorrect mappings using the dropdown menus.
Choose your import behavior:
* **\[OPTION NAME]**: Ignores rows with existing email addresses
* **\[OPTION NAME]**: Overwrites existing customer data with CSV values
* **\[OPTION NAME]**: Adds only new customers
Click **\[BUTTON LABEL]** to begin processing. A progress indicator shows the upload status.
## What happens next
Qwoty processes the CSV file and displays an import summary showing:
* Number of customers successfully created
* Number of existing customers updated
* Number of rows skipped due to errors
You can download an error report if any rows failed validation. Successfully imported customers appear immediately in your customer list and are available for quote creation.
## Troubleshooting
Check that your CSV uses comma separators, UTF-8 encoding, and has header row in the first line. Avoid special characters in column names that don't match the template exactly.
Download the error report to see which rows failed and why. Common issues include missing required fields, invalid email formats, or data that exceeds field length limits.
This happens when email addresses have slight variations (extra spaces, different cases). Clean your data to ensure email consistency before importing, or use the update mode to merge records.
Verify that custom field column names exactly match the field names defined in **\[MENU PATH]** . Custom fields must be created before importing data into them.
## Related resources
Add individual customer records manually
Edit and update customer information
Learn about customer management in Qwoty
Download customer data as CSV
# Customers
Source: https://docs.qwoty.io/user-guide/customers/introduction
Manage your customer database and link quotes, orders, and contracts to customer accounts.
This section covers how to create, organize, and manage customer records in Qwoty. Customers are the foundation of your sales process—every quote, order, and contract is linked to a customer account.
You'll learn how to build your customer database, segment customers for pricing and catalog access, and track all deal activity in one place.
## Reference
Field structure and customer profile organization
How customer status changes from prospect to active account
Group customers for targeted pricing and catalog access
Add custom data fields to customer records
## Guides
Add new customers to your database
Bulk upload customer records from CSV
Associate quotes, orders, and contracts with customer accounts
Combine duplicate customer records
# The customer record
Source: https://docs.qwoty.io/user-guide/customers/reference/customer-record
Understand the structure and data stored in a Qwoty customer record
A customer record in Qwoty stores all information about a company or individual who buys from you. Every quote, order, and contract must be linked to a customer. Customer records centralize contact details, billing information, pricing agreements, and deal history.
## What a customer record contains
Customer records store both administrative data and sales context:
* **Company information**: Legal name, tax ID, industry, and address
* **Contact persons**: Multiple contacts with roles (billing, technical, executive)
* **Billing details**: Payment terms, currency, billing address, tax exemptions
* **Pricing context**: Assigned pricebook, discount tier, or custom pricing rules
* **Relationship history**: All quotes, orders, contracts, and Dealrooms associated with this customer
* **Custom fields**: Additional data fields configured by your admin
Customer records are separate from user accounts. A customer is a company you sell to; users are people who log into Qwoty (your team or Dealroom guests).
## Customer hierarchy
Qwoty supports parent-child customer relationships for enterprise account structures:
| Level | Description | Use case |
| ----------------------- | ---------------------- | --------------------------------------------- |
| **Parent customer** | Top-level organization | Global headquarters with negotiated pricing |
| **Child customer** | Subsidiary or division | Regional office inheriting parent's pricebook |
| **Standalone customer** | No hierarchy | Small business or individual buyer |
Child customers can inherit pricing, payment terms, and contract models from their parent, or override them with specific values.
## Key data sections
### Contact management
Each customer can have unlimited contacts. You can assign roles to organize contacts by function:
* Primary contact for quote discussions
* Billing contact for invoicing
* Technical contact for implementation
* Executive sponsor for approvals
When you publish a quote to a Dealroom, you select which contacts receive access and notifications.
### Pricing assignment
Customer records determine which prices apply to quotes:
* **Pricebook assignment**: Links the customer to a specific price list
* **Discount tier**: Applies volume or loyalty discounts automatically
* **Custom pricing**: Overrides catalog prices for negotiated deals
Assign pricebooks at the customer level to ensure consistent pricing across all quotes for that account.
### Payment terms
Define default payment conditions that auto-populate on new quotes:
* Net 30, Net 60, or custom payment schedules
* Accepted payment methods (credit card, wire transfer, ACH)
* Billing frequency for subscriptions (monthly, annual, quarterly)
## Managing customer data
### Creating customers
Sales reps can create customer records before or during quote creation. Required fields typically include company name, billing address, and currency.
In the left side menu, click **Customer** → **Customer**.
Click **+ New** and fill in the **General Information** fields (Name and Type are required).
Assign a pricebook and payment terms, or leave defaults.
Create at least one contact person for quote delivery.
### Customer segmentation
Use customer properties to segment your accounts:
* Industry or vertical market
* Company size or employee count
* Geographic region
* Customer status (prospect, active, churned)
Segmentation helps you filter reports, assign the right pricebook, and route quotes to appropriate managers for approval.
## Common questions
No. Each customer is assigned one pricebook at a time. However, you can override individual product prices on a quote, or create a custom pricebook for specific high-value accounts.
Existing quotes keep their original prices. Only new quotes created after the pricebook change will use the updated pricing.
No. Customer records are internal to your sales team. Customers access published quotes through the Dealroom, which shows only relevant deal information, not their full customer record.
Contact your admin or Qwoty support to merge duplicate records. This process consolidates contacts, quotes, and history into a single customer record.
Yes. Qwoty supports CSV import and integrations with major CRM platforms. Check with your admin about available integrations and data mapping.
## Related
Start a new quote and link it to a customer
Learn how price lists work and apply to customers
Invite customer contacts to view and sign quotes
Understand who can create and edit customer records
# Segments
Source: https://docs.qwoty.io/user-guide/customers/reference/segments
Group customers by shared characteristics to apply targeted pricing and catalog rules
Segments let you group customers by shared characteristics like industry, size, or region. Use segments to automatically apply the right Pricebook and Catalog when creating quotes for specific customer types.
A customer can belong to multiple segments, but only one segment's pricing rules apply per quote based on Template configuration.
## What are segments
Segments are customer classification labels that drive pricing and product availability. They help you maintain different price lists for different customer types without managing separate configurations manually.
Common segment types include:
* **Geographic**: North America, EMEA, APAC
* **Vertical**: Healthcare, Finance, Retail
* **Customer tier**: Enterprise, Mid-Market, SMB
* **Channel**: Direct, Partner, Reseller
Segments connect to Pricebooks, which define the prices customers in that segment see.
## How segments work
```mermaid theme={null}
graph LR
A[Customer] -->|assigned to| B[Segment]
B -->|linked to| C[Pricebook]
D[Template] -->|uses| C
D -->|generates| E[Quote]
E -->|shows prices from| C
```
When you create a quote:
1. Qwoty identifies the customer's segment
2. The Template applies the Pricebook linked to that segment
3. Products display with segment-specific pricing
4. If a customer has multiple segments, the Template's priority rules determine which Pricebook applies
## Creating and managing segments
### Defining segments
Create segments that reflect how you structure your pricing strategy. Each segment should represent a distinct customer group with unique pricing needs.
Start with 3-5 broad segments. You can always refine later as your pricing complexity grows.
### Assigning customers to segments
Customers are assigned to segments manually. Open a segment record and click **Manage Customers** to add or remove customers. A customer can belong to multiple segments, but Template configuration controls which one takes priority for pricing.
### Linking segments to Pricebooks
Each Pricebook targets one or more segments. When you configure a Template, you select which Pricebooks are available, and Qwoty matches the customer's segment to the appropriate price list.
A Pricebook can serve multiple segments, but a quote only uses one Pricebook at a time.
## Segment priority and conflicts
When a customer belongs to multiple segments, you need clear priority rules:
| Scenario | Resolution |
| ------------------------------ | ------------------------------------------------ |
| Customer has 1 segment | That segment's Pricebook applies automatically |
| Customer has multiple segments | Template configuration determines priority order |
| Customer has no segment | Template's default Pricebook applies |
| Segment has no Pricebook | Quote creation may fail or fall back to default |
Set segment priority at the Template level to ensure consistent pricing logic across your sales team.
## Common questions
Yes, but existing quotes keep their original Pricebook. Only new quotes reflect the updated segment assignment. Republish a quote to apply new pricing.
Quotes already created with that Pricebook retain their pricing. New quotes for customers in that segment will fail to generate until you assign a new Pricebook or update the Template configuration.
Yes. Link both Catalog and Pricebook to segments in your Template configuration. This lets you show different products to different customer types entirely.
There's no hard limit, but practical use typically involves 2-3 segments per customer. More segments make priority logic complex and harder to maintain.
## Related
Manage segment-specific pricing rules
Configure segment-to-Pricebook mapping
Assign and manage customer segments
Control product visibility by segment
# Data migration
Source: https://docs.qwoty.io/user-guide/data-migration/introduction
Import and export your product, master product, and price data via CSV files.
Import and export your catalog data into Qwoty using CSV files — a fast way to bring an existing product list, price book, or variant tree into your workspace without writing a single line of code.
## Import methods
Qwoty supports two main methods for importing data:
| Method | Best for | Volume limit |
| -------------- | ----------------------------------------------------- | ---------------------------- |
| **CSV import** | Standard migrations, periodic bulk updates | Up to \~10,000 rows per file |
| **API** | Large-scale migrations, continuous syncs, automations | Unlimited |
For very large datasets or recurring syncs from your ERP or CRM, use the [Qwoty REST API](https://docs.qwoty.io). The CSV importer is the fastest path for one-time migrations and admin-driven bulk updates.
## CSV import basics
You can import data for three objects: **Master products**, **Products**, and **Prices**. Each file must contain **only one object type**.
**Fields must exist before import.** Uploading a CSV creates records but does not create fields. If you need custom fields, create them first under **Settings → Data → Data model**. See the [Data model](/user-guide/data-model/introduction) page for details.
### Steps
1. Open **Settings → Data → Import/Export Data**.
2. Select the object you want to import: **Master products**, **Products**, or **Prices**.
3. Click **Download example file** to get a template with the right columns.
4. Format your file (see [Prepare your CSV files](/user-guide/data-migration/how-tos/prepare-csv-files)).
5. Click **Import**, drop your file, and follow the 4-step wizard: **Upload → Mapping → Confirm → Result**.
6. Review validation errors at the Confirm step. Errors don't block valid rows.
7. After import, download unimported rows if any failed and fix them in your source file.
## Importing relations between objects
Qwoty objects reference each other:
* A **Product** belongs to a **Master product** (via `parent_product_api_name` or `product_parent_id`)
* A **Price** belongs to both a **Product** and a **Pricebook**
* A **Product** is attached to one or more **Catalogs** and **Categories**
You reference related records by their unique identifier — the `id` (UUID) or the `api_name` (a stable, human-readable identifier you control).
**Import order matters.** Always import objects in this order, because the "parent" must exist before the "child" can reference it:
1. **Master products** — first
2. **Products** — second (referencing their master)
3. **Prices** — last (referencing products and pricebooks)
Catalogs, Categories, and Pricebooks must already exist in your workspace before you start. Create them through the UI or earlier in your migration plan.
See [Import relations](/user-guide/data-migration/reference/import-relations) for details on how relation columns work.
## Export data
Every import screen also exports. Use the **Export** section on the same page to download your current data as a CSV — useful for:
* Backing up before a bulk update
* Bringing data from a sandbox to a production workspace
* Editing in a spreadsheet then re-importing with `id` set on each row to update existing records
Filters apply: for products, you can scope the export to a specific catalog. For prices, you can scope to a specific pricebook.
## Permissions
Data import and export require specific permissions on the **Apps** block of your role:
* **Import CSV** — required to access the import screen
* **Export CSV** — required to download exports
Contact your workspace admin if these are missing. See [Manage roles](/user-guide/settings/users/manage-roles) for the full permission model.
## Reference
Concepts and rules that apply to every import.
File formats, encoding, and CSV best practices.
How Qwoty matches your CSV columns to its fields.
Which fields are unique and how Qwoty deduplicates.
How to link records to their parents during import.
Validation errors and how to fix them.
## How-tos
Step-by-step instructions for the four common imports.
Format your data correctly before importing.
Always start here — masters are the parents that products reference.
Import products attached to master products and catalogs.
Add prices to a pricebook — six pricing models supported.
# Field mapping
Source: https://docs.qwoty.io/user-guide/data-migration/reference/field-mapping
How field mapping works during data import in Qwoty.
## How field mapping works
When you upload a CSV file, Qwoty analyzes your columns and attempts to match them to existing fields on the target object.
### Automatic mapping
Qwoty tries to match columns based on:
* Column header names (exact or similar matches to Qwoty's field names)
* Data type detection (UUIDs, numbers, booleans, ISO dates)
* Common field patterns
**Quick tip:** export a few rows from the object you want to import. The exported file will have the exact column names Qwoty expects, making automatic mapping seamless during import.
### Manual mapping options
For each column, you can:
* **Map to a field** — select the matching Qwoty field from a dropdown at the **Mapping** step
* **Ignore this column** — skip the column entirely (data won't be imported)
**Fields must exist before import.** The import creates records, not fields. Create custom fields under **Settings → Data → Data model** before importing. See the [Data model](/user-guide/data-model/introduction) page for details.
## Field type compatibility
All field types available in Qwoty's data model are supported for import. You can also import `id` (or `product_id`, depending on the object) values to either assign a specific UUID to new records or update existing ones.
## Data format requirements
Some fields have special syntax. We recommend downloading the **example file** from the import screen before preparing your import — it shows the expected syntax for each field type.
### Text fields
* No special formatting required
* Leading and trailing spaces are trimmed automatically
* If a value contains a comma, line break, or quote, wrap the cell in double quotes: `"Premium, Extra Large"`
### Number fields
* Numbers only
* Decimals use a period: `1234.56`
* No thousands separators (not `1,234.56`)
* Negative numbers are accepted only where the field permits (most price fields require positive values)
### Boolean fields
Use lowercase `true` or `false` — applies to fields like `settings[is_active]`.
### Date fields
Use ISO 8601:
* `YYYY-MM-DD` (recommended) — for example, `2026-04-25`
* `YYYY-MM-DDTHH:MM:SSZ` for timestamps
### Currency fields
Use the ISO 4217 three-letter code: `EUR`, `USD`, `GBP`, `JPY`. Don't use the symbol (`€`, `$`) or the full name.
### Tax (VAT) fields
Use the format `_` where the rate is expressed without a decimal point (multiply by 10):
| Value | Meaning |
| -------- | ------------------ |
| `FR_200` | France, 20.0% VAT |
| `FR_055` | France, 5.5% VAT |
| `DE_190` | Germany, 19.0% VAT |
| `-` | No tax applied |
### Enum (select) fields
Use the **exact value** Qwoty expects, including capitalization. Common enums:
| Field | Accepted values |
| --------------------------- | ---------------------------------------------------------------------------- |
| `settings[recurrence_type]` | `one_off`, `recurring` |
| `settings[product_type]` | `physical`, `service`, `subscription` |
| `type` (price) | `one-time`, `reccuring` |
| `pricing_model` | `Flat`, `Cost based`, `Percent`, `Graduated Tiered`, `Volume Tiered`, `None` |
| `period_unit` | `day`, `week`, `month`, `year` |
Enums are **case-sensitive**. `flat` is rejected — use `Flat`. `Recurring` is rejected — use `recurring`. Use the exact values shown above.
### Multi-value fields
Some fields accept multiple values inside a single cell, separated by **commas**, with the cell wrapped in double quotes:
| Column | Example |
| -------------------- | -------------------------- |
| `catalog_api_names` | `"france,partner_pricing"` |
| `category_api_names` | `"hardware,accessories"` |
### Indexed fields (tiers, options, identifiers)
Qwoty uses bracket notation with a numeric index for repeating field groups. Two conventions coexist depending on the object:
| Pattern | Index base | Example |
| --------------- | ---------- | -------------------------------------------------------------- |
| Product options | 1-based | `options[1][name]`, `options[1][value]`, `options[2][name]`... |
| Price tiers | 0-based | `tiers[starting_unit][0]`, `tiers[starting_unit][1]`... |
Add `[1]`, `[2]`... or `[0]`, `[1]`... depending on the field group to declare additional rows.
### ID fields
Specifying an ID during import is optional. Qwoty auto-generates a UUID if not provided.
Use cases for mapping an ID column:
* **Set a specific ID** — choose the UUID for newly created records
* **Update existing records** — match against existing records to update them instead of creating duplicates. In that case, only map the ID column — don't combine it with other unique fields, to keep the import simple.
If you provide an ID, it must be in UUID format (for example, `c776ee49-f608-4a77-8cc8-6fe96ae1e43f`).
### Relation fields
For linking records to their parent or related object, see the [Import relations](/user-guide/data-migration/reference/import-relations) page.
## Finding API names
For Catalog, Category, and Pricebook references, you must use the **API name** (not the display label).
### How to find API names
1. Open **Settings → Data → Data model**
2. Select the object (Catalog, Category, Pricebook)
3. View the API name shown in the **API Name** column
You can also export the object first — the exported CSV includes API names in the appropriate columns.
## Related
Supported file formats and CSV best practices.
Which fields are unique and how Qwoty enforces uniqueness.
How to link records to their parents during import.
Validation errors and how to fix them.
# Supported file formats
Source: https://docs.qwoty.io/user-guide/data-migration/reference/file-formats
File formats supported for data import in Qwoty.
## Supported formats
Qwoty supports CSV files for data import:
| Format | Extension | Notes |
| ------- | --------- | ------------------------------------------------------------------------ |
| **CSV** | `.csv` | The only format supported today. Most compatible with spreadsheet tools. |
Excel `.xlsx` and `.xls` files are not currently accepted. Convert your
spreadsheet to CSV before importing.
## File requirements
| Requirement | Value |
| --------------------- | --------------------------------------------------------------- |
| **Encoding** | UTF-8 (recommended) |
| **Field separator** | Comma `,` |
| **Decimal separator** | Period `.` |
| **Structure** | First row must contain column headers |
| **Content** | One object type per file (Master products, Products, or Prices) |
## CSV best practices
* **Delimiter**: Use comma `,` — Qwoty's standard separator
* **Text qualifier**: Use double quotes `"` for cells containing commas, line breaks, or quotes — for example, `"france,partner_pricing"` for a multi-catalog cell
* **Line endings**: Windows (CRLF) or Unix (LF) both supported
* **Empty values**: Leave cells empty, don't use `NULL` or `N/A`
## Excel best practices
When exporting from Excel:
* Remove formulas (export values only)
* Delete empty rows at the end
* Ensure no merged cells
* Use the first sheet only
* Save explicitly as **CSV UTF-8 (Comma delimited)** to preserve special characters
## Large datasets
The CSV importer is designed for migrations and bulk updates of moderate size. For very large datasets or recurring syncs:
* Split your data into multiple smaller files (a few thousand rows each)
* Or use the [Qwoty REST API](https://docs.qwoty.io) for unlimited records and continuous sync from your CRM, ERP, or accounting tool
For very large migrations (100,000+ records), the API is significantly faster and more reliable than CSV imports.
## Related
How Qwoty matches your CSV columns to its fields.
Step-by-step formatting guide.
# Uniqueness constraints
Source: https://docs.qwoty.io/user-guide/data-migration/reference/uniqueness-constraints
How Qwoty enforces data uniqueness during import.
## Overview
Qwoty enforces uniqueness on certain fields to prevent duplicate records and ensure data integrity. Understanding these constraints is essential for successful imports — especially when you want to update existing records rather than create new ones.
## Default unique fields
| Object | Unique fields |
| ------------------- | ------------------------------------------------------------------------------------- |
| **Master products** | `product_id`, `product_api_name` |
| **Products** | `product_id`, `product_api_name`, `inventory[sku]` (when used as a deduplication key) |
| **Prices** | `id` |
| **Customers** | `id`, custom unique fields you define |
The `product_id`, `id` field (depending on the object) is Qwoty's internal identifier, auto-generated for each record. It uses UUID format (for example, `c776ee49-f608-4a77-8cc8-6fe96ae1e43f`).
The `*_api_name` fields are human-readable, stable identifiers that you control. They're useful when migrating between environments or when you want a permanent reference that survives data resets.
## Custom unique fields
For [manageable objects](/user-guide/data-model/introduction#standard-vs-manageable-objects) like Customer and Contract Model, you can define additional unique fields under **Settings → Data → Data model**:
Go to **Settings → Data → Data model** and click the manageable object.
Click the field you want to make unique.
Enable the **Unique** toggle in the field settings.
### Use cases for custom unique fields
* **External IDs** — store IDs from other systems (Salesforce ID, HubSpot ID, ERP customer code)
* **Business identifiers** — employee numbers, customer codes, internal product references
* **Alternative contact info** — VAT numbers, registration numbers
The field name `id` is reserved for Qwoty's internal UUID. Use a different name like `external_id`, `legacy_id`, or `erp_id` for identifiers from other systems.
## Import behavior
### Creating new records
If a unique field value doesn't exist in Qwoty, a new record is created. Leave the ID column empty in your CSV — Qwoty generates a fresh UUID automatically.
### Updating existing records
If a unique field value matches an existing record, that record is **updated** with the new data. Only the columns present in your CSV are modified — other fields stay untouched.
To update existing records reliably, **only map one unique field** (typically the ID). Mapping multiple unique fields at once can cause ambiguous matches.
### Duplicate detection during import
During the **Confirm** step, Qwoty validates your file:
* Duplicates within your file are flagged as validation errors
* Records with unique values that conflict with existing Qwoty records are flagged
* You can go back to the Mapping step or fix your source CSV before proceeding
Errors don't block the import — valid rows are still imported, and you can download the unimported rows after the import completes to fix and re-upload them.
## Best practices
1. **Remove duplicates** from your file before importing — sort by the unique field in your spreadsheet to spot them quickly
2. **Check for existing records** by exporting first, comparing against your file, and choosing whether to update or create
3. **Use external IDs** when migrating from other systems — they make round-trip imports straightforward
4. **Include the ID column** when you want to update existing records, and leave it empty when you want to create new ones
## Related
How fields are matched between your CSV and Qwoty.
How to link records to their parents during import.
Add custom fields and mark them as unique.
What validation errors mean and how to fix them.
# Manage client comments
Source: https://docs.qwoty.io/user-guide/dealroom/how-tos/manage-client-comments
View, respond to, and resolve client comments in the Dealroom
Client comments in the Dealroom let your customers ask questions, request clarifications, or negotiate terms directly on the quote. You can respond to comments, mark them as resolved, and keep track of all communication in one place.
Comments are visible to all participants in the Dealroom and remain part of the deal history even after the quote is signed.
## Access the Dealroom comments
In the left side menu, click **Project** → **Quote**, then select the quote with an active Dealroom.
Click the **Dealroom** tab at the top of the quote detail page.
Comments appear in the right sidebar of the Dealroom. If the sidebar is collapsed, click the **Comments** icon to expand it.
## View client comments
The comments panel displays all messages from your client and your team. Each comment shows:
* **Author name**: The person who posted the comment
* **Timestamp**: When the comment was posted
* **Status**: Whether the comment is open or resolved
* **Context**: The quote section or line item the comment refers to (if applicable)
Use the filter dropdown at the top of the comments panel to show only open comments, resolved comments, or all comments.
Comments attached to specific line items display an anchor link. Click it to jump directly to the referenced product or section in the quote.
## Respond to a comment
Scroll through the comments panel or use the filter to find the client's comment.
Click in the **Reply** field below the comment and type your response.
Click **Send** or press `Ctrl+Enter` (Windows) or `Cmd+Enter` (Mac) to post your reply.
Your client receives a notification that you've responded. All replies appear in chronological order under the original comment.
Only users with **Sales Rep**, **Manager**, or **Admin** roles can post comments in the Dealroom. **Viewers** can read comments but cannot reply.
## Resolve a comment
Mark a comment as resolved once the question is answered or the issue is addressed. Resolved comments are hidden by default but remain accessible in the comment history.
Click the comment you want to resolve in the comments panel.
Click **Resolve** at the bottom of the comment thread.
The comment moves to the **Resolved** filter view. You can reopen a resolved comment by clicking **Reopen** in the thread.
Resolving comments helps you track which client questions still need attention and keeps the active comments list clean.
## Add an internal note
Internal notes let you communicate with your team without the client seeing the message. Use them to discuss negotiation strategy, flag approval needs, or share context.
In the comment input field, toggle the **Internal note** switch to on.
Type your message in the input field. The field background changes color to indicate it's an internal note.
Click **Send** to post the internal note. It appears with an **Internal** label in the comments panel.
Double-check that the **Internal note** toggle is on before sending sensitive information. Internal notes cannot be converted to public comments after posting.
## What happens next
All comments and replies are saved in the Dealroom history. When the quote moves to **Pending Signature** or **Accepted** status, the comment thread is locked and becomes read-only for all participants.
You can export the full comment history along with the quote PDF from the **Export** menu in the quote actions.
## Troubleshooting
Check that you didn't toggle the **Internal note** switch when replying. Internal notes are visible only to your team. If the reply was sent as internal, post a new public comment with the same information.
Only the comment author or users with **Manager** or **Admin** roles can resolve comments. If you're a **Sales Rep** and didn't create the comment, ask a manager to resolve it or reply with confirmation that the issue is addressed.
Check the filter dropdown at the top of the comments panel. If it's set to **Resolved only**, switch it to **All comments** to see the full history.
## Related resources
Learn how to make a quote available to your client in the Dealroom
Monitor client engagement and interaction with your Dealroom
Allow clients to propose changes and accept or reject them
Move the quote to signature once all comments are resolved
# Publish a quote to Dealroom
Source: https://docs.qwoty.io/user-guide/dealroom/how-tos/publish-quote
Send your quote to clients by email or share a direct link
Once your quote is finalized and approved, you can publish it to your client. Qwoty offers two methods: send by email with Dealroom access, or share a direct link to include in your own communications.
## Prerequisites
* Your quote must be in **Approved** status (or **Draft** if approvals are disabled)
* At least one recipient must be added to the quote
## Navigate to the share modal
1. In the left side menu, click **Project** → **Quote**, then open the quote
2. Click **Share**
The **Share new quote** modal opens with two tabs: **Share Dealroom** and **Get link**.
## Publish via email
This tab lets you send the quote by email with access to the Dealroom.
* **Sender**: The sender is automatically set to you or the quote owner
* **Recipients**: Review the recipient list. Remove a recipient by clicking the cross on their tag
* **Email subject**: Customize the subject line or leave blank to use the default. You can use dynamic variables:
* `{{owner.first_name}}`: Sales rep first name
* `{{business_unit.name}}`: Selling entity name
* **Message**: Add a personalized message to accompany the email
The **Automatic reminder** section lets you send follow-ups if the quote remains unsigned.
You can configure up to 3 reminders:
* Check or uncheck each reminder to enable or disable it
* Set the delay in days after the initial sending
Click **Send quote by email**.
Automatic reminders save you from manually following up with customers and increase signature rates.
## Share via direct link
This tab provides direct links to the quote without sending an email.
Each recipient has a unique link. Click the copy icon next to the link to copy it to your clipboard.
You can share this link via any channel: custom email, WhatsApp, SMS, or instant messaging.
Click **Download PDF** to download a PDF version of the quote for attachments or printing.
Each recipient has a unique link, which lets you track who views and signs the quote.
## What happens next
When you publish a quote:
* The quote status changes to **Published**
* Recipients can access the Dealroom to view, comment on, and sign the quote
* If you configured automatic reminders, Qwoty sends follow-up emails based on your schedule
* You can track views and interactions in the quote activity log
## Related resources
Configure signature settings for your quote
Learn how clients interact with the Dealroom
Make changes to an existing quote
Monitor views, comments, and signatures
# Send a quote to signature
Source: https://docs.qwoty.io/user-guide/dealroom/how-tos/send-to-signature
Request customer signature on a published quote from the Dealroom
Once you've published a quote and shared the Dealroom with your customer, you can send it for signature. This moves the quote from **Published** to **Pending Signature** status and notifies the customer to sign.
You must publish the quote before you can send it to signature. The quote cannot be in **Draft** or **Pending Approval** status.
## Prerequisites
The quote is in **Published** status
The Dealroom has been shared with the customer
A contract model is attached to the template
## Send the quote to signature
Navigate to **Quotes** and select the quote you want to send for signature.
Click **\[BUTTON LABEL]** to open the Dealroom view.
In the Dealroom, click **\[BUTTON LABEL]** in the top right corner.
A signature modal appears with the contract preview and signature settings.
Review the contract preview to ensure all variables have populated correctly.
Select the **\[FIELD NAME]** for the customer signatory. You can choose from existing contacts or add a new one.
Optionally, add a message to include in the signature request email.
Click **\[BUTTON LABEL]** to send the signature request.
The quote status changes to **Pending Signature** and the customer receives an email notification.
## What happens next
The customer receives an email with a link to sign the quote in the Dealroom. They can review the contract, add comments, and provide their electronic signature.
Once the customer signs, the quote automatically moves to **Accepted** status and an order is generated. You receive a notification that the quote has been signed.
If the customer doesn't sign within the quote validity period, the quote moves to **Expired** status.
## Troubleshooting
Verify the quote is in **Published** status. Quotes in **Draft**, **Pending Approval**, or **Approved** status cannot be sent to signature.
Check that a contract model is attached to the template. Without a contract model, signature requests cannot be sent.
Review the contract model to ensure all variables reference valid fields. Common issues include deleted custom fields or typos in variable names.
Check that required customer information is complete in the customer record, especially for variables like `{{customer.name}}` or `{{customer.address}}`.
Verify the customer email address is correct in the contact record.
Check the customer's spam folder. Signature request emails may be filtered by some email providers.
You can resend the signature request from the **\[MENU PATH]** in the Dealroom.
## Related resources
Share the Dealroom link with your customer
Configure dynamic contract templates
Understand quote statuses and transitions
Learn how customers interact with the Dealroom
# Dealroom
Source: https://docs.qwoty.io/user-guide/dealroom/introduction
A client-facing collaboration space where buyers view, negotiate, and sign quotes.
The Dealroom is your digital sales workspace shared with customers. It replaces static PDFs with an interactive experience where buyers explore pricing, leave comments, negotiate terms, and sign—all in one place.
This section covers how to publish quotes to the Dealroom, manage customer interactions, and configure the buyer experience.
## Reference
Components and layout of the customer-facing Dealroom interface
Status lifecycle from Published through Accepted
Control who can access and interact with Dealrooms
Branding, notifications, and configuration options
## Guides
Send your quote live for customer review
Monitor opens, views, and activity in the Dealroom
Respond to buyer feedback and update pricing
Configure and request electronic signatures
# The client view
Source: https://docs.qwoty.io/user-guide/dealroom/reference/client-view
How clients see and interact with your Dealroom
The client view is what your customers see when they access a published Dealroom. It provides a clean, professional interface for reviewing quotes, asking questions, negotiating terms, and signing contracts.
Unlike your internal Qwoty workspace, the client view contains no configuration options or administrative controls. Clients see only the content you've shared with them.
## What clients can see
The client view displays:
* **Quote details**: line items, pricing, discounts, and totals
* **Documents**: attached files like proposals, technical specs, or compliance certificates
* **Comments**: discussion threads on specific line items or the overall quote
* **Contract preview**: the final contract with populated variables
* **Signature interface**: electronic signature fields when the quote reaches Published status
Clients cannot see draft quotes, internal notes, approval workflows, or pricing margins.
Clients must have a valid Dealroom link to access the view. Links are unique per quote and can be regenerated if compromised.
## How access works
```mermaid theme={null}
sequenceDiagram
participant Sales as Sales Rep
participant System as Qwoty
participant Client as Client
Sales->>System: Publish quote
System->>System: Generate unique Dealroom link
System->>Client: Send email with link
Client->>System: Click link
System->>Client: Display Dealroom (no login required)
Client->>System: View, comment, or sign
```
## Access methods
Clients can access the Dealroom through:
* **Email link**: automatically sent when you publish a quote
* **Direct URL**: shareable link you can copy and send through your own channels
No account creation or password is required. The unique URL serves as the authentication mechanism.
Anyone with the Dealroom link can access it. Treat it like a password and share it only through secure channels.
## Client interactions
### Viewing content
Clients can expand and collapse sections, download attached documents, and see real-time updates when you modify the quote.
Changes you make to a published quote appear immediately in the client view without requiring a new link.
### Commenting
Clients can leave comments on:
* Individual line items (e.g., "Can we increase this quantity to 50?")
* The overall quote (e.g., "When does this pricing expire?")
* Specific contract clauses
You receive notifications for new client comments and can respond directly within Qwoty. All comments appear in both views.
### Signing
When the quote status is **Published** and all negotiations are complete, clients see signature fields in the Dealroom.
After signing, the quote moves to **Pending Signature** (if multiple signers are required) or **Accepted** (if complete), and Qwoty automatically generates an Order.
## Customization options
You can customize the client view appearance through your Template settings:
* **Company branding**: logo, colors, and fonts
* **Header text**: custom messaging or calls-to-action
* **Footer**: legal disclaimers or contact information
* **Document order**: arrange sections in a logical flow
Test the client view yourself before sharing. Use the **\[BUTTON LABEL]** button to preview exactly what your customer will see.
## Common questions
No. Clients can only view content and leave comments. All quote modifications must be made by you in the Qwoty workspace. This ensures pricing integrity and maintains your approval workflows.
No. Clients access the Dealroom through a unique URL with no login required. This reduces friction and speeds up deal closure.
Yes. You can regenerate the Dealroom link, which invalidates the previous URL. Clients with the old link will no longer be able to access the Dealroom.
The client can still view the Dealroom, but signature fields become disabled. The quote status shows as **Expired**, and clients see a message indicating the offer is no longer valid.
Yes. The client view includes a download option that generates a PDF version of the quote with all current pricing and terms.
## Related
Learn how to publish a quote and generate a Dealroom link
Manage client discussions and internal notes
Configure and collect electronic signatures
Brand your Dealroom with company colors and logos
# What is Dealroom
Source: https://docs.qwoty.io/user-guide/dealroom/reference/what-is-dealroom
Understand the Dealroom concept and how it enables collaborative deal closing
## Overview
Dealroom is a dedicated, client-facing workspace where your customers view, discuss, negotiate, and sign quotes. It replaces static PDF quotes and endless email threads with a collaborative environment that accelerates deal closure. Each published quote automatically generates its own Dealroom.
## The concept
A Dealroom transforms the quote review process into a transparent, interactive experience:
* **Private workspace**: Each Dealroom is accessible only to invited stakeholders via a secure, unique link
* **Real-time collaboration**: Buyers and sellers comment directly on line items, pricing, and terms without switching tools
* **Version control**: All quote revisions appear in the same Dealroom, maintaining conversation history and context
* **Signature workflow**: Built-in e-signature capabilities eliminate the need for third-party tools
Dealrooms are automatically created when a quote moves from **Approved** to **Published** status. The quote owner controls who receives access and what permissions they have.
## How it works
```mermaid theme={null}
graph LR
A[Quote Approved] --> B[Publish Quote]
B --> C[Dealroom Created]
C --> D[Share Link with Customer]
D --> E[Customer Reviews]
E --> F{Negotiation?}
F -->|Comments/Changes| G[Update Quote]
G --> C
F -->|Approved| H[Customer Signs]
H --> I[Quote Accepted]
I --> J[Order Generated]
```
## Key features
### Interactive quote presentation
The Dealroom displays your quote in a clean, branded format optimized for buyer review. Customers see product details, pricing breakdowns, payment terms, and contract terms in a structured layout without downloading files.
### Threaded comments
Both parties can leave comments on specific line items or sections. Comments support attachments and @mentions, keeping feedback organized and actionable. All stakeholders see the same conversation thread.
### Live negotiation
When customers request changes, you update the quote directly in Qwoty. The Dealroom refreshes automatically with the new version, preserving all previous comments and context. No need to send updated PDFs or create new links.
### E-signature integration
Once the customer approves the quote, they sign directly within the Dealroom. The signature workflow captures signatory details, timestamps, and audit trails. Signed quotes automatically transition to **Accepted** status and generate orders.
### Access control
You control who can view the Dealroom by inviting specific email addresses. Recipients access the Dealroom without creating Qwoty accounts. Track who has viewed the quote and when.
## Dealroom lifecycle
| Stage | Description | Actions Available |
| ----------------- | ------------------------------------------ | ------------------------------------- |
| **Created** | Dealroom generated when quote is published | Share link, invite stakeholders |
| **Viewed** | Customer has opened the Dealroom | Monitor activity, respond to comments |
| **Negotiating** | Active back-and-forth on terms or pricing | Update quote, address comments |
| **Ready to Sign** | Customer agrees to terms | Send signature request |
| **Signed** | Customer has executed the contract | View signed document, access order |
| **Expired** | Quote passed expiration date | Extend expiration or mark as lost |
## Common questions
Yes. Each published quote generates its own Dealroom. If you're working on multiple opportunities with the same customer, each gets a separate workspace with its own access link and conversation thread.
The Dealroom persists across quote revisions. When you update and republish a quote, the same Dealroom displays the new version with a revision indicator. All previous comments remain visible, maintaining negotiation context.
No. Customers access Dealrooms via secure, unique links without creating accounts. They enter their email address to view the quote and participate in discussions. This reduces friction in the buying process.
Yes. You control whether customers can leave comments or request changes in the Dealroom settings. For non-negotiable quotes, you can restrict the Dealroom to view-only mode and signature workflow.
Dealrooms remain accessible as long as the underlying quote is valid. When a quote expires or is marked **Lost** or **Canceled**, the Dealroom displays the appropriate status but retains historical data for audit purposes.
Yes. Dealrooms inherit branding from your Qwoty workspace settings, including logo, colors, and domain. Admins configure these settings at the workspace level to ensure consistent customer-facing presentation.
## Related
Learn how to publish quotes and generate Dealrooms
Configure and manage the e-signature process
Understand the quote lifecycle and status transitions
Set up dynamic contract templates for Dealrooms
# Create a contract model
Source: https://docs.qwoty.io/user-guide/documents/how-tos/create-contract-model
Build a custom contract template with dynamic fields and variables
Contract models are reusable contract templates that populate automatically with data from your quotes and customers. You can insert dynamic variables like `{{customer.name}}` or `{{quote.total}}` to generate personalized contracts for every deal.
This guide walks you through creating a new contract model from scratch.
**Prerequisites**
* You must have the **Admin** role to access this screen
* Custom fields must be created beforehand in **Settings → Data Model**
## Access the contract model builder
In the left side menu, click **Content**.
Click **Contract Models**.
Click **+ New** in the top right corner.
## Fill in general information
In the **General Information** section, configure:
* **Name** \*: Contract model name (e.g., "Standard SaaS Agreement", "Professional Services Contract")
* **Reference**: Internal reference to identify the model
* **Language** \*: Contract language
* **Category**: Organizational category
## Write the contract content
The **Contract** block contains a rich text editor for your contract document body. Formatting tools include headings, lists, bold, italic, underline, strike, highlight, text alignment, columns, images, and links. You can also import a DOCX file as a starting point.
Insert dynamic variables directly in the editor to populate quote and customer data automatically. See [Use dynamic variables](/user-guide/documents/how-tos/use-dynamic-variables) for syntax and available fields.
## Configure contract fields
The **Contract Fields** section defines the contractual parameters. Each field has an **Editable by sale** checkbox that controls whether sales reps can modify the value when creating a quote.
* **Duration Type**: How the contract duration is structured
* **Duration (months)**: Contract duration in months
* **Renewal Type**: How the contract renews after the initial term
* **Prior Notice**: Whether advance notice is required to terminate the contract
## Configure custom fields
The **Custom Fields** section lists workspace-level custom fields for contract models. Toggle each field on to include it, enter its default value, and check **Editable by sale** if sales reps should be able to modify it when creating a quote.
## Add external identifiers
If you integrate with external systems, fill in the corresponding identifiers in the **Identifiers** section:
* **CRM ID**: Identifier in your CRM
* **ERP ID**: Identifier in your ERP
* **Accounting ID**: Identifier in your accounting software
## Define the status
In the right side panel, configure:
* **Status**: `Active` (available in templates) or `Inactive`
* **API Name** \*: Technical identifier generated automatically from the name
The **API Name** can only be modified at creation. Once saved, it can no longer be modified to ensure the stability of your integrations.
## Save your contract model
Once all fields are configured, click **Save** at the top right.
## What happens next
Your contract model is now available to associate with quote templates. When a quote using this template reaches the **Accepted** status, Qwoty automatically generates a contract by populating your model with the quote and customer data.
Edit, archive, or delete existing contract models
Build templates that combine catalogs, pricebooks, and contract models
Add custom fields to your workspace
Learn how customers interact with quotes and contracts
# Manage the media library
Source: https://docs.qwoty.io/user-guide/documents/how-tos/manage-media-library
Upload, organize, and reuse images, documents, and assets across products, quotes, and contracts
The media library centralizes all files uploaded in Qwoty: product images, contract documents, marketing assets, and more. You can organize files by category and type, then reuse them across product sheets, quotes, contracts, and sales documents.
## Access the media library
In the left sidebar, click **Content**, then click **Media**.
## Navigate folders
The left navigation panel organizes media by category:
* **Product**: Product-related files
* Picture: Product images
* Asset: Product documents and files
* **Template**: Template-related files
* Header Media: Images used in quote/contract template headers
* **Business Unit**: Business unit assets
* Logo: Business unit logo images
* **Customer**: Customer-related files
* Logo: Customer logo images
* **My Media**: Files uploaded directly by the current user
Click a folder to filter the media list and display only files in that category.
## View media details
The media list displays the following information for each file:
* **Image**: Thumbnail preview of the file
* **Name**: File name
* **Type**: MIME type (e.g., `image/png`, `image/jpeg`)
* **Folder**: The category/subcategory path where the file is stored (e.g., `product/picture`, `customer/logo`)
* **Updated**: Last modified date
Use the search bar at the top to find a specific file by name or ID.
## Add media
Click the add button in the top right corner.
In the modal, select one of the following options:
* **Import**: Upload a new file from your computer
* **Select an existing file**: Reuse a file already in the media library
Click **Add** to save the media to the selected folder.
All file types are accepted. Use clear, descriptive names to make files easy to find later.
## Export media
To download a file, select it from the list and click the export option from the row menu.
## Related resources
Create reusable content for quotes and contracts
Build dynamic contract models with variables
Add products to your catalog
# Use dynamic variables
Source: https://docs.qwoty.io/user-guide/documents/how-tos/use-dynamic-variables
Insert dynamic variables into contract models and email templates to populate customer, quote, and product data automatically.
Dynamic variables let you insert placeholders into contract models, email templates, and documents that automatically populate with real data when you generate or send them. You can reference customer details, quote totals, product names, and custom fields.
## Syntax
Dynamic variables use double curly braces: `{{variable.name}}`. They pull data from the customer record, quote, or line items associated with the document.
Common variable categories:
* `{{customer.*}}` — Customer information (name, email, address, custom fields)
* `{{quote.*}}` — Quote details (number, total, subtotal, status, dates)
* `{{product.*}}` — Product line item details (name, quantity, price, discount)
* `{{user.*}}` — Sales rep or owner information (name, email, title, phone)
* `{{company.*}}` — Your company details (name, address, logo URL)
Variable names are case-sensitive. Use the exact format shown in the variable reference panel.
## Insert a variable into a contract model
In the left side menu, click **Content** → **Contract Models**, then select the contract model you want to edit, or create a new one.
Click where you want the variable to appear in the document body.
Type `{{` to open the variable picker, or manually type the full variable name like `{{customer.name}}`.
Select the variable from the list or press **Enter** to confirm.
Click **\[BUTTON LABEL]** to see how the variable renders with sample data.
Click **Save** to apply your changes.
## Format variables
You can apply formatting modifiers to control how variables display.
### Date formatting
Add a format modifier after the variable name:
* `{{quote.created_date | date: "MM/DD/YYYY"}}` → 12/31/2024
* `{{quote.expiry_date | date: "MMMM D, YYYY"}}` → December 31, 2024
### Number formatting
Format currency and decimal values:
* `{{quote.total | currency}}` → \$10,000.00
* `{{product.quantity | number: 2}}` → 1,234.56
### Conditional logic
Show or hide content based on variable values:
```
{{#if quote.discount}}
A discount of {{quote.discount}}% has been applied.
{{/if}}
```
Advanced conditional logic and loops require specific syntax. Contact support for custom formatting requirements.
## Use variables in email templates
You can insert dynamic variables into email subject lines and body text when configuring automated notifications or quote emails.
Navigate to **\[MENU PATH]** and select the email template. Use the same `{{variable.name}}` syntax in the subject or body fields.
## Common variables reference
| Variable | Description | Example output |
| ------------------------ | ---------------------- | ------------------------------------------- |
| `{{customer.name}}` | Customer full name | Acme Corporation |
| `{{customer.email}}` | Customer email address | [contact@acme.com](mailto:contact@acme.com) |
| `{{quote.number}}` | Quote reference number | Q-2024-00123 |
| `{{quote.total}}` | Quote total amount | 10000.00 |
| `{{quote.created_date}}` | Quote creation date | 2024-01-15 |
| `{{user.name}}` | Sales rep full name | John Smith |
| `{{user.email}}` | Sales rep email | [john@company.com](mailto:john@company.com) |
| `{{company.name}}` | Your company name | Your Company Inc. |
To see the complete list of available variables for your instance, click **\[BUTTON LABEL]** in the contract model editor.
## What happens next
When you publish a quote or generate a contract, Qwoty replaces all variables with actual data from the associated records. If a variable references a field with no value, it displays as blank unless you configure a fallback.
## Troubleshooting
The variable name may be misspelled or the field does not exist. Check the variable reference panel for the correct syntax. Verify the customer record contains data in that field.
Add a date format modifier: `{{quote.created_date | date: "MM/DD/YYYY"}}`. Verify the format string matches the supported date tokens.
The referenced field is empty in the source record. Add a default value using conditional syntax or populate the field before generating the document.
Custom field variables may use a different syntax like `{{custom.field_name}}` or `{{customer.custom_field_name}}`. Check with your admin or the variable reference panel for custom field naming conventions.
## Related
Build contract templates with variables and formatting
Set up automated emails with dynamic content
Add custom fields to quotes and customers
Create final contracts from approved quotes
# Documents and Models
Source: https://docs.qwoty.io/user-guide/documents/introduction
Learn how to create and manage contract templates, proposals, and document models in Qwoty.
Documents and Models let you automate contract generation, create branded proposals, and standardize your sales documents. This section covers Contract Models, document templates, and how to use dynamic variables to personalize content.
## Reference
Template structure with dynamic variables for automated contract generation
Available merge fields for customer, quote, product, and deal data
How documents are generated, versioned, and finalized
Supported formats for templates and generated documents
## Guides
Build your first contract template with dynamic variables
Insert quote, customer, and product data into your templates
Configure document review and approval processes
Control branding, formatting, and layout of generated documents
# Content blocks
Source: https://docs.qwoty.io/user-guide/documents/reference/content-blocks
Reusable marketing text sections that sales reps can insert into quotes
Content blocks are pre-written marketing text sections that sales reps insert into their quotes. They save time, ensure consistency, and guarantee that all quotes include the latest approved messaging.
You can create different block types—company overview, solutions, case studies, team introductions—and associate them with specific quote templates so the right content appears automatically.
## What content blocks include
Each content block contains:
* **Formatted text**: Rich text with headings, bold, italic, alignment, and line breaks
* **Dynamic variables**: Placeholders like customer name, address, or buyer name that populate automatically
* **Type classification**: Category (Introduction, Company, Solution, etc.) for organization and placement
* **Template association**: Links to specific quote templates that use this block
* **Language**: Content language for multi-language support
Content blocks work like contract templates—both support dynamic variables that replace with quote or customer data.
## How it works
```mermaid theme={null}
graph LR
A[Create content block] --> B[Set type & language]
B --> C[Write formatted content]
C --> D[Add dynamic variables]
D --> E[Associate with templates]
E --> F[Sales rep creates quote]
F --> G[Block auto-inserts]
G --> H[Variables populate]
```
## Access content blocks
Click **Content** in the left sidebar.
Click **Content Block**.
## Content block list
The list displays all configured content blocks with these columns:
* **Active**: Active or Inactive status
* **Name**: Content block name
* **Description**: Short description of the block
* **Reference**: Technical reference code
* **Type**: Content category
* **Language**: Block language
* **Updated at**: Last modification date
Use the search bar to find specific blocks quickly.
## Create a content block
Click **New** in the top right.
Fill in the required fields:
* **Name** \*: Block name (e.g., `Acme Inc Overview`, `Our methodology`)
* **Reference**: Technical reference code
* **Internal description**: Note for your team
* **Language**: Content language
* **Type**: Content category (see types below)
* **Associated templates**: Quote templates that will use this block
In the right panel, set **Status** to Active or Inactive.
### Content types
Select the type that matches your content:
| Type | Purpose |
| ------------------ | ------------------------------- |
| **Introduction** | General quote introduction |
| **Context** | Project or request background |
| **Objectives** | Goals to achieve |
| **Company** | Company overview |
| **Solution** | Proposed solution description |
| **Advantage** | Benefits and advantages |
| **Case Studies** | Customer references |
| **Timeline** | Project schedule and milestones |
| **Results & KPIs** | Expected outcomes and metrics |
| **Team** | Project team introduction |
The type helps organize your blocks and can automatically position the block in the correct quote section based on template configuration.
## Write content
The rich text editor provides these formatting tools:
* Text style (headings, paragraphs)
* **Bold**, *Italic*, Underlined
* Alignment (left, centered, right, justified)
* Line breaks
### Insert dynamic variables
Add variables that populate automatically with quote or customer data:
* **Customer name**: Customer legal name
* **Address**: Customer address
* **Buyer name**: Contact name
* Other variables based on your configuration
**Example**: `We are delighted to present to {{customer.name}} our solution tailored to your needs...`
Variables use the same syntax as contract templates: `{{customer.name}}`, `{{quote.total}}`, etc.
## External identifiers
Link content blocks to external systems:
* **CRM ID**: Identifier in your CRM
* **ERP ID**: Identifier in your ERP
* **Accounting ID**: Identifier in your accounting software
## Import and export
Manage content blocks in bulk:
* **Export**: Click **Export** to download all blocks
* **Import**: Click **Import** to upload blocks from a file
## Save changes
Click **Save** in the top right to save your content block.
Changes apply to all new quotes created after the update. Existing quotes retain the version that was current when they were created. This ensures consistency in already-published quotes.
Yes. Associate a content block with multiple templates in the **Associated templates** field. The block will be available for all linked templates.
Inactive blocks don't appear when sales reps create quotes, but they remain in the system. Use Inactive status to temporarily hide outdated content without deleting it.
The current editor supports text formatting only. For visual content, consider adding image links or references that sales reps can customize per quote.
Learn how contract templates use the same variable syntax
Configure templates that use content blocks
See how sales reps insert content blocks
View all available variables for content blocks
# Contract model and variables
Source: https://docs.qwoty.io/user-guide/documents/reference/contract-model-and-variables
Reusable contract templates with dynamic variables that auto-populate quote and customer data
Contract models are reusable contract templates that populate automatically with data from your quotes and customers. You can insert dynamic variables like `{{customer.name}}` or `{{quote.total}}` to generate personalized contracts for every deal.
This guide walks you through creating a new contract model from scratch.
**Prerequisites**
* You must have the **Admin** role to access this screen
* Custom fields must be created beforehand in **Settings → Data Model**
## Access the contract model builder
In the left side menu, click **Content**.
Click **Contract Models**.
Click **+ New** in the top right corner.
## Fill in general information
In the **General Information** section, configure:
* **Name** \*: Contract model name (e.g., "Standard SaaS Agreement", "Professional Services Contract")
* **Reference**: Internal reference to identify the model
* **Language** \*: Contract language
* **Category**: Organizational category
## Write the contract content
The **Contract** block contains a rich text editor for your contract document body. Formatting tools include headings, lists, bold, italic, underline, strike, highlight, text alignment, columns, images, and links. You can also import a DOCX file as a starting point.
Insert dynamic variables directly in the editor to populate quote and customer data automatically. See [Use dynamic variables](/user-guide/documents/how-tos/use-dynamic-variables) for syntax and available fields.
## Configure contract fields
The **Contract Fields** section defines the contractual parameters. Each field has an **Editable by sale** checkbox that controls whether sales reps can modify the value when creating a quote.
* **Duration Type**: How the contract duration is structured
* **Duration (months)**: Contract duration in months
* **Renewal Type**: How the contract renews after the initial term
* **Prior Notice**: Whether advance notice is required to terminate the contract
## Configure custom fields
The **Custom Fields** section lists workspace-level custom fields for contract models. Toggle each field on to include it, enter its default value, and check **Editable by sale** if sales reps should be able to modify it when creating a quote.
## Add external identifiers
If you integrate with external systems, fill in the corresponding identifiers in the **Identifiers** section:
* **CRM ID**: Identifier in your CRM
* **ERP ID**: Identifier in your ERP
* **Accounting ID**: Identifier in your accounting software
## Define the status
In the right side panel, configure:
* **Status**: `Active` (available in templates) or `Inactive`
* **API Name** \*: Technical identifier generated automatically from the name
The **API Name** can only be modified at creation. Once saved, it can no longer be modified to ensure the stability of your integrations.
## Save your contract model
Once all fields are configured, click **Save** at the top right.
## What happens next
Your contract model is now available to associate with quote templates. When a quote using this template reaches the **Accepted** status, Qwoty automatically generates a contract by populating your model with the quote and customer data.
Edit, archive, or delete existing contract models
Build templates that combine catalogs, pricebooks, and contract models
Add custom fields to your workspace
Learn how customers interact with quotes and contracts
# Add products and options
Source: https://docs.qwoty.io/user-guide/quotes/how-tos/add-products-and-options
Add products to your quote and configure pricing, quantity, and optional add-ons.
Adding products to a quote is the core step in building a customer proposal. You can add individual products, bundles, and configure product options to match your customer's needs.
## Prerequisites
* You have an open quote in **Draft** status
* The quote is linked to a Template with an active Catalog and Pricebook
## Access the product catalog
Go to **Project** > **Quote** in the side menu, then click the quote you want to edit.
In the **Quote** section, click one of the following buttons to add a section:
* **+ Product section** — adds a standard product section for one-time or recurring products
* **+ Plan section** — adds a plan-based section for subscription products
* **+ Import bundle** — imports a pre-configured bundle from the catalog
## Add products to the quote
Within the product section, browse or search for products in the catalog and select them to add to your quote.
For each product, set the quantity. The unit price is pulled from the Pricebook automatically. You can override the price if your role permissions allow it.
Click **Save** at the top right of the quote to save your changes.
Use bundles to add multiple related products at once with pre-configured pricing.
## Configure product options
If a product has configurable options (created with the Product Configurator), you can customize it for your customer.
In the quote line items table, locate the product with configurable options. Click the **\[CONFIGURE]** icon or button next to the product.
Choose from available options such as add-ons, upgrades, or variants. The Product Configurator enforces any rules set by your Admin (e.g., required selections, incompatible combinations).
Click **\[SAVE]** or **\[APPLY]** to update the product line item. The quote total recalculates automatically based on your selections.
Configurator rules may require certain options or prevent invalid combinations. Required fields are marked and must be completed before you can save.
## Adjust line items
After adding products, you can modify the quote line items:
* **Edit quantity or pricing**: Click on the line item to change values
* **Apply discounts**: Add percentage or fixed amount discounts to individual line items, or click **+ Global discount** in the **Summary** section for a quote-level discount
* **Reorder items**: Drag and drop line items to change their order in the quote
* **Remove products**: Click the **\[DELETE]** or **\[REMOVE]** icon to remove a line item
Removing a product from a quote cannot be undone. The product will need to be re-added from the catalog.
## What happens next
Once you've added all products and configured options:
1. Review the quote total and line item details
2. Add any additional information in the quote description or notes
3. Preview the quote to see how it will appear to your customer
4. Submit the quote for approval (if required) or publish it directly to the Dealroom
## Troubleshooting
The catalog shown depends on the Template assigned to your quote. If products are missing, verify that:
* The products are active in the Catalog linked to the Template
* The products have prices defined in the active Pricebook
* Your user role has permission to view those products
Contact your Admin if you need access to additional products.
Price override permissions are role-based. Sales Reps may have limited or no ability to modify prices pulled from the Pricebook. Managers and Admins typically have override permissions.
If you need to offer custom pricing, request Manager approval or use discount fields instead of changing the base price.
Configurator errors occur when:
* Required options are not selected
* Incompatible options are selected together
* Quantity limits are exceeded
Review the error message and adjust your selections. If the issue persists, the Product Configurator rules may need updating by an Admin.
The quote total includes:
* Sum of all line item subtotals (quantity × unit price)
* Line item and quote-level discounts
* Taxes (if configured in the Template)
* Any fees defined in Payment Terms
Expand the quote summary section to view the detailed calculation breakdown.
## Related resources
Learn how to start a new quote from a Template
Add quote-level or line item discounts
Understand how product options and rules work
Create and manage product bundles
# Apply a discount
Source: https://docs.qwoty.io/user-guide/quotes/how-tos/apply-discount
Add percentage or fixed discounts to quote line items or entire quotes
You can apply discounts to individual line items or to the entire quote. Discounts help you offer promotional pricing, volume incentives, or negotiate with customers during the deal process.
Qwoty supports both percentage-based discounts (e.g., 15% off) and fixed-amount discounts (e.g., \$500 off). Line item discounts apply before quote-level discounts are calculated.
## Prerequisites
* You have the **Sales Rep**, **Manager**, or **Admin** role
* The quote is in **Draft** status
## Access the quote
Navigate to **Project** > **Quote** in the side menu and open the quote you want to modify.
## Apply a line item discount
Find the line item you want to discount in the quote editor.
Click the **\[BUTTON LABEL]** next to the line item price or total.
Choose either **Percentage** or **Fixed amount** from the discount type dropdown.
Enter the discount value in the **\[FIELD NAME]** field. For percentage discounts, enter the number without the % symbol (e.g., `15`). For fixed discounts, enter the amount (e.g., `500`).
Click **\[BUTTON LABEL]** to apply the discount.
The line item total updates automatically to reflect the discount.
## Apply a quote-level discount
Scroll to the **Summary** section, located below all line items in the quote editor.
Click **+ Global discount** in the top right of the **Summary** section.
Select **Percentage** or **Fixed amount** as the discount type.
Enter the discount value in the **\[FIELD NAME]** field.
Optionally, add a discount reason or label in the **\[FIELD NAME]** field to document why the discount was applied.
Click **\[BUTTON LABEL]** to apply the quote-level discount.
The quote total recalculates to reflect both line item and quote-level discounts.
## What happens next
The discount appears on the quote and in the Dealroom once you publish it. Your customer sees the original price, the discount applied, and the final amount.
If your organization requires approval for discounts above a certain threshold, the quote may move to **Pending approval** status automatically.
Quote-level discounts apply to the subtotal after all line item discounts have been calculated.
## Troubleshooting
Verify the quote is in **Draft** status. Published or approved quotes must be duplicated or have changes requested before you can modify discounts.
Check your user role permissions with your admin if discount fields are not visible.
Ensure you clicked the save or apply button after entering the discount value. Refresh the page if the total hasn't updated.
For quote-level discounts, confirm that line items exist—quote discounts only apply to the subtotal.
Your organization may have approval rules for discounts exceeding a certain percentage or amount. Submit the quote for approval or contact your manager.
## Related resources
Learn how to add line items before applying discounts
Send quotes with discounts through the approval workflow
Understand quote statuses and state transitions
Share your discounted quote with customers
# Create a quote
Source: https://docs.qwoty.io/user-guide/quotes/how-tos/create-quote
Create a new quote in Qwoty from the quotes list in a few clicks
Quotes are at the heart of your sales activity in Qwoty. You can view all your quotes and create new ones from the quotes list.
This guide walks you through creating a new quote and selecting the right template, catalog, and pricebook.
## Prerequisites
Before creating a quote, ensure your workspace has:
* At least one Template configured
* A Catalog with products
* A Pricebook (price grid)
If you're an Admin, you can configure these in the settings. Contact your Admin if you don't have access.
## Access the quotes list
Navigate to **Project** > **Quote** in the side menu.
The list displays all quotes in your workspace with the following columns:
* **Project**: Unique identifier in the format `QUO-YYYY-NNNN`
* **Status**: Current state of the quote (Draft, Approved, Published, etc.)
* **Customer**: Company name and primary contact
* **Title**: Quote name (optional)
* **Created**: Date and time of creation
Use the **search bar** to find a quote by number, customer name, or title. Use the pagination at the bottom to navigate through results.
## Create a quote
Click **New quote** at the top right of the quotes list.
In the **New project** modal, complete the following fields:
* **Customer** (optional): Select the customer company from the dropdown
* **Primary contact** (optional): Choose the contact associated with the customer
* **Template** (required): Select the sales template
* **Catalog** (required): Select the product catalog
* **Pricebook** (required): Select the applicable pricebook
When you select a **Template**, the **Catalog** and **Pricebook** fields are automatically pre-filled based on the template configuration. You can change them if needed.
Click **Create**.
## What happens next
You're redirected to the quote editor where you can add products, configure quantities, apply discounts, and build your proposal.
The quote is created with a **Draft** status. It remains in Draft until you submit it for approval or publish it directly to the Dealroom.
You can create a quote without selecting a customer and associate one later. This is useful when preparing a proposal before you have all prospect details.
## Related resources
Add products and configure your quote
Configure templates for your sales process
Create and organize product catalogs
Set up price grids for customer segments
# Manage expired and lost quotes
Source: https://docs.qwoty.io/user-guide/quotes/how-tos/manage-expired-and-lost
Mark quotes as Expired or Lost to keep your pipeline clean and track deals that didn't close
Quotes that don't move forward need proper closure to keep your pipeline accurate. You can manually mark a quote as **Expired** when its validity period has passed, or as **Lost** when the deal didn't close. This helps your team track conversion rates and analyze why deals fail.
Qwoty can also automatically expire quotes based on the validity period you define in the template.
## Access the quote
In the left side menu, click **Project** > **Quote**.
Click the quote you want to mark as expired or lost.
## Mark a quote as Expired
Use this status when the quote's validity period has ended and the customer hasn't signed.
In the top right of the quote, click the **⋮** (three-dot) menu.
Click **Set status to expired** from the list.
Confirm the action if prompted.
The quote is now marked as **Expired** and removed from your active pipeline.
If you set a validity period in the quote template, Qwoty automatically moves quotes to **Expired** status after the deadline passes.
## Mark a quote as Lost
Use this status when the customer decided not to proceed with the purchase.
In the top right of the quote, click the **⋮** (three-dot) menu.
Click **Set status to lost**.
In the dialog box, select or enter the reason the deal was lost (e.g., `Price too high`, `Competitor chosen`, `Budget cut`).
Confirm the action if prompted.
The quote is now marked as **Lost** and removed from your active pipeline.
Tracking lost reasons helps you identify patterns and improve your sales process. Review lost deals regularly with your team.
## What happens next
* The quote is removed from your active pipeline but remains searchable in the **Quotes** list
* The Dealroom (if published) displays the updated status to the customer
* No further actions can be taken on the quote unless you reopen it
* Reporting data is updated to reflect the closed status
## Reopen an expired or lost quote
If a customer returns after a quote is marked as **Expired** or **Lost**, you can reopen it.
Navigate to the expired or lost quote.
Click the **⋮** (three-dot) menu and select the appropriate status to restart the quote.
If needed, update the pricing, products, or validity period before republishing.
If you reopen an expired quote, make sure to update the validity period and verify that pricing is still accurate.
## Troubleshooting
Check your user role. Only **Sales Reps**, **Managers**, and **Admins** can change quote statuses. **Viewers** have read-only access.
Check the validity period set in the quote. Navigate to the quote details and review the **Valid until** date. If the template has automatic expiration enabled, quotes will expire after this date.
Make sure you entered a lost reason when marking the quote as **Lost**. If you skipped this step, reopen the quote, mark it as **Lost** again, and add the reason.
## Related resources
Learn about all quote statuses and transitions
Set default validity periods and expiration rules
Analyze expired and lost quotes to improve conversion
Restart a closed deal with a returning customer
# Publish to Dealroom
Source: https://docs.qwoty.io/user-guide/quotes/how-tos/publish-to-dealroom
Share your quote with clients via email or direct link and enable e-signature
Once your quote is finalized and approved (if required), you can publish it to the Dealroom. Qwoty offers two sharing methods: sending by email or sharing a unique link via your preferred channel.
## Prerequisites
* Quote must be in **Approved** status or **Draft** (if approval is not required)
* At least one recipient contact assigned to the quote
## Access the share modal
1. Open the quote in the editor
2. Click **Share** in the top toolbar
The **Share new quote** modal opens with two tabs: **Share Dealroom** and **Get link**.
## Share via email
The **Sender** field is automatically set to you or the quote owner.
Review the **Recipients** list. Remove a recipient by clicking the cross icon on their tag.
Enter a custom **Email subject** or leave blank to use the default.
You can use dynamic variables:
* `{{owner.first_name}}` — Sales rep first name
* `{{business_unit.name}}` — Selling entity name
Enter a **Message** that will appear in the email body alongside the Dealroom link.
The **Automatic reminder** section sends follow-up emails if no signature is received.
Automatic reminders save you from manual follow-ups and increase signature rates.
Click **Send quote by email**.
Recipients receive an email with a link to the Dealroom where they can view, comment on, and sign the quote.
## Share via direct link
In the **Share new quote** modal, click the **Get link** tab.
Click the copy icon next to the quote link to copy it to your clipboard.
Paste the link into your preferred channel: custom email, WhatsApp, SMS, instant messaging, or any other platform.
## Download the PDF
Click **PDF** in the top toolbar of the quote editor to download a PDF version. Use it for email attachments or printing.
## What happens next
* The quote status changes to **Published**
* Recipients can access the Dealroom to view the quote
* You can track views and engagement in the quote activity log
* When a recipient signs, the quote moves to **Pending signature** or **Accepted**
## Related resources
Configure electronic signature for quotes
Learn how clients interact with the Dealroom
Modify quote content and settings
Monitor views, comments, and engagement
# Submit a quote for approval
Source: https://docs.qwoty.io/user-guide/quotes/how-tos/submit-for-approval
Send a quote to your manager for review and approval before publishing to the customer
When your quote is ready, you must submit it for approval if your organization requires manager review. The quote moves from **Draft** to **Pending approval** status and your manager receives a notification.
This guide walks you through submitting a quote and tracking its approval status.
## Prerequisites
* You have created a quote in **Draft** status
* Your user role is **Sales Rep** or higher
* Your organization has approval workflows enabled for the quote template
## Submit the quote
Navigate to **Project** > **Quote** in the side menu and click the quote you want to submit.
The quote must be in **Draft** status to submit for approval.
Verify all quote information is accurate:
* Line items, quantities, and pricing
* Discounts and payment terms
* Customer information
* Expiration date
You cannot edit the quote after submission. If changes are needed during approval, your manager will move it to **Changes requested** status.
Click the **\[BUTTON LABEL]** button in the top right corner of the quote page.
A confirmation dialog appears.
Enter a message for the approver in the **\[FIELD NAME]** field if you need to provide context or highlight specific items.
Click **\[BUTTON LABEL]** to confirm.
## What happens next
* The quote status changes to **Pending approval**
* Your manager receives an email notification with a link to review the quote
* You receive a confirmation email that the quote was submitted
* The quote is locked for editing until approved or returned
You can track approval progress on the quote detail page. The **\[FIELD NAME]** section shows who needs to approve and their current status.
## Troubleshooting
Your quote template may not require approval, or you may not have permission to submit. Contact your **Admin** to verify approval workflow settings.
The button is also hidden if the quote is not in **Draft** status.
Ask your manager to check their spam folder and verify their email address is correct in their user profile. **Admins** can resend notifications from the quote page.
Alternatively, share the direct quote link from your browser's address bar.
Contact the assigned approver and ask them to move the quote to **Changes requested** status. You'll regain edit access and can resubmit after making updates.
If your approver is unavailable, an **Admin** can change the quote status.
## Related resources
Learn how managers review and approve submitted quotes
Understand the complete quote lifecycle and status transitions
Make changes to a quote in Draft or Changes Requested status
View what each role can do in Qwoty
# Quotes
Source: https://docs.qwoty.io/user-guide/quotes/introduction
Create, configure, and manage quotes from draft to signature
Quotes are the core output of Qwoty. You build them from templates, configure products, apply pricing, and send them to customers through the Dealroom. This section covers the full quote lifecycle from creation to acceptance.
## Reference
Understand quote structure, sections, and key fields
Status flow from Draft to Accepted and alternative paths
Apply pricebooks, discounts, and pricing rules
Configure and manage quote approval processes
## Guides
Start a new quote from a template
Add and customize products and bundles
Share your quote with customers
Create new versions and manage changes
Reuse existing quotes as starting points
Generate PDFs and other formats
# Quote lifecycle
Source: https://docs.qwoty.io/user-guide/quotes/reference/lifecycle
Understand the complete journey of a quote from creation to contract.
A quote in Qwoty moves through a structured lifecycle from initial draft to final acceptance. Each status represents a milestone in the sales process and determines what actions are available.
Understanding the lifecycle helps you track deal progress, set up appropriate approval workflows, and automate downstream processes like order generation and contract creation.
## Status flow
Quotes follow a primary path through these statuses:
* **Draft**: Initial creation and editing phase
* **Pending approval**: Submitted for manager review (optional, workflow-dependent)
* **Approved**: Cleared for publication
* **Published**: Sent to the Dealroom and visible to the customer
* **Pending signature**: Customer is reviewing and preparing to sign
* **Accepted**: Quote is signed, triggering automatic order creation
Alternative statuses handle exceptions and negative outcomes:
* **Changes requested**: Approver requires modifications before approval
* **Expired**: Quote passed its validity date without acceptance
* **Lost**: Deal did not close, competitor won or customer canceled
* **Canceled**: Quote withdrawn by the sales team
Not all quotes pass through approval. If your template doesn't require approval, quotes move directly from **Draft** to **Approved** when published.
## Lifecycle diagram
```mermaid theme={null}
graph TD
A[Draft] --> B[Pending approval]
A -.-> C[Approved]
B --> D[Changes Requested]
B --> C
D --> A
C --> E[Published]
E --> F[Pending signature]
F --> G[Accepted]
E -.-> H[Expired]
E -.-> I[Lost]
A -.-> J[Canceled]
E -.-> J
F -.-> J
style G fill:#90EE90
style H fill:#FFB6C1
style I fill:#FFB6C1
style J fill:#FFB6C1
```
## Status details
### Draft
The quote is being created or edited by a sales rep. All quote fields, line items, and configurations can be modified.
**Available actions:**
* Edit all quote details
* Add or remove products
* Apply discounts
* Submit for approval or publish directly (depending on workflow)
* Delete the quote
**Who can access:** Quote owner, managers, admins
### Pending approval
The quote is locked and awaiting manager review. Sales reps cannot modify the quote during this phase.
**Available actions:**
* Approve the quote (manager/admin only)
* Request changes with feedback (manager/admin only)
* View quote details (all roles)
**Who can access:** All users (read-only for sales reps)
Approval requirements are configured in the template settings. Common triggers include total value thresholds, specific discount levels, or custom fields.
### Changes Requested
An approver returned the quote with modification requests. The quote returns to draft-like editing capabilities.
**Available actions:**
* Edit quote based on feedback
* View approver comments
* Resubmit for approval
**Who can access:** Quote owner, managers, admins
### Approved
The quote passed approval (or didn't require it) and is ready to send to the customer.
**Available actions:**
* Publish to Dealroom
* Edit quote (returns to Draft)
* Generate PDF preview
**Who can access:** Quote owner, managers, admins
### Published
The quote is live in the Dealroom and visible to the customer. The Dealroom URL is active and can be shared.
**Available actions:**
* View Dealroom
* Track customer engagement
* Move to Pending signature manually
* Mark as Lost or Expired
**Who can access:** Quote owner, managers, admins, customer (via Dealroom link)
**Restrictions:** Quote line items and pricing are locked. Only metadata and notes can be edited.
### Pending signature
The customer is actively reviewing the quote and preparing to sign. This status indicates high intent.
**Available actions:**
* Monitor signature progress
* Send reminders
* Mark as Accepted (if signed)
* Mark as Lost or Canceled
**Who can access:** Quote owner, managers, admins, customer (via Dealroom link)
### Accepted
The customer signed the quote. Qwoty automatically generates an order and may create or update a contract.
**Available actions:**
* View generated order
* View contract
* Export for downstream systems
**Who can access:** All users (read-only)
**Automatic processes:**
* Order creation
* Contract generation (if configured)
* Webhook notifications (if configured)
When a quote reaches **Accepted**, it becomes the source of truth for fulfillment. The order is locked and cannot be modified without creating a new quote.
### Expired
The quote passed its validity date without customer acceptance. Customers can no longer access the Dealroom.
**Available actions:**
* Clone to create a new quote
* Archive for reporting
**Who can access:** Quote owner, managers, admins
### Lost
The deal did not close. Use this status to track lost reasons for pipeline analysis.
**Available actions:**
* Add lost reason and notes
* Clone to create a new opportunity
**Who can access:** Quote owner, managers, admins
### Canceled
The sales team withdrew the quote before completion.
**Available actions:**
* Add cancellation notes
* Clone to create a revised quote
**Who can access:** Quote owner, managers, admins
## Common questions
You cannot edit line items or pricing once a quote is published. You can update metadata fields and internal notes. To modify pricing or products, cancel the quote and create a new version by cloning it.
The Dealroom becomes inaccessible to customers, and the signature option is disabled. The quote remains in your system for reporting. Clone the expired quote to create a new version with an updated validity date.
Yes. Approval is configured at the template level. If your template doesn't define approval rules, quotes move directly from **Draft** to **Approved** when you publish them.
From **Approved** status, you can edit the quote, which automatically returns it to **Draft**. From **Published** or later statuses, you must cancel the current quote and clone it to create an editable copy.
**Lost** means the customer chose not to proceed (competitor won, budget cut, etc.). **Canceled** means your team withdrew the quote (pricing error, wrong product, customer requested different terms). Use **Lost** for pipeline analysis and win/loss reporting.
No. Once expired, the Dealroom is locked. Clone the quote, update the validity date, and publish the new version. Share the new Dealroom link with your customer.
## Related
Configure multi-level approvals based on quote value, discount, or custom rules
Learn how to manually update quote status and add status comments
Understand how orders are generated from accepted quotes
Explore the customer-facing space for quote review and signature
# Quote anatomy
Source: https://docs.qwoty.io/user-guide/quotes/reference/quote-anatomy
Understanding the structure and components of a Qwoty quote
A Qwoty quote is composed of several interconnected sections that define what you're selling, how it's priced, and how the customer will pay. Understanding the anatomy of a quote helps you build accurate proposals and troubleshoot pricing issues.
Every quote inherits its baseline configuration from a Template, but you can customize individual sections for each deal.
## Core components
A quote consists of six primary sections:
* **Header**: customer information, quote metadata, expiration date, and overall status
* **Line items**: products, bundles, and services being sold with quantities and pricing
* **Pricing logic**: discounts, fees, taxes, and pricing model (one-time, recurring, usage-based)
* **Payment terms**: billing frequency, payment schedule, and installment options
* **Summary**: totals broken down by pricing model and time period
* **Contract variables**: dynamic data used to populate the contract document
Each section pulls data from different sources in Qwoty—catalogs, pricebooks, payment term templates, and customer records.
## Quote structure flow
```mermaid theme={null}
graph TD
A[Template] --> B[Quote Header]
A --> C[Line Items]
A --> D[Payment Terms]
A --> E[Contract Model]
C --> F[Pricing Logic]
F --> G[Summary]
G --> H[Contract Variables]
B --> H
D --> H
style A fill:#e3f2fd
style G fill:#f3e5f5
style H fill:#fff3e0
```
## Header section
The header contains identifying information and controls the quote lifecycle.
### Key header fields
| Field | Description | Source |
| --------------- | --------------------------------------- | ----------------------------------- |
| Quote number | Unique identifier, auto-generated | System or custom format |
| Customer | Company and contact receiving the quote | Customer record |
| Expiration date | Deadline for customer acceptance | Template default or manual override |
| Status | Current position in quote workflow | System-managed based on actions |
| Owner | Sales rep responsible for the quote | User assignment |
| Currency | Pricing currency for all line items | Template or customer default |
You cannot change the currency after adding line items to a quote.
### Custom fields
You can add custom fields to the header to capture deal-specific information like competitor names, deal stage, or internal notes. Custom fields are available as contract variables.
## Line items section
Line items define what the customer is purchasing. Each line item references a product from the quote's catalog and applies pricing from the assigned pricebook.
### Line item attributes
* **Product**: the catalog item being sold
* **Quantity**: number of units or licenses
* **Pricing model**: one-time, recurring (monthly, annual), or usage-based
* **Unit price**: base price from the pricebook
* **Discount**: percentage or flat amount reduction
* **Net price**: final price after discounts and before taxes
### Bundles and parent-child relationships
Bundles appear as expandable line items. The bundle acts as a parent with nested child products underneath. You can price the bundle as a whole or price individual components.
## Pricing logic section
This section calculates the final numbers based on line items, discounts, and additional charges.
### Pricing components
Apply line-level, quote-level, or promotional discounts as percentages or fixed amounts
Add setup fees, implementation charges, or other one-time costs
Calculate sales tax or VAT based on customer location and product tax rules
Schedule price increases or decreases over the contract term
### Discount hierarchy
Discounts are applied in a specific order:
1. Line item discount (applied first)
2. Bundle discount (if the line item is part of a bundle)
3. Quote-level discount (applied last)
Each discount compounds on the result of the previous discount, not the original price.
## Payment terms section
Payment terms define when and how the customer pays. These settings come from the template but can be overridden per quote.
### Common payment configurations
| Configuration | Use case |
| ------------------- | --------------------------------------------- |
| Due on receipt | Standard B2B invoicing |
| Net 30/60/90 | Payment due X days after invoice |
| Installments | Split payment into multiple scheduled charges |
| Upfront + recurring | Initial setup fee plus ongoing subscription |
| Annual prepay | Customer pays full year in advance |
Use installments to improve deal velocity for high-value quotes. Customers are more likely to commit when the upfront cost is lower.
## Summary section
The summary aggregates all pricing components and displays totals broken down by pricing model and time period.
### Summary breakdowns
* **One-time total**: all upfront charges including fees, setup, and one-time products
* **Recurring total**: monthly or annual subscription amounts
* **First invoice**: what the customer pays initially (one-time + first recurring period)
* **Contract value**: total value over the entire contract term (TCV)
* **Annual contract value**: annualized value for multi-year deals (ACV)
The summary automatically updates when you modify line items, discounts, or payment terms.
## Contract variables
Variables are placeholders in your contract template that pull live data from the quote, customer record, and company settings.
### Variable categories
Reference quote data like `{{quote.total}}`, `{{quote.number}}`, `{{quote.start_date}}`, or `{{quote.end_date}}`.
Insert customer details like `{{customer.name}}`, `{{customer.address}}`, `{{customer.billing_contact}}`, or custom fields.
Include your company information like `{{company.name}}`, `{{company.legal_entity}}`, or `{{company.support_email}}`.
Loop through products with `{{#line_items}}` to generate dynamic product tables in your contract.
Use any custom field added to the quote header as a variable in your contract.
Variables are resolved when you publish the quote and create the Dealroom.
## Common questions
No. The template is locked when the quote is created. To use a different template, you must create a new quote. You can clone the existing quote to preserve line items.
The unit price remains the same unless volume-based pricing tiers are configured in the pricebook. The net price recalculates automatically based on the new quantity.
No. All line items must come from the single catalog assigned to the quote's template. To sell cross-catalog products, create a unified catalog or use multiple quotes under the same contract.
Expand the bundle line item to view child products and their individual prices. The bundle total equals the sum of its components unless you've applied a bundle-level discount.
## Related
Step-by-step guide to building your first quote
Understanding quote statuses and workflow
How templates control quote configuration
Complete reference for dynamic contract fields
# Versions and history
Source: https://docs.qwoty.io/user-guide/quotes/reference/versions-and-history
Understand how Qwoty tracks quote changes and maintains version history
Qwoty automatically tracks every change made to a quote, creating a complete audit trail. Each time you publish a quote or make significant modifications, the system creates a new version, allowing you to compare changes and restore previous states.
## What is version control
Version control preserves the state of a quote at specific points in time. When you update pricing, add products, or modify terms on a published quote, Qwoty saves the previous version before applying changes.
Key features:
* **Automatic versioning** — new versions are created when you republish a quote
* **Change tracking** — every modification is logged with timestamp and user
* **Version comparison** — side-by-side view of what changed between versions
* **Restoration** — ability to revert to a previous version if needed
* **Client visibility** — customers see only the current published version in the Dealroom
## How versioning works
```mermaid theme={null}
sequenceDiagram
participant SR as Sales Rep
participant Q as Quote
participant DL as Dealroom
participant C as Customer
SR->>Q: Create quote (v1.0)
Q->>Q: Status: Draft
SR->>Q: Publish
Q->>DL: v1.0 visible
DL->>C: Customer views v1.0
C->>SR: Request changes
SR->>Q: Edit (creates draft)
Q->>Q: Status: Draft
SR->>Q: Publish changes
Q->>Q: Save as v1.1
Q->>DL: v1.1 visible (v1.0 archived)
DL->>C: Customer sees v1.1
```
## Version numbering
Qwoty uses a semantic versioning approach to track quote iterations:
| Version type | Format | When created | Example |
| ------------- | ------------- | ----------------------------------- | ------------ |
| Major version | `X.0` | First publish or significant change | `1.0`, `2.0` |
| Minor version | `X.Y` | Republish with updates | `1.1`, `1.2` |
| Draft state | Not versioned | Any edit before publish | — |
Only published quotes receive version numbers. Draft changes are tracked in the activity log but don't create a new version until you publish.
## Change history
The activity log records all modifications made to a quote, regardless of version status. Each entry includes:
* **User** — who made the change
* **Timestamp** — when the change occurred
* **Action** — what was modified (e.g., "Updated line item," "Changed discount")
* **Details** — specific values before and after
### What gets tracked
* Line item additions, removals, and edits
* Price and discount changes
* Quote settings and metadata
* Status transitions
* Approval decisions
* Customer interactions in the Dealroom
* Signature events
## Version comparison
You can compare any two versions to identify exactly what changed:
Open the quote and navigate to **\[MENU PATH]**.
Choose two versions from the list. The comparison highlights differences in pricing, products, and terms.
Modified fields appear highlighted. Additions show in green, removals in red, and changes show both old and new values.
Use version comparison during negotiations to show clients exactly what changed based on their feedback.
## Restoring previous versions
If you need to revert to an earlier version:
1. Open the quote version history
2. Select the version you want to restore
3. Click **\[BUTTON LABEL]**
4. Qwoty creates a new draft with the content from that version
5. Review and publish to make it the active version
Restoring doesn't delete newer versions—it creates a new version based on the old content. The complete history remains intact.
## Version visibility in Dealroom
Customers in the Dealroom always see only the most recently published version. Previous versions are visible only to internal users with appropriate permissions.
When you publish a new version:
* The Dealroom updates automatically
* Customers receive a notification of the changes (if enabled)
* Previous versions move to the archived state
* Comments and activity from earlier versions remain accessible
## Common questions
No. Qwoty maintains a complete audit trail for compliance and traceability. All versions are permanently retained, though only users with appropriate permissions can view historical versions.
When you clone a quote, the new quote starts fresh at version `1.0` (after first publish). The version history from the original quote is not copied, though the activity log notes the source quote.
Draft changes appear in the activity log but don't create a numbered version. A version is only created when you publish the quote.
No. Version comparison works only within a single quote. To compare different quotes, you need to review them separately.
Version history is retained for the lifetime of the quote and associated Contract. Qwoty does not automatically purge historical versions.
## Related
Learn how status changes are tracked in version history
Understand how approvals interact with versions
See how customers view published quote versions
Learn about system-wide audit logging
# CRM integrations
Source: https://docs.qwoty.io/user-guide/settings/integrations/crm
Connect Qwoty to your CRM to sync customers, opportunities, and deals
CRM integrations keep your customer data synchronized between Qwoty and your sales platform. When enabled, Qwoty can automatically create or update CRM records when you create quotes, publish Dealrooms, or close deals.
Qwoty supports native integrations with Salesforce, HubSpot, and Pipedrive.
## What CRM integrations do
CRM integrations establish a two-way connection between Qwoty and your sales platform. This keeps customer records, deal status, and quote data in sync across both systems.
**Key capabilities:**
* Pull customer and opportunity data into Qwoty when creating quotes
* Push quote totals, line items, and status updates back to the CRM
* Automatically create or update CRM deals when quotes reach specific statuses
* Map Qwoty fields to custom CRM fields
* Sync contract records and order details
CRM integrations require **Admin** role permissions to configure. Once enabled, all users can benefit from the sync.
## How syncing works
```mermaid theme={null}
sequenceDiagram
participant CRM
participant Qwoty
participant Sales Rep
Sales Rep->>Qwoty: Create quote for customer
Qwoty->>CRM: Fetch customer & opportunity data
CRM-->>Qwoty: Return account details
Sales Rep->>Qwoty: Publish quote to Dealroom
Qwoty->>CRM: Update opportunity stage
Sales Rep->>Qwoty: Quote accepted
Qwoty->>CRM: Create closed-won deal + order record
```
## Supported platforms
The Salesforce integration connects to Accounts, Opportunities, Quotes, and Products. You can sync both standard and custom objects.
**Synced objects:**
* Accounts → Customers
* Opportunities → Quotes
* Products → Catalog items
* Orders → Qwoty Orders
Qwoty supports both Salesforce Classic and Lightning Experience.
The HubSpot integration syncs Companies, Deals, Line Items, and Contacts through the HubSpot CRM API.
**Synced objects:**
* Companies → Customers
* Deals → Quotes
* Line Items → Quote line items
* Contacts → Customer contacts
Custom properties are supported for field mapping.
The Pipedrive integration connects Organizations, Deals, and Products using the Pipedrive REST API.
**Synced objects:**
* Organizations → Customers
* Deals → Quotes
* Products → Catalog items
You can map deal stages to Qwoty quote statuses.
## Configuring an integration
Go to **Settings** → **Integrations** → **CRM** and select your platform.
Click **\[BUTTON LABEL]** and complete the OAuth flow in your CRM. Grant Qwoty the requested permissions.
Map Qwoty fields to your CRM fields. Required mappings include customer name, opportunity ID, and quote total. Add custom field mappings as needed.
Choose which Qwoty events trigger CRM updates. Common triggers include quote creation, status changes, and order generation.
Click **\[BUTTON LABEL]** to verify data flows correctly between systems.
Once activated, the integration begins syncing immediately. Test thoroughly in a sandbox environment before enabling in production.
## Field mapping
Field mapping defines how data flows between Qwoty and your CRM. You can map both standard and custom fields.
**Standard mappings:**
| Qwoty field | CRM field (typical) | Direction |
| ------------------ | ------------------------ | ----------- |
| Customer name | Account/Company Name | Both |
| Customer email | Account Email | Both |
| Quote total | Opportunity Amount | Qwoty → CRM |
| Quote status | Opportunity Stage | Both |
| Quote created date | Opportunity Created Date | Qwoty → CRM |
**Custom field mapping:**
Add custom mappings to sync additional data like industry, region, or deal type. Custom fields must exist in both systems before mapping.
## Sync behavior and rules
Qwoty's CRM sync follows these rules to prevent conflicts and data loss:
**Sync direction:**
* Customer and opportunity data typically flows **CRM → Qwoty** when creating quotes
* Quote status, totals, and line items flow **Qwoty → CRM** when quotes are updated
* Two-way sync is available for customer contact information and opportunity notes
**Conflict resolution:**
* CRM data takes precedence for customer master records
* Qwoty data takes precedence for quote calculations and line items
* Manual changes in either system trigger a sync event
**Sync frequency:**
* Real-time for status changes and critical updates
* Batched every 15 minutes for non-critical data
* On-demand sync available via **\[BUTTON LABEL]**
Deleted records in Qwoty are not deleted in your CRM. They are marked with a custom field to preserve audit history.
## Disconnecting an integration
To disconnect a CRM integration, go to **Settings** → **Integrations** → **CRM** and click **\[BUTTON LABEL]** .
Disconnecting stops all syncing immediately. Existing data in both systems remains unchanged. You can reconnect at any time without data loss.
## Common questions
No. Qwoty supports one active CRM connection per workspace. To switch CRMs, disconnect the current integration before connecting a new one.
Failed syncs are logged in **Settings** → **Integrations** → **Sync Logs**. Qwoty retries automatically up to three times. If the failure persists, you receive an email notification and can manually retry.
Yes. After enabling the integration, use the **\[BUTTON LABEL]** option to sync existing quotes. This is a one-time operation and may take several minutes depending on data volume.
Yes. You can map any custom field in your CRM to Qwoty fields during setup. Both text and picklist field types are supported.
Quote line items sync as individual product records or line item objects in your CRM. The structure depends on your CRM's data model. Bundles are synced as parent-child relationships where supported.
## Related topics
Connect Qwoty to custom systems using REST API and webhooks
Learn how customers and contacts are managed in Qwoty
Understand quote statuses and how they map to CRM stages
Control who can configure and use integrations
# ERP integrations
Source: https://docs.qwoty.io/user-guide/settings/integrations/erp
Connect Qwoty to your ERP system to sync orders, customers, and products automatically
Qwoty integrates with your ERP (Enterprise Resource Planning) system to automate the flow of data between sales and operations. When a quote reaches the Accepted status and generates an Order, the integration can push that order directly to your ERP for fulfillment, invoicing, and accounting.
ERP integrations eliminate duplicate data entry, reduce errors, and ensure your finance and operations teams work from the same quote data your sales team negotiated.
## How ERP integrations work
Qwoty supports bi-directional sync between your CPQ instance and your ERP system:
* **Orders flow out**: When a quote is accepted, Qwoty generates an Order and pushes it to your ERP with line items, pricing, customer details, and payment terms
* **Products sync in**: Your ERP product catalog syncs to Qwoty, keeping SKUs, descriptions, and base prices up to date
* **Customers sync both ways**: Customer records created in Qwoty can push to your ERP, and existing ERP customers can be imported to avoid duplicates
* **Invoices reference back**: Invoices created in your ERP can link back to the original Qwoty quote and order for traceability
Orders are automatically created when a quote reaches the Accepted status. The ERP integration listens for this event and triggers the sync.
## Supported ERP systems
Qwoty offers native integrations and API-based connectors for common ERP platforms:
| ERP system | Integration type | Sync frequency | Order push | Product sync | Customer sync |
| ---------------------- | ---------------- | -------------- | ---------- | ------------ | ------------- |
| NetSuite | Native | Real-time | ✓ | ✓ | ✓ |
| SAP S/4HANA | API connector | Scheduled | ✓ | ✓ | ✓ |
| Microsoft Dynamics 365 | Native | Real-time | ✓ | ✓ | ✓ |
| Sage Intacct | API connector | Scheduled | ✓ | ✓ | ✓ |
| Odoo | API connector | Scheduled | ✓ | ✓ | ✓ |
| Custom ERP | REST API | Custom | ✓ | ✓ | ✓ |
If your ERP is not listed, you can build a custom integration using Qwoty's REST API and webhooks. Contact your implementation team for guidance.
## Setting up an ERP integration
Navigate to **\[MENU PATH]** > **Integrations** > **ERP**. Select your ERP system from the dropdown and click **\[BUTTON LABEL]**.
Provide your ERP credentials or API key. For OAuth-based ERPs like NetSuite, you'll be redirected to authorize Qwoty to access your account.
Choose which data to sync:
* **Order sync**: Enable automatic order creation when quotes are accepted
* **Product sync**: Import products from your ERP catalog into Qwoty
* **Customer sync**: Sync customer records bi-directionally or one-way
* **Sync frequency**: Set real-time or scheduled (hourly, daily)
Match Qwoty fields to your ERP fields. Common mappings include:
* Quote line items → ERP order line items
* Quote total → ERP order amount
* Payment terms → ERP payment conditions
* Customer name → ERP account name
Create a test quote, publish it to a Dealroom, and accept it. Verify the order appears correctly in your ERP with accurate line items and pricing.
Always test in a sandbox or staging environment before enabling the integration in production. Incorrect field mappings can create duplicate or malformed records in your ERP.
## Field mapping and custom attributes
Qwoty allows you to map custom quote fields to custom ERP fields using **\[FIELD NAME]**. This is useful when your ERP tracks attributes like project codes, cost centers, or delivery dates that don't exist in Qwoty's standard schema.
To create a custom mapping:
1. Navigate to **\[MENU PATH]** > **Field Mappings**
2. Click **\[BUTTON LABEL]**
3. Select the Qwoty field or custom attribute
4. Enter the corresponding ERP field API name
5. Choose the data type (text, number, date, picklist)
6. Set the sync direction: Qwoty → ERP, ERP → Qwoty, or bi-directional
Custom field mappings require Admin role permissions.
## Sync logs and error handling
Qwoty logs every sync event and provides visibility into failures. Access sync logs from **\[MENU PATH]** > **Sync History**.
Each log entry shows:
* **Timestamp**: When the sync occurred
* **Direction**: Qwoty → ERP or ERP → Qwoty
* **Object type**: Order, Product, or Customer
* **Status**: Success, Warning, or Error
* **Details**: Full API request and response for debugging
### Common sync errors
| Error | Cause | Resolution |
| ------------------------ | ---------------------------------------- | ------------------------------------------------------------------------- |
| `Invalid customer ID` | Customer record doesn't exist in ERP | Create the customer in your ERP or enable automatic customer creation |
| `Missing required field` | ERP requires a field not mapped in Qwoty | Add the required field to your quote template and map it |
| `Duplicate order number` | Order ID already exists in ERP | Check if the order was already synced, or adjust order numbering settings |
| `Authentication failed` | API credentials expired or revoked | Re-authenticate the integration from the settings page |
Set up email or Slack notifications for sync errors so your team can respond immediately. Configure alerts in **\[MENU PATH]** > **Notifications**.
## Common questions
No. Qwoty only syncs Orders to your ERP, and orders are created when quotes reach the Accepted status. Syncing draft or pending quotes would clutter your ERP with incomplete data.
If you need visibility into pipeline quotes, use Qwoty's reporting or a CRM integration instead.
Qwoty sends an update to the ERP to mark the order as canceled or void, depending on your ERP's capabilities. You can configure this behavior in **\[MENU PATH]** > **Sync Settings** > **\[FIELD NAME]**.
Some ERPs don't support order cancellation after creation. In those cases, you'll need to manually void or delete the order in your ERP.
Yes. Open the order detail page and click **\[BUTTON LABEL]** in the top-right corner. This re-sends the order to your ERP, useful when a sync fails or you've corrected a mapping error.
By default, the integration only syncs new records created after activation. To import historical orders, products, or customers, use the bulk import tool or contact support for a data migration.
Yes. Use the **\[BUTTON LABEL]** toggle on the integration settings page. This pauses all syncing but preserves your field mappings and authentication. Re-enable anytime by toggling it back on.
## Related
Learn how orders are automatically generated from accepted quotes
Understand how contracts group quotes and orders for the same deal
Build custom ERP integrations using Qwoty's REST API
Configure webhooks to notify your ERP when orders are created or updated
# Integrations
Source: https://docs.qwoty.io/user-guide/settings/integrations/introduction
Connect Qwoty with your CRM, accounting, and business tools
This section covers how to connect Qwoty with external systems like CRMs, accounting software, and other business tools. Learn how to configure, authenticate, and manage data sync between platforms.
## Reference
Complete list of supported third-party platforms and their capabilities
Configure authentication, field mapping, and sync preferences
Control when and how data flows between Qwoty and connected systems
Send real-time quote and order events to external endpoints
## Guides
Sync customers, opportunities, and quotes with Salesforce or HubSpot
Push orders and invoices to QuickBooks, Xero, or NetSuite
Match Qwoty fields to your CRM or ERP schema
Resolve common authentication and data sync issues
# Settings
Source: https://docs.qwoty.io/user-guide/settings/introduction
Configure your Qwoty workspace, manage users, and customize system behavior.
Settings let you configure your Qwoty workspace to match your sales process and business requirements. This section covers workspace configuration, user management, integrations, and customization options.
Access settings by clicking **Settings** in the main navigation. The available options depend on your user role—Admins have full access, while other roles may have limited permissions.
## Reference
Company details, regional settings, and workspace preferences
Add users, assign roles, and manage team permissions
Connect Qwoty with your CRM, accounting, and other tools
Add custom fields to quotes, customers, and products
Configure approval rules and thresholds
Customize notification and outreach emails
## Guides
Configure company details, currency, and regional preferences
Add users to your workspace and assign appropriate roles
Sync customers and deals with Salesforce or HubSpot
Set up automatic approval workflows based on deal size or discount
# Qwoty billing
Source: https://docs.qwoty.io/user-guide/settings/qwoty-billing
How Qwoty charges for your subscription, including plan tiers, usage-based pricing, and billing cycles.
Qwoty uses a subscription-based pricing model with tiered plans and optional usage-based add-ons. Your billing is managed through your account settings and depends on factors like the number of active users, quote volume, and enabled features.
## Subscription plans
Qwoty offers multiple plan tiers to match your organization's needs. Each tier includes a baseline feature set and quota limits.
* **E-sign**: Designed for small teams with basic CPQ needs
* **Business**: Includes advanced features like approval workflows and custom contract models
* **Pro**: Full platform access with dedicated support and custom integrations
Your plan determines access to features like the Product Configurator, Dealroom customization, and advanced reporting. Admins can upgrade or downgrade plans at any time.
Plan changes take effect at the start of your next billing cycle unless you
choose immediate upgrade.
## Billing cycles and payments
You can choose between monthly or annual billing cycles. Annual billing typically includes a discount compared to monthly pricing.
| Billing cycle | Payment frequency | Discount |
| ------------- | ----------------- | --------- |
| Monthly | Every month | None |
| Annual | Once per year | Up to 20% |
Qwoty accepts major credit cards and bank transfers for annual Enterprise plans. Payment methods are managed in your billing settings.
## Usage-based pricing
Certain features incur additional charges based on usage volume:
* **Quote volume**: Number of published quotes per billing period
* **Dealroom seats**: Additional client users accessing Dealrooms
* **API calls**: Integrations exceeding the plan's API quota
* **Contract storage**: Document storage beyond the included limit
Usage charges are calculated monthly and added to your subscription invoice. You can view current usage metrics in your billing dashboard.
Set up usage alerts to receive notifications when approaching plan limits.
## Managing users and seats
Your subscription includes a set number of user seats based on your plan tier. User roles (Admin, Manager, Sales Rep, Viewer) don't affect seat pricing—all internal users count equally toward your limit.
To add users beyond your plan limit, you'll need to purchase additional seats. Seat charges are prorated when added mid-cycle.
Viewer roles count as full seats. Consider using Dealroom guest access for
external stakeholders instead.
## Invoices and billing history
Qwoty generates invoices automatically at the start of each billing period. Invoices include:
* Subscription plan charges
* Additional user seats
* Usage-based charges from the previous period
* Applicable taxes based on your billing address
You can download invoices and view billing history in **Settings -> Billing**. All invoices are sent to the billing contact email address.
## Common questions
If you exceed quote volume or other usage limits, Qwoty will continue to function normally. Overage charges will appear on your next invoice based on your plan's overage rate. Admins receive email notifications when approaching limits.
Yes. You can switch between monthly and annual billing at any time. When
moving from monthly to annual, you'll receive a prorated credit for the
current month. Changes take effect immediately and your renewal date will
adjust accordingly.
Admins can update payment methods in **\[MENU PATH]**. Changes apply to all future invoices. You'll receive confirmation once the new payment method is validated.
Qwoty will automatically retry failed payments three times over 10 days. You'll receive email notifications after each attempt. If all retries fail, your account enters a grace period where you can update payment information. Access is restricted after 15 days of non-payment.
## Related
Learn about user roles and permissions
Configure your account and organization details
# Manage roles
Source: https://docs.qwoty.io/user-guide/settings/users/manage-roles
Control user access and permissions with system and custom roles.
Roles define what users can do in Qwoty — which objects they can read, write, or delete, which settings they can manage, and which app-level actions they can perform. You assign one role to each user to control their permissions across the platform.
You must have the **Admin** role to view, create, or modify roles.
## System roles
Qwoty provides four predefined system roles that cover the most common permission levels. You cannot edit or delete system roles.
* **Admin** — Full access to all workspaces, settings, and configuration. Can manage users, roles, templates, catalogs, and all system features.
* **Manager** — Access to the entire workspace. Can approve quotes, view reports, and manage team activities.
* **Sales Rep** — Can create and manage quotes, customers, and Dealrooms. No access to system settings or team-wide reports.
* **Viewer** — Read-only access. Can view quotes and data but cannot create or edit anything.
The API name for **Sales Rep** is `Sale` for backwards compatibility with earlier versions.
## Custom roles
You can create custom roles to define granular permissions that match your organization's needs. Custom roles appear with a **Custom** badge in the roles list and can be edited or deleted at any time.
Use custom roles to:
* Grant partial access to specific modules (for example, catalogs only, or reports only)
* Restrict actions on an object family (for example, read customers but never delete)
* Scope project visibility by team, business unit, or workspace
* Enable or disable transversal actions like CSV imports and exports
## The permission model
A Qwoty role configures permissions across three distinct areas.
### Objects — CRUD per object family
The **Objects** section controls what users can read, write, or delete, broken down by object family. You set one checkbox per action per family.
| Object family | Objects concerned | Actions |
| ---------------------- | -------------------------------- | --------------------- |
| **Project** | Quote, contract, order | Read / Write / Delete |
| **Products & Pricing** | Catalogs, Products, price grid | Read / Write / Delete |
| **Customers** | Customers | Read / Write / Delete |
| **Workflow** | Approval, workflow, configurator | Read / Write / Delete |
| **Content** | Contract model, media, content | Read / Write / Delete |
| **Templates** | Project templates | Read / Write / Delete |
Grant **Read** without **Write** to give users visibility without letting them change records — useful for Finance or Executive roles.
### Project Access Level
At the top of the **Objects** section, the **Project Access Level** dropdown controls the scope of projects a user can see, regardless of their Read/Write/Delete rights.
* **Workspace** — The user can access projects across the entire workspace
* **Team** — The user only accesses projects created by their team
* **Personal** — The user only accesses projects they created themselves
Project Access Level acts as a filter on top of CRUD rights. A user with `Project: Read` set to true but a `Personal` access level will only see their own quotes, even if the workspace contains thousands.
### Settings — toggle per admin area
The **Settings** section controls access to administration zones. Each zone has a single **Authorize** toggle.
| Setting | What it unlocks |
| ---------------- | ------------------------------------------- |
| **Workspace** | Set global workspace preferences |
| **User & Teams** | Add or remove users and define teams |
| **Roles** | Define user roles and access levels |
| **Data** | Edit CPQ data structure and fields |
| **Developers** | Manage integrations, API keys, and webhooks |
| **Organization** | Manage multi-tenant org and billing |
Settings access is binary — a user either has full access to a zone or none. If you need finer control, keep the zone off and delegate configuration to an Admin.
### Apps — transversal app-level actions
The **Apps** section controls transversal capabilities that apply across modules.
| Action | Description |
| ------------------------------ | ------------------------------------------------------------------------------------- |
| **Import CSV** | Allow importing data from CSV files |
| **Export CSV** | Allow exporting data to CSV files |
| **Overpass sales restriction** | Allow bypassing restrictions such as approvals, discount caps, and pricing guardrails |
**Overpass sales restriction** gives users the ability to skip approval workflows and discount limits. Grant this only to senior roles — typically VP Sales, CRO, or equivalent.
## How roles work with teams
Roles define **what** users can do. Teams define **which resources** they can access. The Project Access Level in a role connects the two.
| Element | Controls | Example |
| ------------------------ | -------------------------- | ------------------------------------------------- |
| **Role** | Feature access and actions | A Sales Rep can write quotes; a Viewer cannot |
| **Team** | Group of users | A Sales Rep belongs to the France team |
| **Project Access Level** | Scope of visible projects | `Team` limits the Sales Rep to France team quotes |
When you assign a user to a team and set their role's Project Access Level to `Team`, they only see resources scoped to that team. Change it to `Workspace` and they see everything.
If you remove a user from all teams, they lose access to templates, catalogs, and team-scoped projects, even if they retain their role.
## Access the roles screen
In the left sidebar, click **Settings**.
Under the **Users & Team** section, click **Role** to view the list of all available roles.
## View the roles list
The roles table displays:
* **Role** — The role name
* **API name** — Technical identifier used for integrations and API calls
* **Type** — **System** (green badge) or **Custom** (gray badge)
Use the search bar to filter roles by name or API name. You can adjust the number of rows displayed per page and navigate between pages using the pagination controls at the bottom.
## Create a new role
Click the **+ New role** button at the top right of the screen.
Enter the role **name** and **API name**. The API name cannot contain spaces or special characters.
Select the **Project Access Level** (Workspace, Team, or Personal), then check the **Read**, **Write**, and **Delete** boxes for each object family.
Toggle **Authorize** on each setting zone the role should manage (Workspace, Users & Teams, Roles, Data, Developers, Organization).
Toggle **Authorize** on each app-level action the role should perform (Import CSV, Export CSV, Overpass sales restriction).
Click **Save**. The role appears in the list with a **Custom** badge.
## View a system role
Find the system role in the roles list.
Click the **⋮** menu to the right of the row.
Click **View** to open the role details in read-only mode.
## Edit a custom role
Find the custom role in the roles list.
Click the **⋮** menu to the right of the row.
Click **Edit**, update the Objects, Settings, or Apps sections, then click **Save**.
## Delete a custom role
Find the custom role in the roles list.
Click the **⋮** menu to the right of the row.
Click **Delete** and confirm the deletion.
Before deleting a custom role, reassign any users currently holding it. Users with a deleted role lose access until they receive a new role.
## Assign a role to a user
You assign a role when you invite a new user or edit an existing one.
Click **Settings** in the left sidebar, then click **User** under the **Users & Team** section.
To invite a new user, click **+ Invite user**. To edit an existing user, click the **⋮** menu next to their name and select **Edit**.
In the user form, select the appropriate role from the **Role** dropdown menu.
Click **Save** or **Send invitation** to apply the role.
Assign the **Manager** role to users who need to approve quotes but don't require full Admin privileges.
## Common questions
No. System roles are locked to ensure Qwoty functions correctly. Create a custom role instead and configure the permissions to match your needs.
The role is removed, but users assigned to it remain in the system. You must manually reassign each user to a new role before they can access Qwoty again.
No. Each user is assigned exactly one role. If you need a combination of permissions, create a custom role that bundles them.
The API name is a technical identifier used in integrations, API calls, and automation scripts. It cannot contain spaces or special characters.
Yes. API requests authenticate using user credentials or API keys tied to a user account, so role permissions apply to API calls as well.
The change takes effect immediately. The user's permissions update the next time they refresh their session or log in again.
Read permission decides **whether** a user can view quotes, contracts, and orders. Project Access Level decides **which** ones — all projects in the workspace, only their team's, or only those they created themselves. Both must align for a user to see a project.
Grant this only to senior roles who legitimately need to bypass approval workflows, discount caps, or pricing guardrails — typically VP Sales, CRO, or Finance leads handling exceptions.
## Related
View, invite, and edit users in your workspace.
Organize users into teams to control access to templates and catalogs.
Configure workspace-level settings and business units.
Set up approval rules that depend on roles and team scope.
# Manage teams
Source: https://docs.qwoty.io/user-guide/settings/users/manage-teams
Organize users into teams to control access to catalogs, templates, and discounts.
Teams let you group users and control which quote templates, catalogs, and resources they can access. This page shows you how to view, create, edit, and delete teams.
You must have the **Admin** role to manage teams.
## Access the teams screen
Click **Settings** in the left sidebar.
In the **Users & Team** section, click **Team**.
## View and search teams
The teams list displays all teams in your workspace. Each row shows:
* **Name**: The display name of the team
* **Reference**: Internal identifier for the team
* **API Name**: Technical identifier used in integrations and API calls
**Search for a team**\
Use the search bar at the top to filter teams by name or reference.
**Navigate the list**\
At the bottom of the table, you can adjust the number of rows per page using the **Rows per page** dropdown and navigate between pages using the pagination controls.
## Create a team
Click the **+ New team** button at the top right of the screen.
A new page opens where you can set the team name, reference, and assign members and resources.
See [Create and configure a team](#) for detailed configuration options.
## Edit a team
Find the team in the list you want to edit.
Click the **⋮** menu on the right side of the row.
Select **Edit** from the dropdown menu.
Make your changes and click **Save**.
## Delete a team
Find the team you want to delete in the list.
Click the **⋮** menu on the right side of the row.
Select **Delete** from the dropdown menu.
Confirm the deletion in the dialog that appears.
Before deleting a team, ensure no users are assigned to it. Users assigned to a deleted team will lose access to templates, catalogs, and other resources linked to that team.
## Common questions
When you delete a team, users assigned to it lose access to any templates, catalogs, or discounts restricted to that team. Make sure to reassign users to another team before deletion.
Yes. A user can be assigned to multiple teams and will inherit access rights from all teams they belong to.
The API Name is a technical identifier used when integrating Qwoty with external systems or calling the Qwoty API. It ensures consistent references across integrations.
## Related resources
Learn how to set up team members, assign catalogs, and configure access rights
View, invite, edit, and delete users in your workspace
Understand the permissions for Admin, Manager, Sales Rep, and Viewer roles
Configure templates that combine catalogs, pricebooks, and contract models
# Manage users
Source: https://docs.qwoty.io/user-guide/settings/users/manage-users
View, invite, edit, and remove users in your Qwoty workspace
You manage all users in your Qwoty workspace from the **Users** settings page. You can invite new users, assign roles, organize users into teams, and control access to quotes and templates.
You must have the **Admin** role to manage users.
## Access the users page
Click **Settings** in the left sidebar.
In the **Users & Team** section, click **User**.
## View the user list
The **Users** page displays all active and pending users in your workspace. Each row shows:
* **Name**: Full name of the user
* **Email**: Email address used for login
* **Role**: Admin, Manager, Sales Rep, or Viewer
* **Team**: Team assignment (if applicable)
* **Status**: Active, Invited, or Inactive
### Search for a user
Use the search bar at the top of the page to filter users by name or email.
### Navigate the list
At the bottom of the table, you can:
* Change the number of rows displayed per page via the **Rows per page** dropdown
* Navigate between pages using the pagination buttons
## Invite a new user
Click the **+ New user** button at the top right of the page.
A form opens where you enter the user's email, assign a role, and optionally add them to a team.
Click **Send invitation**. The user receives an email with a link to set their password and access Qwoty.
See [Invite and configure a user](/user-guide/settings/users/manage-users) for detailed configuration options.
## Edit a user
Find the user in the list.
Click the **⋮** menu to the right of the row.
Select **Edit** from the dropdown.
Make your changes (role, team, or status) and click **Save**.
## Remove a user
Find the user in the list.
Click the **⋮** menu to the right of the row.
Select **Delete** from the dropdown.
Confirm the deletion in the dialog that appears.
When you delete a user, they immediately lose access to Qwoty. Any quotes or Dealrooms they own remain in the system but may need reassignment.
## User roles and permissions
Qwoty uses four roles to control what users can view and edit:
| Role | Access level |
| ------------- | ---------------------------------------------------------- |
| **Admin** | Full access to all features, settings, users, and data |
| **Manager** | Team management, approvals, reporting, and quote oversight |
| **Sales Rep** | Create and manage quotes, customers, and Dealrooms |
| **Viewer** | Read-only access to quotes and reports |
You assign roles when you invite a user or by editing an existing user.
## Common questions
No. Each user can only be assigned to one team at a time. If you need a user to access resources from multiple teams, you must grant those permissions at the template or catalog level.
The quotes remain in the system. They are not deleted, but ownership may need to be reassigned manually. We recommend transferring ownership before removing the user.
Yes. Edit the user and change their status to **Inactive**. This preserves their record and quote history while blocking access.
Open the **⋮** menu for the user and select **Resend invitation**. They will receive a new email with the setup link.
## Related resources
Learn how to invite new users and configure their permissions
Organize users into teams to control access to templates and catalogs
Understand the permissions available to each role
Set up approval workflows for quotes based on user roles
# Business units
Source: https://docs.qwoty.io/user-guide/settings/workspace/business-units
Organize your workspace into separate business units with their own brand, tax identity, and templates.
Business units let you divide your workspace into separate operational entities. Each unit has its own brand identity, legal address, tax configuration, brand kit, and linked templates, while sharing the same workspace-level billing and user directory.
Use business units when you operate multiple brands, regional divisions, or legal entities that need different branding, tax IDs, or sales workflows — while keeping one team and one billing relationship with Qwoty.
A default business unit is created automatically when you sign up. You only need to create more units if you operate multiple brands or legal entities.
## What a business unit contains
Every business unit combines four sections:
* **General information** — brand name, legal entity, address, country, and tax IDs
* **Linked templates** — the quote templates that belong to this unit
* **Brand kit** — fonts, colors, and logo that style quotes and Dealrooms
* **Identifiers** — external IDs used to sync with your CRM, ERP, accounting, or warehouse system
Quotes, templates, and Dealrooms belong to exactly one business unit. The unit determines the branding that the buyer sees and the legal entity that appears on contracts.
## Access requirements
You must have the **Admin** role to create, edit, or delete business units.
**Manager** and **Sales Rep** roles can view the business units their templates belong to, but cannot modify business unit settings.
## Access the business units screen
In the left sidebar, click **Settings**.
Under the **Workspace** section, click **Business Units**. You'll see the list of existing units and an option to create a new one.
## Create a business unit
From the Business Units list, click **Add business unit**.
Enter the required fields and any optional ones that apply:
| Field | Required | Description |
| -------------------------- | -------- | ------------------------------------------------------------ |
| **Brand** | ✓ | Public-facing brand name shown on quotes and Dealrooms |
| **Company name** | ✓ | Legal entity name shown on contracts and invoices |
| **Address** | ✓ | Street address of the legal entity |
| **Apartment, suite, etc.** | — | Secondary address line |
| **Town / city** | ✓ | City |
| **County / state** | — | Applicable in regions that use states or counties |
| **Zipcode / Postal code** | ✓ | Postal code |
| **Country** | ✓ | Country of the legal entity |
| **Tax** / **Tax Value** | — | Tax identifier type and value (for example, VAT + `FR76...`) |
Use **+ Add tax ID** to register multiple tax identifiers (for example, a domestic VAT number plus an EU VAT number for cross-border sales).
The Brand kit controls how quotes and Dealrooms look for this business unit.
* **Title Font** — font family used for headings (for example, `Poppins`)
* **Theme Background Color** / **Theme Text Color** — base colors for pages
* **Button Background Color** / **Button Text Color** / **Button Corner** — button styling (corner controls roundness)
* **Block Stroke** / **Block Stroke Color** / **Block Corner** / **Block Shadow** — styling for content blocks
* **Logo** — drag and drop a file or click to upload
Logo requirements: formats PNG, JPEG, PJG, WEBP, or GIF. Maximum file size 10 MB. Recommended dimensions: maximum height 50 px, maximum width 150 px. Images display at their original size.
**Preview your branding live.** Open an existing quote in a separate browser window or tab before you start editing. As you save changes to the Brand kit, refresh the quote window to see colors, fonts, and logo update in real time. This is the fastest way to iterate visually without saving and reopening the same screen repeatedly.
Click **Reset** at any point to revert the Brand kit to Qwoty defaults.
If you sync Qwoty with other systems, enter the external IDs that match this business unit:
* **CRM ID** — identifier in your CRM (HubSpot, Salesforce, Pipedrive)
* **ERP ID** — identifier in your ERP
* **Accounting ID** — identifier in your accounting tool (Pennylane, Sage)
* **Warehouse ID** — identifier in your inventory or warehouse system
These IDs are used by integrations to route data to the correct business unit.
Click **Save** to create the business unit. You can now link templates to it and start creating quotes under this brand.
## Edit a business unit
In the Business Units list, click the unit you want to edit.
Edit any of the four sections: General information, Linked templates, Brand kit, Identifiers.
Click **Save** to apply your changes. Existing quotes keep their current branding — only new quotes pick up the updated Brand kit.
## Link templates to a business unit
Templates are the link between a business unit and its quotes. A template always belongs to one business unit, and any quote created from that template inherits the unit's brand, legal entity, and identifiers.
You link templates from the **Templates** section of the business unit form, or when creating or editing a template.
Learn how to create and link quote templates to a business unit.
## Common questions
No. The default business unit created at sign-up works for single-brand operations. Configure its brand kit, address, and tax ID to match your company and you're done.
Yes. Admins and Managers typically have access across all units. Sales Reps access the units whose templates they can use, based on their team and role scope.
You cannot delete a business unit while it has linked templates, active quotes, or orders. Archive or reassign them first.
No. Quotes inherit their business unit from the template used at creation. To switch units, duplicate the quote and pick a template from the target unit.
No. The fields store the mapping, but syncing happens through configured integrations. See the integration pages for CRM, ERP, and accounting to enable two-way sync.
**Brand** is customer-facing — it appears on quotes and Dealrooms. **Company name** is the legal entity — it appears on contracts and invoices. They can be identical (for example, `DOYE` and `DOYE`) or different when a legal entity operates under a commercial brand.
## Related
Configure workspace-level settings that apply across all business units.
Understand how roles interact with business unit access.
Link templates to a business unit to scope quotes.
Use external Identifiers to sync with your CRM, ERP, or accounting tool.
# Currencies and taxes
Source: https://docs.qwoty.io/user-guide/settings/workspace/currencies-and-taxes
Configure the currencies you quote in and the tax rates you apply across your workspace.
Qwoty separates currency and tax configuration into two dedicated pages under your workspace settings. Both are set once at the workspace level and then used when creating quotes.
You must have the **Admin** role to add, edit, or delete currencies and taxes.
## Currencies vs taxes
Currencies define **what denominations** you can price in. Taxes define **what levies** you apply on top of (or inside) those prices. Qwoty keeps them separate because they change for different reasons — you might add a currency when opening a new market, while taxes change whenever a country updates its fiscal rules.
| Setting | What it controls | Where to find it |
| -------------- | -------------------------------------------- | ----------------------------------- |
| **Currencies** | Available currencies for pricing and quoting | **Settings → Workspace → Currency** |
| **Taxes** | Tax types and rates applied on quotes | **Settings → Workspace → Tax** |
## Manage currencies
Currencies are available at the workspace level. Each quote is issued in exactly one currency, chosen at quote creation.
### Access the currencies screen
In the left sidebar, click **Settings**.
Under the **Workspace** section, click **Currency**. You'll see the list of currencies enabled in your workspace.
### Add a currency
Click **+ New currency** (or equivalent add action) at the top of the Currency list.
In **General Information**, click the **Currency** dropdown. Qwoty shows a searchable list of world currencies with their ISO code and native symbol — for example:
* Albanian Lek (ALL) — L
* Australian Dollar (AUD) — A\$
* Argentine Peso (ARS) — \$
* Bahraini Dinar (BHD) — د.ب
Use the search field to filter by name or code.
In the right panel, set **Active** to make the currency available for new quotes. Inactive currencies remain in the list but cannot be selected.
Fill in **API Name** if you need to reference this currency programmatically in integrations or webhooks. This identifier is separate from the ISO code.
Set **Is default** to `true` if this currency should be the default for new quotes. Only one currency can be the default at a time — setting a new default removes the flag from the previous one.
Click **Save** in the top-right corner. The currency appears in your list and becomes available on quote templates and quotes.
Once a quote is created, its currency is locked. To change the currency, duplicate the quote and select a different currency at creation.
## Manage taxes
Taxes are configured per country and can apply as a percentage (calculated automatically on quote totals) or as a fixed amount (manually defined per transaction).
### Access the taxes screen
In the left sidebar, click **Settings**.
Under the **Workspace** section, click **Tax**. You'll see the list of taxes configured in your workspace.
### Tax types
When you add a tax, you first choose a **Type** from four predefined values. The type drives which fields the form asks for.
| Type | Use case | Available modes |
| ------------- | ----------------------------------------------------- | -------------------- |
| **VAT** | Value-Added Tax (most of Europe, many other regions) | Percentage only |
| **GST** | Goods and Services Tax (Australia, Canada, India, NZ) | Percentage only |
| **Sales Tax** | US state and local sales tax | Percentage only |
| **Custom** | Any other levy not covered by the three above | Percentage or Amount |
Choose **Custom** when you need to configure a flat-fee tax (for example, a stamp duty or a regulatory contribution that isn't a percentage of the total).
### Calculation modes
Qwoty supports two modes of calculation:
* **Percentage** — The tax amount is automatically calculated as a percentage of the taxable base. You enter a **Rate** (for example, `20` for 20%).
* **Amount** — The tax is a fixed value you configure manually, either directly on the quote or through specific calculations. You enter a **Currency** and an **Amount** (for example, `15` EUR).
Percentage is available for all tax types. Amount is only available when **Type** is set to **Custom**. VAT, GST, and Sales Tax are always percentage-based.
### Add a tax
Click **+ New tax** (or equivalent add action) at the top of the Tax list.
In **General Information**, select **Type** from the dropdown: `VAT`, `GST`, `Sales Tax`, or `Custom`.
If you pick **Custom**, a **Name** field appears so you can label the tax yourself (for example, `Eco-participation`). For VAT, GST, and Sales Tax, the name is derived from the type.
Pick the **Country** to which this tax applies. One tax configuration is scoped to one country — create separate entries for each jurisdiction.
Click either **Percentage** or **Amount**.
* **Percentage** is the default and is the only mode available for VAT, GST, and Sales Tax.
* **Amount** is only clickable when Type is **Custom**.
Enter the percentage in the **Rate** field. For example, `20` for a 20% VAT.
* Select the **Currency** the amount is denominated in.
* Enter the fixed **Amount** value (for example, `15`).
Click **Save** to create the tax. It becomes available for selection on quotes and quote templates.
### Apply a tax to a quote
Taxes are applied at the quote line level or at the quote level, depending on the template configuration.
* **Default tax per template** — set a tax in a quote template so new quotes inherit it.
* **Override on a quote** — a sales rep can override or add taxes when editing a quote, based on permissions.
* **Line-item taxes** — different lines in the same quote can use different tax rates (useful for mixed-rate catalogs).
Taxes flow through the quote-to-revenue cycle: they appear on the quote, the published Dealroom view, the signed document, and eventually the order.
## Common questions
No. Currency is locked at quote creation to avoid pricing inconsistencies. Duplicate the quote if you need to issue it in another currency.
Qwoty displays the quote in the currency chosen at creation. If you need the same quote in a different currency, duplicate the quote and change the currency at the top of the form.
Yes — set **Active** to `Inactive` on a currency to prevent new quotes from using it. Existing quotes keep their original currency.
These three tax types are always calculated as a percentage of the taxable base. To use a fixed-amount tax, choose **Custom** as the type.
Yes. You can apply different taxes on different line items (for example, 20% VAT on products, 0% VAT on services), and the total is the sum of all line-level taxes.
It's a technical identifier used by integrations and API calls to reference the currency or tax. Use a short, unique string without spaces or special characters. It's optional if you don't plan to use the API.
You cannot delete a tax while it's referenced by active quotes or orders. Deactivate it or reassign the affected quotes to a new tax first.
## Related
Configure language, country, and other workspace-wide defaults.
Customize quote numbering and association resolution.
Add tax IDs per legal entity and manage multi-entity tax configuration.
Learn how prices, currencies, and taxes combine on quote line items.
# Forms
Source: https://docs.qwoty.io/user-guide/settings/workspace/forms
Customize the data you collect from customers in the Dealroom using custom forms
Forms allow you to collect structured information from your customers directly in the Dealroom. You can create custom forms to gather requirements, qualify needs, or capture additional deal data before signature.
Forms appear as interactive sections in the Dealroom where customers can fill in fields, upload files, and submit responses. You configure forms in your workspace settings and attach them to quote templates.
## Key features
* **Custom fields**: Add text inputs, dropdowns, checkboxes, file uploads, and more
* **Conditional logic**: Show or hide fields based on previous answers
* **Template-level**: Attach forms to specific templates so they appear automatically
* **Submission tracking**: See when customers complete forms and review their responses
* **Pre-filled data**: Auto-populate form fields with quote or customer variables
Form responses are stored with the quote and visible to sales reps in the quote detail view.
## How forms work
```mermaid theme={null}
graph LR
A[Create Form] --> B[Add Fields]
B --> C[Configure Logic]
C --> D[Attach to Template]
D --> E[Publish Quote]
E --> F[Customer Fills Form]
F --> G[Responses Saved]
```
## Creating a form
Navigate to **Settings** > **Workspace** > **Forms** to create and manage forms.
Click **\[BUTTON LABEL]** and enter a form name and optional description. The name appears as the section title in the Dealroom.
Click **\[BUTTON LABEL]** to add fields. Configure each field's type, label, placeholder text, and whether it's required.
Supported field types:
* Short text
* Long text (multi-line)
* Number
* Email
* Dropdown (single select)
* Multi-select
* Date
* File upload
* Checkbox
Use conditional logic to show fields only when certain conditions are met. For example, show a **\[FIELD NAME]** field only when the customer selects `Enterprise` from a plan dropdown.
Open the template you want to use this form with. In the **\[MENU PATH]** section, select the form from the dropdown. Multiple forms can be attached to a single template.
## Form responses
When a customer submits a form in the Dealroom, responses are immediately visible to the sales team.
View responses in the quote detail page under the **\[MENU PATH]** section. Responses are read-only once submitted, but admins and managers can reset forms to allow customers to edit their answers.
Forms can be marked as required. When required, customers cannot proceed to signature until all required forms are completed.
## Field mapping
Map form responses to customer or quote custom fields to integrate form data with your CRM or reporting tools.
When you enable field mapping, responses automatically populate the linked custom field when the form is submitted. This allows you to:
* Sync data to your CRM via API
* Use form responses in contract variables
* Filter and report on form data across quotes
## Common questions
No, responses are locked after submission to maintain data integrity. Admins and managers can reset a form to allow re-submission if needed.
Yes. Forms are captured as part of the quote snapshot when published. Changes to the form template do not affect already-published quotes.
Yes, if you map form fields to custom fields. You can then reference those custom fields in your contract model using variables like `{{quote.custom_field_name}}`.
File upload fields support common document and image formats. You can configure maximum file size and allowed file types per field.
## Related
Configure templates to include forms automatically
Learn how customers interact with forms in the Dealroom
Create custom fields to store and map form data
Use form responses as variables in contracts
# General workspace settings
Source: https://docs.qwoty.io/user-guide/settings/workspace/general
Configure your workspace name, language, timezone, and unit system
General workspace settings define the default configuration for all users in your workspace. These include the workspace name, back-office language, timezone, and unit system. Individual users can override some of these defaults in their personal profile settings.
You must have the **Admin** role to access and modify these settings.
## Overview
General settings apply workspace-wide and establish the baseline configuration for your team. They control how dates, times, numbers, and currencies appear throughout Qwoty. Changes to these settings affect all users immediately, though users can customize certain preferences individually.
## Accessing general settings
Click **Settings** in the left side menu.
In the **Workspace** section, click **General**.
## Available settings
### Workspace name
The workspace name identifies your environment and appears to all team members. Choose a name that clearly represents your organization or team.
### Back-office language
Select the default language for the Qwoty interface. This setting determines the language for menus, buttons, labels, and system messages. Users can override this in their profile to work in their preferred language.
### Display format
The display format shows how numbers and currencies render in the interface. This field is read-only and updates automatically based on the selected back-office language. It follows standard locale conventions for thousands separators, decimal points, and currency symbols.
### Time zone
The workspace timezone controls how Qwoty displays and records timestamps for quotes, approvals, signatures, and activity logs. Select the timezone that matches your primary business location.
All timestamps in the system adjust to this timezone. Users see dates and times consistently across reports, quote histories, and the Dealroom.
### Unit system
Choose the measurement system that matches your products and regional conventions:
* **Metric system**: kilometers, kilograms, liters, Celsius
* **Imperial system**: miles, pounds, gallons, Fahrenheit
This setting affects how product dimensions, weights, and volumes display in quotes and the product catalog.
## Technical identifiers
Two read-only identifiers appear at the bottom of the general settings page:
| Identifier | Purpose |
| ------------------- | ------------------------------------------------- |
| **Workspace ID** | Unique technical identifier for your workspace |
| **Organization ID** | Unique technical identifier for your organization |
Copy these identifiers when contacting Qwoty support or configuring API integrations. They help technical teams identify your environment quickly.
## Common questions
Yes. Each user can customize their language and display format in their profile settings. Workspace settings serve as default values for new users and those who haven't set a preference.
Existing timestamps remain accurate but display according to the new timezone. For example, a quote created at 2:00 PM EST will show as 11:00 AM PST if you switch to Pacific time.
The unit system controls how units display, but it doesn't automatically convert existing product data. If you switch from metric to imperial, update product specifications manually to ensure accuracy.
Any user with access to the general settings page can view these identifiers. Only **Admin** users can access this page by default.
## Related resources
Customize your personal language, timezone, and notification preferences
Manage roles, permissions, and team structure
Connect Qwoty to your CRM, ERP, and other business tools
Explore all workspace-level configuration options
# Payment methods
Source: https://docs.qwoty.io/user-guide/settings/workspace/payment-methods
Configure how customers can pay their invoices: credit card, bank transfer, direct debit, and more.
Payment methods define how your customers can pay their invoices. You configure them at the workspace level, then assign them to templates so Sales Reps can offer appropriate payment options on quotes.
Payment methods answer "how" to pay (credit card, wire transfer, check). Payment terms answer "when" to pay (Net 30, due on receipt). Both work together to define complete payment conditions.
## What is a payment method
A payment method is a workspace-level configuration that represents a way customers can settle invoices. Common examples include:
* **Credit card** — online card payment
* **Bank transfer** — wire or ACH payment
* **Direct debit** — automated bank withdrawal
* **Check** — paper check payment
* **Cash** — cash payment
Each payment method includes:
* **Name** — customer-facing label displayed on quotes and in the Dealroom
* **API Name** — technical identifier used for integrations and automation
* **Status** — Active or Inactive
Only Admins can create and manage payment methods. Sales Reps select from available active methods when building quotes.
## How payment methods work in Qwoty
| Step | Action | User |
| ---- | ------------------------------------------------------------------------- | --------- |
| 1 | Admin creates payment methods at workspace level | Admin |
| 2 | Admin assigns payment methods to a Template | Admin |
| 3 | Sales Rep selects payment method when creating a quote from that Template | Sales Rep |
| 4 | Customer views available payment method in the Dealroom | Customer |
| 5 | Payment method appears on the final Contract | All |
Payment methods cascade from workspace settings through templates to individual quotes. You can assign multiple payment methods to a single template, giving Sales Reps flexibility to match customer preferences.
## Managing your payment methods
### Access the payment methods list
Click **Settings** in the left sidebar.
In the **Workspace** section, click **Payment methods**.
The list displays all configured payment methods with their status, name, and API name.
### Search and filter
Use the search bar at the top to filter payment methods by name. Use the **Rows per page** dropdown to adjust how many items display. Navigate between pages with the pagination controls at the bottom.
### Create a payment method
Click the **+ New payment method** button at the top right. A configuration page opens where you define the name, API name, status, and other properties.
### Edit a payment method
Click the **⋮** menu on the right of any row, then select **Edit**. Make your changes and save.
### Delete a payment method
Click the **⋮** menu on the right of the row, then select **Delete**. Confirm the deletion in the dialog.
Before deleting a payment method, verify it is not assigned to any active templates. Deleting a method used in templates may cause errors.
## Common questions
Yes. You can create any payment method that matches your business model. Common custom examples include financing, lease-to-own, or partner-specific payment arrangements.
Deactivated payment methods no longer appear when creating new quotes, but existing quotes that use them remain unchanged. You can reactivate the method at any time.
Yes. Changes to a payment method's name or status do not affect existing quotes. Only new quotes will reflect the updated configuration.
You can assign as many payment methods as needed to a template. Sales Reps will see all assigned methods when creating quotes from that template.
## Related resources
Configure when customers must pay invoices
Build quote templates with payment methods
Create and manage customer quotes
Configure global workspace options
# Payment terms
Source: https://docs.qwoty.io/user-guide/settings/workspace/payment-terms
Define single or multi-installment payment plans that customers see on every quote.
Payment terms define how and when customers pay. A payment term can be as simple as `Net 30` or as complex as a 4-installment plan with different deadlines and custom delivery-linked dates. Once created, payment terms are linked to quote templates so sales reps pick the right plan at quote creation.
You must have the **Admin** role to create, edit, or delete payment terms.
## What payment terms control
Each payment term defines:
* **A name and reference** — how the term appears in quotes and integrations
* **One or more installments** — each with its own percentage and deadline
* **A total percentage** — which must equal 100%
* **External identifiers** — to sync with CRM, ERP, or accounting systems
* **A status** — Active (available on quotes) or Inactive (hidden from new quotes)
Payment terms flow through the quote-to-revenue cycle: they appear on the quote, the published Dealroom, the contract, and the order.
## Access the payment terms screen
In the left sidebar, click **Settings**.
Under the **Workspace** section, click **Payment terms**. You'll see the list of all configured terms with their name, API name, and status.
Use the search bar at the top of the list to filter terms by name. Adjust **Rows per page** at the bottom of the table to browse larger lists.
## Single-installment vs multi-installment terms
Qwoty supports two kinds of payment terms:
* **Single installment** — the customer pays 100% of the quote total in one go, at a specific deadline (for example, `Net 30`)
* **Multi-installment** — the customer pays in multiple scheduled portions, each with its own percentage and deadline (for example, a 4×25% plan over 4 months)
Both are created through the same form — you simply add one installment row for a single payment, or several rows for a split plan.
## Available deadline options
When you configure an installment, the **Term** dropdown offers 14 preset options that cover the most common billing practices:
| Option | When payment is due |
| ------------------------------- | ---------------------------------------------- |
| **Due immediately** | At the moment the quote is accepted |
| **Due on receipt** | When the customer receives the invoice |
| **Net 15** | 15 days after the invoice date |
| **Net 30** | 30 days after the invoice date |
| **Net 45** | 45 days after the invoice date |
| **Net 60** | 60 days after the invoice date |
| **Net 90** | 90 days after the invoice date |
| **Net 120** | 120 days after the invoice date |
| **30 days end of month** | 30 days from the last day of the invoice month |
| **45 days end of month** | 45 days from the last day of the invoice month |
| **60 days end of month** | 60 days from the last day of the invoice month |
| **Upon purchase order receipt** | When the customer's PO is received |
| **Custom** | A deadline you describe yourself in free text |
Picking **Custom** adds a **Custom Text** field next to the term, where you write the deadline in plain language — for example, `2 days before delivery`, `15 days after delivery`, or `on January 15, 2027`.
## Create a payment term
From the Payment terms list, click **+ New payment term** at the top right of the screen.
* **Name** (required) — the label shown to sales reps and customers, for example `Net 30` or `4X`
* **Reference** (optional) — a short internal reference, for example `4X`
Use a naming convention that works for sales reps browsing the dropdown. `Net 30` is clearer than `Standard 30d`.
Each installment row has three (or four) fields:
* **Percentage** (required) — the portion of the quote total due at this deadline (for example, `25` for 25%)
* **Term** (required) — the deadline, picked from the dropdown
* **Custom Text** (only shown when Term is `Custom`) — free text describing the deadline
For a single-payment term like `Net 30`, enter **100** as the percentage and pick `Net 30`.
Click **+ Add installment** to add another row. Configure its percentage, term, and optional custom text. You can delete any row with the red trash icon on the right.
The **Total Percentage** field below the list updates automatically as you add or edit installments. It must reach exactly **100.00%** before you can save.
If you sync Qwoty with other systems, enter the matching IDs:
* **CRM ID** — identifier in your CRM
* **ERP ID** — identifier in your ERP
* **Accounting ID** — identifier in your accounting tool
In the right panel:
* **Status** — set to `Active` to make the term available on quotes, or `Inactive` to hide it
* **API Name** — technical identifier used in integrations (for example, `4x`)
Click **Save** in the top right. The new term appears in the Payment terms list and becomes available on quote templates.
## Example: a 4×25% installment plan
Here's what a typical multi-installment payment term looks like — a 4-installment plan tied to delivery events:
| Installment | Percentage | Term | Custom Text |
| ----------- | ---------- | -------------- | ------------------------ |
| 1 | 25% | Due on receipt | — |
| 2 | 25% | Net 45 | — |
| 3 | 25% | Custom | `2 days before delivery` |
| 4 | 25% | Custom | `15 days after delivery` |
**Total percentage:** 100.00% ✅
This plan bills the customer in four equal portions: immediately, at 45 days, shortly before delivery, and shortly after delivery.
## Edit a payment term
In the Payment terms list, find the term you want to edit.
Click the **⋮** menu on the right of the row and select **Edit**.
Adjust the General Information, installments, Identifiers, or Status. Click **Save** to apply changes.
Editing a payment term doesn't change the terms already locked on existing quotes. Only new quotes pick up the updated configuration.
## Delete a payment term
In the Payment terms list, find the term you want to delete.
Click the **⋮** menu on the right of the row and select **Delete**.
Confirm the deletion. The term is removed from the list.
Before deleting a payment term, verify it isn't used by any active quote templates. Deactivate it (set Status to `Inactive`) instead of deleting if you want to preserve historical references.
## Common questions
Yes. Even a single-payment term requires one installment row with `100` in the **Percentage** field and `Net 30` (or any deadline) in the **Term** field. The Total Percentage must equal 100% before you can save.
No. Qwoty prevents saving a payment term whose total doesn't equal exactly 100.00%. This ensures the full quote amount is always billed.
Yes. Each installment has its own Term dropdown, so you can combine `Net 30` with `Custom` deadlines like `2 days before delivery` in the same payment term.
**Name** is the label shown in the app and to customers. **Reference** is an optional internal short code (often identical to the name but useful when your accounting team uses a different internal label).
It's a technical identifier used by integrations and API calls to reference the payment term. Use a short, unique string without spaces or special characters. It's optional if you don't use the API.
Yes. Set **Status** to `Inactive` to hide the term from new quotes while preserving existing quotes that already use it. You can reactivate it at any time.
Existing quotes keep the term's data for historical accuracy, but you cannot create new quotes with a deleted term. Prefer deactivation over deletion unless you're sure the term was never used.
Yes. Each template can have its own default payment terms, and you can offer multiple terms in the same template so sales reps pick the right one at quote creation.
## Related
Link payment terms to templates so new quotes inherit them.
Configure how customers actually settle the invoices — bank transfer, card, direct debit.
See where payment terms appear in the quote flow.
Payment terms apply across a business unit's quotes.
# PDF settings
Source: https://docs.qwoty.io/user-guide/settings/workspace/pdf
Customize PDF output for quotes, orders, and contracts
PDF settings control how Qwoty generates documents for your quotes, orders, and contracts. You configure these settings at the workspace level to match your brand identity and regional requirements.
## What you can configure
PDF settings cover three main areas:
* **Header and footer**: Add your logo, company information, and page numbers
* **Styling**: Define colors, fonts, and spacing to match your brand guidelines
* **Content layout**: Choose which sections and fields appear in generated PDFs
Each workspace has its own PDF configuration. This lets you create region-specific documents with different languages, currencies, or branding.
## How it works
Click **Settings** in the left menu, then select **PDF** in the **Workspace** section.
Upload your logo, set colors, and define header and footer content.
Use the preview pane to see your changes, then click **Save changes**.
You must have the **Admin** role to modify PDF settings.
## Header configuration
The header appears at the top of every page in your PDF documents.
**Logo**: Upload an image file (PNG, JPG, or SVG) with a recommended width of 200–400 pixels. The logo appears in the top-left corner by default.
**Company information**: Add your company name, address, tax ID, and contact details. These fields support line breaks and appear in the top-right section.
## Footer configuration
The footer appears at the bottom of every page.
**Legal text**: Add terms, conditions, or disclaimers that apply to all documents. This text appears on every page.
**Page numbers**: Enable automatic page numbering in the format `Page 1 of 5`.
## Styling options
Customize the visual appearance of your PDFs:
* **Primary color**: Used for headings, table headers, and accent elements
* **Secondary color**: Applied to borders and subtle backgrounds
* **Font family**: Choose from system-safe fonts or upload custom web fonts
Use your brand's primary color for consistency across quotes, presentations, and marketing materials.
## Content sections
Control which sections appear in generated PDFs:
* **Quote summary**: Overview table with total, discounts, and payment terms
* **Line items**: Detailed product list with descriptions, quantities, and prices
* **Bundle details**: Expanded view of products included in bundles
* **Signatures**: Signature blocks for internal approval or customer acceptance
* **Attachments**: Links or embedded files added to the quote
## Common questions
No, PDF settings apply workspace-wide. All quotes, orders, and contracts in the same workspace use the same PDF configuration. If you need different branding, create separate workspaces.
PDF generation automatically includes data from the quote, customer, and contract records. You don't need to configure variables—Qwoty populates them based on the document type.
PDFs generate without a logo. The header displays only your company information text.
Yes. When you publish a quote to the Dealroom, customers can download the PDF version at any time before signing.
## Related resources
Configure workspace name, currency, and locale
Create contract templates with dynamic variables
Send quotes to the Dealroom for customer review
Learn how clients interact with published quotes
# Quote settings
Source: https://docs.qwoty.io/user-guide/settings/workspace/quote-settings
Configure how quotes are numbered and how associations are resolved when multiple levels apply.
Quote settings control two important behaviors of your quoting process: the **numbering format** used to identify each quote, and the **resolution mode** that decides which catalogs, pricebooks, and other assets are available when a customer, segment, and template each have their own associations.
You must have the **Admin** role to modify quote settings.
## Access the quote settings screen
In the left sidebar, click **Settings**.
Under the **Workspace** section, click **Quote**.
## Configure quote numbering
The **Configure Quote Numbering** section lets you design a custom numbering format by combining static text with dynamic variables. Every new quote gets an identifier generated from this pattern.
### The format field
Enter your pattern in the **Single line text** field. You can mix plain text with any of the available variables shown below. The variables are evaluated at the moment the quote is created.
For example, `QUO-{YYYY}-{0000}` generates identifiers like `QUO-2026-0001`, `QUO-2026-0002`, and so on.
### Available variables
| Variable | Description |
| -------- | ----------------------------------------------------------- |
| `{0000}` | Counter — add or remove zeros to control the padding length |
| `{YYYY}` | Year, 4 digits (for example, `2026`) |
| `{YY}` | Year, 2 digits (for example, `26`) |
| `{MM}` | Month, 2 digits (`01`–`12`) |
| `{DD}` | Day, 2 digits (`01`–`31`) |
Click the copy icon next to any variable to insert it into your format string
without typing it manually.
### Start the counter at a specific number
Append `+N` inside the counter variable to offset the starting point. For example, `{0000+30}` starts counting at `30` instead of `0001`. This is useful when you migrate from another tool and want to continue an existing numbering sequence.
| Format | Result |
| ------------- | ------------------------- |
| `{0000}` | `0001`, `0002`, `0003`... |
| `{0000+30}` | `0031`, `0032`, `0033`... |
| `{0000+1000}` | `1001`, `1002`, `1003`... |
### Examples
Qwoty shows three ready-to-use format examples at the bottom of the section:
| Format | Result |
| ------------------------ | ---------------- |
| `QUO-{YYYY}-{0000}` | `QUO-2026-0001` |
| `Q{YY}{MM}-{000+30}` | `Q2603-031` |
| `{YYYY}{MM}{DD}-{00000}` | `20260315-00001` |
Pick the one closest to your preference and adapt the prefix, separators, or counter length.
### Reset quote numbers periodically
The **Reset quote numbers periodically** toggle at the bottom of the section lets you restart the counter on a regular basis (for example, every year). When off (the default), counters increment forever.
Switching reset on mid-year can cause duplicate numbers if your format doesn't
include a period variable. Always include `{YYYY}`, `{YY}`, `{MM}`, or `{DD}`
in your format before enabling periodic reset.
## Configure association resolution
The **Association Resolution Mode** determines which catalogs, pricebooks, contract models, payment terms, and payment methods are available when a sales rep creates a quote — in situations where the customer, their segment, and the chosen template each have their own associations.
### Priority order
Qwoty evaluates associations in this fixed order:
```
Customer → Customer Segment → Template
```
The mode you pick decides whether to combine items across levels or limit them to the highest-priority level that has any configured.
### Inclusive mode (default)
Items from all levels are combined. If the customer, their segment, and the template each have pricebooks attached, the sales rep sees all of them in the dropdown.
Example: If the customer has associated pricebooks, those from the segment and
template remain available as well.
**When to use Inclusive:** you want sales reps to have maximum flexibility and trust them to pick the right pricebook, catalog, or payment method for each deal.
### Exclusive mode
Only the items from the first configured level are available. If the customer has any pricebooks attached, the segment and template pricebooks are ignored. If the customer has none, Qwoty falls back to the segment's pricebooks, then the template's.
Example: If the customer has associated catalogs or payment methods, those
from the segment and template are ignored.
**When to use Exclusive:** you want tight control over what sales reps can pick — for example, a key account with negotiated pricing should never see the standard pricebook.
### Choose a mode
Pick the one that matches your governance model:
| Mode | Best for |
| ------------- | -------------------------------------------------------------- |
| **Inclusive** | Teams that want flexibility and combined options across levels |
| **Exclusive** | Teams that enforce strict per-customer or per-segment pricing |
Set your choice in the **Resolution Mode** dropdown at the bottom of the section. The change applies to new quotes immediately. Existing quotes keep the associations they were created with.
## Common questions
Yes. Existing quotes keep their original numbers. The new format applies
only to quotes created after the change.
The counter continues where it left off, regardless of format changes. If
you were at `0042` in the old format, the next quote will be `0043` in the
new format. Use `{0000 + N}` to reset to a specific value.
No. The resolution mode only applies at the moment a new quote is created.
Quotes keep the catalogs, pricebooks, and terms that were resolved when they
were drafted.
No. The Association Resolution Mode is workspace-wide. All business units
use the same setting.
If neither customer, segment, nor template has any association for a given
asset type (catalog, pricebook, etc.), the sales rep sees an empty list for
that asset at quote creation. Configure at least one default at the template
level to avoid this.
## Related
Configure workspace-level preferences beyond quote behavior.
Set default catalogs, pricebooks, and terms at the template level.
Group customers to apply shared associations through segments.
See how quote numbers appear across statuses and exports.
# Create a quote template
Source: https://docs.qwoty.io/user-guide/templates/how-tos/create-quote-template
Set up a new quote template to streamline your sales process
A quote template defines the sales funnel configuration for your quotes. It combines a catalog, pricebook, payment terms, and contract model into a reusable blueprint that helps your sales team create consistent proposals faster.
## Prerequisites
Before creating a quote template, ensure you have:
* At least one **Catalog** configured with products
* At least one **Pricebook** set up
* Payment terms defined (optional)
* A **Contract Model** created (optional)
You must have **Admin** or **Manager** role permissions to create and manage quote templates.
## Access the templates page
In the left side menu, click **Template**.
## Create a new template
Click **+ New** at the top right of the template list.
In the **General Information** section, complete the required fields:
* **Name** \*: Enter a descriptive name (e.g., "France SMB Template")
* **Currency**: Set the currency for quotes created from this template
* **Language**: Select the quote language
* **Team** \*: Select which team(s) can access this template
* **Business Unit (Seller)**: Assign the selling entity
* **Internal description** (optional): Add notes about when to use this template
In the **Product & Prices Access** section:
* **Catalogs**: Add the product catalog(s) available for this template
* **Pricebooks**: Add the price list(s) to apply (only pricebooks linked to the selected catalogs are available)
* **Discounts**: Add any discount rules available to sales reps
In the **Payment & Terms** section:
* Under **Payments & Credits**: Set default payment terms and payment methods
* Under **Contract & Terms**: Add the contract model(s) to associate with this template
Set **Status** to **Active** in the right panel (default), then click **Save**.
## What happens next
Your new template appears in the template list and becomes available for sales reps to select when creating quotes. The catalog and pricebook you configured will apply automatically when someone chooses this template.
Create multiple templates for different sales scenarios—for example, separate templates for different regions, customer segments, or product lines.
## Related resources
Configure catalogs and add products
Set up price lists for different segments
Use your template to build a quote
Design contract templates with variables
# Duplicate a template
Source: https://docs.qwoty.io/user-guide/templates/how-tos/duplicate-template
Create a copy of an existing template to quickly build new sales configurations
Duplicating a template lets you reuse an existing sales funnel configuration as the starting point for a new template. This saves time when you need similar configurations for different regions, customer segments, or product lines.
The duplicate includes all settings from the original template: Catalog, Pricebook, Payment Terms, and Contract Model references.
## Prerequisites
You need **Admin** or **Manager** role permissions to duplicate templates.
## Access the templates list
In the left side menu, click **Template**.
## Duplicate the template
Find the template you want to duplicate in the template list.
Click the **⋯** menu at the end of the template row and select **Duplicate**.
Review and update the duplicated template's name and settings as needed. The copy inherits all configuration from the original — Catalog, Pricebook, Payment Terms, and Contract Model references.
Click **Save** to confirm any changes.
The new template is created with a `Draft` or inactive status. You can edit it without affecting active quotes.
## What happens next
The duplicated template appears in your templates list. You can now:
* Edit the Catalog or Pricebook to customize products and pricing
* Modify Payment Terms for different deal structures
* Update the Contract Model with region-specific legal language
* Activate the template when ready to use it for new quotes
Use clear naming conventions like "Enterprise - US Q1 2024" or "SMB - EMEA Standard" to distinguish duplicated templates.
## Troubleshooting
Only **Admin** and **Manager** roles can duplicate templates. Contact your administrator if you need this permission.
The duplicate inherits all references from the original template. Edit the new template and select the correct Pricebook from the template settings.
Template duplication only works within the same workspace. You'll need to manually recreate the template configuration in another workspace.
## Related resources
Build a new template from scratch
Modify template settings and references
Learn how templates work in Qwoty
Configure product catalogs for templates
# Link a contract model
Source: https://docs.qwoty.io/user-guide/templates/how-tos/link-contract-model
Attach a contract template to a quote template to auto-generate contracts when quotes are accepted
Linking a contract model to a template allows Qwoty to automatically generate contracts with populated data when quotes are accepted. You configure the association at the template level, and every quote created from that template inherits the contract model.
## Prerequisites
* You have created or have access to a contract model with the required variables
* You have **Admin** or **Manager** permissions
* The template you want to configure already exists
## Access the template settings
In the left side menu, click **Template**, then click the **⋯** menu next to the template you want to configure and select **Edit**.
## Link the contract model
Scroll to the **Payment & Terms** section and expand **Contract & Terms**.
In the **Contract & Terms** table, click **Add** and select the contract model you want to link from the list.
Only active contract models appear in the list. If you don't see the model you need, verify it's not archived.
Click **Save** to apply the changes.
## What happens next
All new quotes created from this template will automatically use the linked contract model. When a quote reaches the **Accepted** status, Qwoty generates a contract using the model and populates it with data from the quote and customer records.
Existing quotes created before you linked the contract model are not affected. You must manually assign a contract model to those quotes if needed.
## Troubleshooting
Verify that you have created at least one contract model and that it is not archived. Navigate to **Contracts** > **Models** to check.
Contract model assignments happen at quote creation. Update the template, then create a new quote to test the configuration.
Check that the contract model variables match available quote and customer fields. Missing or misnamed variables like `{{quote.totla}}` instead of `{{quote.total}}` will not populate.
## Related resources
Build contract templates with dynamic variables
Set up a new quote template with catalog and pricing
Reference list of available dynamic variables
Understand quote lifecycle and status transitions
# Templates
Source: https://docs.qwoty.io/user-guide/templates/introduction
Configure your sales funnel with templates that combine catalogs, pricing, payment terms, and contracts.
Templates define how your sales team creates quotes. Each template combines a catalog of products, a pricebook for pricing, payment terms, and a contract model into a single reusable configuration.
Use templates to set up different sales funnels for regions, customer segments, or product lines. When a sales rep creates a quote, they select a template that automatically applies the right products, prices, and contract terms.
## Reference
Understand template components and structure
How templates move from draft to active to archived
Group products by region, market, or business unit
Set pricing rules and customer segment discounts
## Guides
Step-by-step guide to building a template
Configure templates for different markets and currencies
Configure billing schedules and payment conditions
Attach dynamic contract templates to quotes
# Quote vs Order vs Contract
Source: https://docs.qwoty.io/user-guide/templates/reference/quote-vs-order-vs-contract
Understand the differences between quotes, orders, and contracts in Qwoty
Qwoty uses three distinct objects to manage the complete sales-to-revenue lifecycle: **Quote**, **Order**, and **Contract**. Each serves a specific purpose in the deal flow, and understanding their relationship helps you structure deals correctly.
A **Quote** represents the proposal, an **Order** is the commitment to deliver, and a **Contract** is the legal container that binds them together.
## The three objects
**Quote**
* The customer-facing proposal created by sales
* Contains pricing, products, payment terms, and discounts
* Lives in Draft until published to the Dealroom
* Drives the negotiation and approval workflow
* Status-driven lifecycle from Draft to Accepted
**Order**
* Automatically generated when a quote reaches `Accepted` status
* Represents the confirmed purchase and commitment to deliver
* Immutable record of what was sold and at what price
* Used by fulfillment, operations, and finance teams
* Cannot be edited once created
**Contract**
* The legal umbrella grouping related quotes and orders
* Contains one or more quotes for the same customer deal
* Generates the final signed agreement document
* Persists beyond individual transactions for amendments and renewals
* Tracks the customer relationship over time
A single contract can contain multiple quotes—for example, an initial sale, an upsell three months later, and a renewal after one year.
## How they relate
```mermaid theme={null}
graph LR
A[Template] --> B[Quote]
B -->|Approved & Published| C[Dealroom]
C -->|Signed| D[Quote: Accepted]
D -->|Auto-generated| E[Order]
B -.belongs to.-> F[Contract]
E -.belongs to.-> F
F -->|generates| G[Signed Agreement PDF]
```
## When each is created
| Object | Created when | Created by | Purpose |
| ------------ | ----------------------------------------------- | ------------------------ | -------------------------------------------- |
| **Quote** | Sales rep initiates a deal | Manual or API | Propose pricing and terms |
| **Order** | Quote status changes to Accepted | Automatic | Confirm purchase for fulfillment |
| **Contract** | Quote is created or linked to existing contract | Manual selection or auto | Group all transactions for one customer deal |
## Key differences
### Quote
**Editable until published**
You can modify products, pricing, discounts, and terms while the quote is in Draft or Changes Requested status. Once published, you must create a new version or revision.
**Status-driven**
The quote moves through a defined lifecycle: Draft → Pending Approval → Approved → Published → Pending Signature → Accepted.
**Customer-facing**
Published quotes appear in the Dealroom where customers review, comment, and sign.
### Order
**Immutable**
Orders cannot be edited after creation. If the deal changes, you issue a new quote which generates a new order.
**Operations-focused**
Used by fulfillment, provisioning, and finance teams to execute delivery and recognize revenue.
**System-generated**
You never manually create an order—Qwoty creates it automatically when the quote is accepted.
### Contract
**Multi-transaction container**
One contract holds the full customer relationship, including initial sale, add-ons, renewals, and amendments.
**Legal document source**
The contract generates the final agreement PDF using the Contract Model template with variables populated from quote data.
**Long-lived**
Contracts persist across multiple deals and can span years, while quotes and orders are transactional.
Deleting a contract does not delete its quotes or orders—they become orphaned. Reassign quotes to a different contract before deletion.
## Common questions
No. Every quote must belong to a contract. If you don't select an existing contract when creating a quote, Qwoty creates a new contract automatically.
The order remains in the system as a historical record. Orders are immutable and reflect the state at the moment of acceptance. You may need to issue a credit or reversal quote depending on your process.
No. Orders are only created automatically when a quote reaches Accepted status. This ensures data consistency between the proposal and the commitment.
Create a new quote within the same contract. The new quote generates a separate order when accepted, but both orders belong to the same contract umbrella.
No. Renewals should be added as new quotes within the existing contract. This maintains a complete history of the customer relationship in one place.
## Related
Understand quote statuses and transitions
Configure dynamic contract templates
Step-by-step guide to building a quote
Learn how customers interact with published quotes
# What is a template
Source: https://docs.qwoty.io/user-guide/templates/reference/what-is-a-template
Learn how templates combine catalog, pricing, payment terms, and contract models to streamline quote creation.
A **template** in Qwoty is a reusable sales funnel configuration that defines which products you offer, at what prices, with what payment terms, and what contract applies. Templates eliminate repetitive setup and ensure consistency across your sales process.
Think of a template as a blueprint for creating quotes. Instead of manually selecting catalogs, pricebooks, and contract terms every time, you configure a template once and apply it to multiple quotes.
## What a template includes
Every template combines four core elements:
* **Catalog**: the group of products available to sell (e.g., "France Catalog" or "Enterprise Products")
* **Pricebook**: the price list applied to the catalog, often tailored to customer segments or regions
* **Payment Terms**: the billing rules, payment schedules, and terms that apply to quotes created from this template
* **Contract Model**: the contract template with dynamic variables that populate automatically when a quote is accepted
Templates don't store customer data or quote-specific details. They only define the *structure* and *rules* for creating quotes.
## How templates work
```mermaid theme={null}
graph LR
A[Template] --> B[Catalog]
A --> C[Pricebook]
A --> D[Payment Terms]
A --> E[Contract Model]
B --> F[Quote]
C --> F
D --> F
E --> F
F --> G[Dealroom]
G --> H[Contract]
```
When you create a quote, you select a template. Qwoty applies the template's catalog, pricebook, payment terms, and contract model to the quote automatically. This ensures every quote follows your configured sales process.
## When to use templates
Templates are most useful when you have:
* **Different product catalogs** for different regions, industries, or customer segments
* **Multiple pricing strategies** such as standard, premium, or partner pricing
* **Varied payment terms** like net-30 for enterprise customers and upfront payment for SMBs
* **Custom contracts** that differ by product line or customer type
Create separate templates for each combination of catalog, pricing strategy, and contract type. For example: "US Enterprise," "EU SMB," or "Partner Reseller."
## Template vs. quote
| Template | Quote |
| ----------------------------------------- | ----------------------------------------- |
| Reusable configuration | One-time sales document |
| Defines product catalog and pricing rules | Contains specific products and quantities |
| No customer information | Tied to a specific customer |
| Created by Admins or Managers | Created by Sales Reps using a template |
| Updated to affect future quotes | Locked once published |
## Common questions
Yes. Changes to a template only affect new quotes created after the update. Existing quotes remain unchanged.
No. Templates are designed for reuse across multiple customers with similar needs. Use customer segments or regions to determine template structure, not individual customers.
You cannot delete a template that has active quotes. Archive the template instead to prevent new quotes while preserving historical data.
No. Each quote is created from a single template. If you need to combine products from different catalogs, configure a template with a merged catalog or create separate quotes.
## Related
Step-by-step guide to building your first template
Understand how catalogs and pricebooks work together
Learn how to create dynamic contract templates
Use templates to generate quotes quickly
# Create Address
Source: https://docs.qwoty.io/api-reference/addresses/create
POST https://qwoty.app/api/addresses
Create a new address linked to a customer
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Request Body
UUID of the customer this address belongs to
Address type. Allowed values: `"billing"`, `"shipping"`
Street address
City
Postal/ZIP code
Country code (ISO 3166-1 alpha-2, e.g., `"FR"`, `"US"`, `"DE"`)
Label for this address (e.g., `"Head office"`, `"Warehouse"`)
Secondary address line (apartment, floor, etc.)
State or region
UUID of the contact associated with this address
Internal comment
External CRM ID
External ERP ID
External accounting ID
## Examples
```bash cURL theme={null}
curl -X POST https://qwoty.app/api/addresses \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"type": "billing",
"name": "Siège social",
"address_line1": "12 Rue de la Paix",
"city": "Paris",
"postal_code": "75001",
"country": "FR"
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/addresses', {
method: 'POST',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
customer_id: '550e8400-e29b-41d4-a716-446655440000',
type: 'billing',
name: 'Siège social',
address_line1: '12 Rue de la Paix',
city: 'Paris',
postal_code: '75001',
country: 'FR',
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.post(
'https://qwoty.app/api/addresses',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
json={
'customer_id': '550e8400-e29b-41d4-a716-446655440000',
'type': 'billing',
'name': 'Siège social',
'address_line1': '12 Rue de la Paix',
'city': 'Paris',
'postal_code': '75001',
'country': 'FR',
},
)
data = response.json()
```
## Response
`true` if the address was created
The created address object (same shape as [Get
Address](/api-reference/addresses/get))
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"workspace_id": "706eb564-6c4a-4e18-841c-bdd79be6bca7",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"type": "billing",
"name": "Siège social",
"address_line1": "12 Rue de la Paix",
"address_line2": null,
"city": "Paris",
"state": null,
"postal_code": "75001",
"country": "FR",
"contact_id": null,
"comment": null,
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}
```
```json Validation Error theme={null}
{
"error": "Validation error",
"details": [
{ "path": "customer_id", "message": "customer_id must be a valid UUID" },
{ "path": "type", "message": "Type must be \"billing\" or \"shipping\"" }
]
}
```
# Get Address
Source: https://docs.qwoty.io/api-reference/addresses/get
GET https://qwoty.app/api/addresses/{id}
Retrieve a specific address by ID
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the address
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/addresses/770e8400-e29b-41d4-a716-446655440002 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const addressId = '770e8400-e29b-41d4-a716-446655440002'
const response = await fetch(`https://qwoty.app/api/addresses/${addressId}`, {
headers: { Authorization: 'Bearer qwoty_your_token' },
})
const data = await response.json()
```
```python Python theme={null}
import requests
address_id = '770e8400-e29b-41d4-a716-446655440002'
response = requests.get(
f'https://qwoty.app/api/addresses/{address_id}',
headers={'Authorization': 'Bearer qwoty_your_token'},
)
data = response.json()
```
## Response
`true` if the request succeeded
The address object
UUID
Workspace UUID
Parent customer UUID
`billing` or `shipping`
Address label
Street address
Secondary address line
City
State or region
Postal/ZIP code
Country code (ISO 3166-1 alpha-2)
UUID of the associated contact (optional). Use `GET /api/contacts/{id}` to fetch the contact.
Internal comment
External CRM ID
External ERP ID
External accounting ID
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"workspace_id": "706eb564-6c4a-4e18-841c-bdd79be6bca7",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"type": "billing",
"name": "Siège social",
"address_line1": "12 Rue de la Paix",
"address_line2": null,
"city": "Paris",
"state": "Île-de-France",
"postal_code": "75001",
"country": "FR",
"contact_id": null,
"comment": null,
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}
```
```json Not Found theme={null}
{
"success": false,
"error": "Address not found"
}
```
# List Addresses
Source: https://docs.qwoty.io/api-reference/addresses/list
GET https://qwoty.app/api/addresses
Retrieve all addresses in your workspace
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Query Parameters
Filter addresses by customer UUID
## Examples
```bash cURL theme={null}
curl "https://qwoty.app/api/addresses?customer_id=550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch(
'https://qwoty.app/api/addresses?customer_id=550e8400-e29b-41d4-a716-446655440000',
{ headers: { Authorization: 'Bearer qwoty_your_token' } },
)
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/addresses',
params={'customer_id': '550e8400-e29b-41d4-a716-446655440000'},
headers={'Authorization': 'Bearer qwoty_your_token'},
)
data = response.json()
```
## Response
`true` if the request succeeded
Array of address objects
UUID
Workspace UUID
Parent customer UUID
`billing` or `shipping`
Address label (e.g., "Head office")
Street address
Secondary address line
City
State or region
Postal/ZIP code
Country code (ISO 3166-1 alpha-2)
UUID of the associated contact (optional). Use `GET /api/contacts/{id}` to fetch the contact.
Internal comment
External CRM ID
External ERP ID
External accounting ID
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": [
{
"id": "770e8400-e29b-41d4-a716-446655440002",
"workspace_id": "706eb564-6c4a-4e18-841c-bdd79be6bca7",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"type": "billing",
"name": "Siège social",
"address_line1": "12 Rue de la Paix",
"address_line2": null,
"city": "Paris",
"state": "Île-de-France",
"postal_code": "75001",
"country": "FR",
"contact_id": null,
"comment": null,
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
]
}
```
# Update Address
Source: https://docs.qwoty.io/api-reference/addresses/update
PATCH https://qwoty.app/api/addresses/{id}
Partially update an address
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the address to update
## Request Body
All fields are optional. Only the fields provided will be updated.
Address type. Allowed values: `"billing"`, `"shipping"`
Address label. Set to `null` to unset.
Street address
Secondary address line. Set to `null` to unset.
City
State or region. Set to `null` to unset.
Postal/ZIP code
Country code (ISO 3166-1 alpha-2)
UUID of the associated contact. Set to `null` to unset.
Internal comment. Set to `null` to unset.
External CRM ID
External ERP ID
External accounting ID
`customer_id` cannot be changed after creation.
## Examples
```bash cURL theme={null}
curl -X PATCH https://qwoty.app/api/addresses/770e8400-e29b-41d4-a716-446655440002 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Nouveau siège",
"address_line1": "42 Avenue des Champs-Élysées",
"city": "Paris",
"postal_code": "75008",
"country": "FR"
}'
```
```javascript JavaScript theme={null}
const addressId = '770e8400-e29b-41d4-a716-446655440002'
const response = await fetch(`https://qwoty.app/api/addresses/${addressId}`, {
method: 'PATCH',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Nouveau siège',
address_line1: '42 Avenue des Champs-Élysées',
city: 'Paris',
postal_code: '75008',
country: 'FR',
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
address_id = '770e8400-e29b-41d4-a716-446655440002'
response = requests.patch(
f'https://qwoty.app/api/addresses/{address_id}',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
json={
'name': 'Nouveau siège',
'address_line1': '42 Avenue des Champs-Élysées',
'city': 'Paris',
'postal_code': '75008',
'country': 'FR',
},
)
data = response.json()
```
## Response
`true` if the update succeeded
The updated address object (same shape as [Get
Address](/api-reference/addresses/get))
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "770e8400-e29b-41d4-a716-446655440002",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"type": "billing",
"name": "Nouveau siège",
"address_line1": "42 Avenue des Champs-Élysées",
"address_line2": null,
"city": "Paris",
"state": null,
"postal_code": "75008",
"country": "FR",
"contact_id": null,
"comment": null,
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-03-24T14:00:00Z"
}
}
```
```json Not Found theme={null}
{
"success": false,
"error": "Address not found"
}
```
# Create Catalog
Source: https://docs.qwoty.io/api-reference/catalogs/create
POST https://qwoty.app/api/catalogs
Create a new catalog in your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Request Body
Catalog name
API identifier (auto-generated if not provided)
Internal reference
Catalog description
Active status
External CRM ID
External ERP ID
External accounting ID
## Examples
```bash cURL theme={null}
curl -X POST https://qwoty.app/api/catalogs \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Premium Products",
"description": "Catalog for premium tier products",
"reference": "CAT-002",
"is_active": true
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/catalogs', {
method: 'POST',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Premium Products',
description: 'Catalog for premium tier products',
reference: 'CAT-002',
is_active: true,
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.post(
'https://qwoty.app/api/catalogs',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'name': 'Premium Products',
'description': 'Catalog for premium tier products',
'reference': 'CAT-002',
'is_active': True
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The created catalog object with all fields
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Premium Products",
"api_name": "premium_products",
"reference": "CAT-002",
"description": "Catalog for premium tier products",
"is_active": true,
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T10:30:00Z"
}
}
```
## Error Responses
```json Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": {
"name": "Name is required"
}
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Get Catalog
Source: https://docs.qwoty.io/api-reference/catalogs/get
GET https://qwoty.app/api/catalogs/{id}
Retrieve a specific catalog by ID
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the catalog
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/catalogs/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const catalogId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(`https://qwoty.app/api/catalogs/${catalogId}`, {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
catalog_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.get(
f'https://qwoty.app/api/catalogs/{catalog_id}',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The catalog object
Unique identifier (UUID)
Catalog name
API identifier
Internal reference
Catalog description
Active status
External CRM ID
External ERP ID
External accounting ID
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Standard Products",
"api_name": "standard_products",
"reference": "CAT-001",
"description": "Main product catalog",
"is_active": true,
"id_crm": "crm_123",
"id_erp": "erp_456",
"id_accounting": "acc_789",
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T10:30:00Z"
}
}
```
## Error Responses
```json Not Found theme={null}
{
"success": false,
"error": "Catalog not found"
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# List Catalogs
Source: https://docs.qwoty.io/api-reference/catalogs/list
GET https://qwoty.app/api/catalogs
Retrieve a list of all catalogs in your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/catalogs \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/catalogs', {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/catalogs',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
Array of catalog objects
Unique identifier (UUID)
Catalog name
API identifier
Internal reference
Catalog description
Active status
External CRM ID
External ERP ID
External accounting ID
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Standard Products",
"api_name": "standard_products",
"reference": "CAT-001",
"description": "Main product catalog",
"is_active": true,
"id_crm": "crm_123",
"id_erp": "erp_456",
"id_accounting": "acc_789",
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T10:30:00Z"
}
]
}
```
## Error Responses
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Update Catalog
Source: https://docs.qwoty.io/api-reference/catalogs/update
PATCH https://qwoty.app/api/catalogs/{id}
Update an existing catalog
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the catalog to update
## Request Body
Catalog name
API identifier
Internal reference
Catalog description
Active status
External CRM ID
External ERP ID
External accounting ID
## Examples
```bash cURL theme={null}
curl -X PATCH https://qwoty.app/api/catalogs/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Catalog Name",
"description": "Updated description"
}'
```
```javascript JavaScript theme={null}
const catalogId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(`https://qwoty.app/api/catalogs/${catalogId}`, {
method: 'PATCH',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Updated Catalog Name',
description: 'Updated description',
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
catalog_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.patch(
f'https://qwoty.app/api/catalogs/{catalog_id}',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'name': 'Updated Catalog Name',
'description': 'Updated description'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The updated catalog object with all fields
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Updated Catalog Name",
"api_name": "updated_catalog_name",
"reference": "CAT-001",
"description": "Updated description",
"is_active": true,
"id_crm": "crm_123",
"id_erp": "erp_456",
"id_accounting": "acc_789",
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T14:45:00Z"
}
}
```
## Error Responses
```json Not Found theme={null}
{
"success": false,
"error": "Catalog not found"
}
```
```json Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": {
"name": "Name must be at least 2 characters"
}
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Create Category
Source: https://docs.qwoty.io/api-reference/categories/create
POST https://qwoty.app/api/categories
Create a new product category in your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Request Body
Unique API identifier (snake\_case). Can only contain lowercase letters,
numbers and underscores. Cannot start or end with underscore.
Category name
Category description
Active status
UUID of parent category for hierarchical organization
## Examples
```bash cURL theme={null}
curl -X POST https://qwoty.app/api/categories \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"api_name": "electronics",
"name": "Electronics",
"description": "Electronic products and accessories",
"is_active": true
}'
```
```bash With Parent theme={null}
curl -X POST https://qwoty.app/api/categories \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"api_name": "smartphones",
"name": "Smartphones",
"description": "Mobile phones and accessories",
"is_active": true,
"parent_category_id": "550e8400-e29b-41d4-a716-446655440001"
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/categories', {
method: 'POST',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
api_name: 'electronics',
name: 'Electronics',
description: 'Electronic products and accessories',
is_active: true,
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.post(
'https://qwoty.app/api/categories',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'api_name': 'electronics',
'name': 'Electronics',
'description': 'Electronic products and accessories',
'is_active': True
}
)
data = response.json()
```
## Response
Indicates whether the request was successful
Created category object
Unique identifier (UUID)
Workspace ID (UUID)
Category name
API identifier
Category description
Active status
ID of parent category (UUID)
Creation timestamp (ISO 8601)
Last update timestamp (ISO 8601)
```json 201 Created theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Electronics",
"api_name": "electronics",
"description": "Electronic products and accessories",
"is_active": true,
"parent_category_id": null,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z"
}
}
```
## Error Responses
```json 400 Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": [
"Field 'api_name' is required"
]
}
```
```json 400 Invalid api_name theme={null}
{
"success": false,
"error": "Validation error",
"details": [
"Field 'api_name' can only contain lowercase letters, numbers and underscores (cannot start or end with underscore)"
]
}
```
# Delete Category
Source: https://docs.qwoty.io/api-reference/categories/delete
DELETE https://qwoty.app/api/categories/{id}
Soft delete a product category
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
Category UUID
## Behavior
This endpoint performs a **soft delete** by setting the `deleted_at` timestamp on the category. The category will no longer appear in standard API queries but is not permanently removed from the database.
Deletion is **blocked** if the category is referenced by active discounts. In
that case, you will receive a `409 Conflict` error. You must first remove or
update the discount references before deleting the category.
## Examples
```bash cURL theme={null}
curl -X DELETE https://qwoty.app/api/categories/550e8400-e29b-41d4-a716-446655440001 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const categoryId = '550e8400-e29b-41d4-a716-446655440001'
const response = await fetch(`https://qwoty.app/api/categories/${categoryId}`, {
method: 'DELETE',
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
category_id = '550e8400-e29b-41d4-a716-446655440001'
response = requests.delete(
f'https://qwoty.app/api/categories/{category_id}',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates whether the request was successful
Deletion confirmation
UUID of the deleted category
Always `true` when deletion succeeds
```json 200 Success theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"deleted": true
}
}
```
## Error Responses
```json 404 Not Found theme={null}
{
"success": false,
"error": "Category not found"
}
```
```json 409 Conflict - Referenced by Discounts theme={null}
{
"success": false,
"error": "CATEGORY_REFERENCED_IN_DISCOUNT"
}
```
## Cascade Effects
When a category is deleted:
* Products linked to this category through `category_ids` retain the reference (soft delete doesn't cascade)
* Child categories (with `parent_category_id` pointing to this category) are **not** automatically deleted
* Price percentage rules scoped to this category remain intact
To permanently remove all traces of a category, you should first: 1. Remove
the category from all products (`category_ids` field) 2. Update or delete all
discounts referencing this category 3. Reassign or delete child categories 4.
Then delete the category
# Get Category
Source: https://docs.qwoty.io/api-reference/categories/get
GET https://qwoty.app/api/categories/{id}
Retrieve a single product category by its ID
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
Category UUID
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/categories/550e8400-e29b-41d4-a716-446655440001 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const categoryId = '550e8400-e29b-41d4-a716-446655440001'
const response = await fetch(`https://qwoty.app/api/categories/${categoryId}`, {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
category_id = '550e8400-e29b-41d4-a716-446655440001'
response = requests.get(
f'https://qwoty.app/api/categories/{category_id}',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates whether the request was successful
Category object
Unique identifier (UUID)
Workspace ID (UUID)
Category name
API identifier (unique, immutable)
Category description
Active status
ID of parent category (UUID)
Creation timestamp (ISO 8601)
Last update timestamp (ISO 8601)
```json Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Electronics",
"api_name": "electronics",
"description": "Electronic products and accessories",
"is_active": true,
"parent_category_id": null,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z"
}
}
```
## Error Responses
```json 404 Not Found theme={null}
{
"success": false,
"error": "Category not found"
}
```
# List Categories
Source: https://docs.qwoty.io/api-reference/categories/list
GET https://qwoty.app/api/categories
Retrieve all product categories in your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/categories \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/categories', {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/categories',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates whether the request was successful
Array of category objects
Unique identifier (UUID)
Workspace ID (UUID)
Category name
API identifier (unique, immutable)
Category description
Active status
ID of parent category for hierarchical organization (UUID)
Creation timestamp (ISO 8601)
Last update timestamp (ISO 8601)
```json Response theme={null}
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Electronics",
"api_name": "electronics",
"description": "Electronic products and accessories",
"is_active": true,
"parent_category_id": null,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z"
},
{
"id": "550e8400-e29b-41d4-a716-446655440002",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Smartphones",
"api_name": "smartphones",
"description": "Mobile phones and accessories",
"is_active": true,
"parent_category_id": "550e8400-e29b-41d4-a716-446655440001",
"created_at": "2026-01-16T14:30:00Z",
"updated_at": "2026-01-16T14:30:00Z"
}
]
}
```
# Update Category
Source: https://docs.qwoty.io/api-reference/categories/update
PATCH https://qwoty.app/api/categories/{id}
Update an existing product category
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
Category UUID
## Request Body
All fields are optional. Only provided fields will be updated.
Category name
Category description
Active status
UUID of parent category for hierarchical organization. Set to `null` to remove
parent.
The `api_name` field is immutable and cannot be updated after creation.
## Examples
```bash cURL theme={null}
curl -X PATCH https://qwoty.app/api/categories/550e8400-e29b-41d4-a716-446655440001 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Electronics",
"description": "Updated description for electronics",
"is_active": false
}'
```
```bash Update Parent theme={null}
curl -X PATCH https://qwoty.app/api/categories/550e8400-e29b-41d4-a716-446655440002 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"parent_category_id": "550e8400-e29b-41d4-a716-446655440001"
}'
```
```javascript JavaScript theme={null}
const categoryId = '550e8400-e29b-41d4-a716-446655440001'
const response = await fetch(`https://qwoty.app/api/categories/${categoryId}`, {
method: 'PATCH',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Updated Electronics',
description: 'Updated description for electronics',
is_active: false,
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
category_id = '550e8400-e29b-41d4-a716-446655440001'
response = requests.patch(
f'https://qwoty.app/api/categories/{category_id}',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'name': 'Updated Electronics',
'description': 'Updated description for electronics',
'is_active': False
}
)
data = response.json()
```
## Response
Indicates whether the request was successful
Updated category object
Unique identifier (UUID)
Workspace ID (UUID)
Category name
API identifier (immutable)
Category description
Active status
ID of parent category (UUID)
Creation timestamp (ISO 8601)
Last update timestamp (ISO 8601)
```json 200 Success theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Updated Electronics",
"api_name": "electronics",
"description": "Updated description for electronics",
"is_active": false,
"parent_category_id": null,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-06-03T14:30:00Z"
}
}
```
## Error Responses
```json 404 Not Found theme={null}
{
"success": false,
"error": "Category not found"
}
```
```json 400 Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": ["Field 'api_name' is required when creating a category"]
}
```
# Create Contact
Source: https://docs.qwoty.io/api-reference/contacts/create
POST https://qwoty.app/api/contacts
Create a new contact linked to a customer
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Request Body
UUID of the customer this contact belongs to
Contact's first name
Contact's last name
Contact's email address
Phone number
Assigned roles. Allowed values: `"buyer"`, `"signer"`, `"billing"`,
`"shipping"`, `"legal"`, `"admin"`
External CRM ID
External ERP ID
External accounting ID
## Examples
```bash cURL theme={null}
curl -X POST https://qwoty.app/api/contacts \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"first_name": "Jean",
"last_name": "Dupont",
"email": "jean.dupont@acme.com",
"phone": "+33 6 12 34 56 78",
"role": ["buyer", "signer"]
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/contacts', {
method: 'POST',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
customer_id: '550e8400-e29b-41d4-a716-446655440000',
first_name: 'Jean',
last_name: 'Dupont',
email: 'jean.dupont@acme.com',
phone: '+33 6 12 34 56 78',
role: ['buyer', 'signer'],
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.post(
'https://qwoty.app/api/contacts',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
json={
'customer_id': '550e8400-e29b-41d4-a716-446655440000',
'first_name': 'Jean',
'last_name': 'Dupont',
'email': 'jean.dupont@acme.com',
'phone': '+33 6 12 34 56 78',
'role': ['buyer', 'signer'],
},
)
data = response.json()
```
## Response
`true` if the contact was created
The created contact object (same shape as [Get
Contact](/api-reference/contacts/get))
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"workspace_id": "706eb564-6c4a-4e18-841c-bdd79be6bca7",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"first_name": "Jean",
"last_name": "Dupont",
"email": "jean.dupont@acme.com",
"phone": "+33 6 12 34 56 78",
"role": ["buyer", "signer"],
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}
```
```json Validation Error theme={null}
{
"error": "Validation error",
"details": [
{ "path": "email", "message": "Invalid email address" },
{ "path": "customer_id", "message": "customer_id must be a valid UUID" }
]
}
```
# Get Contact
Source: https://docs.qwoty.io/api-reference/contacts/get
GET https://qwoty.app/api/contacts/{id}
Retrieve a specific contact by ID
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the contact
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/contacts/660e8400-e29b-41d4-a716-446655440001 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const contactId = '660e8400-e29b-41d4-a716-446655440001'
const response = await fetch(`https://qwoty.app/api/contacts/${contactId}`, {
headers: { Authorization: 'Bearer qwoty_your_token' },
})
const data = await response.json()
```
```python Python theme={null}
import requests
contact_id = '660e8400-e29b-41d4-a716-446655440001'
response = requests.get(
f'https://qwoty.app/api/contacts/{contact_id}',
headers={'Authorization': 'Bearer qwoty_your_token'},
)
data = response.json()
```
## Response
`true` if the request succeeded
The contact object
UUID
Workspace UUID
Parent customer UUID
First name
Last name
Email address
Phone number
Assigned roles. Possible values: `buyer`, `signer`, `billing`, `shipping`, `legal`, `admin`
External CRM ID
External ERP ID
External accounting ID
Array of address UUIDs linked to this contact. Use `GET /api/addresses/{id}` to fetch each address.
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"workspace_id": "706eb564-6c4a-4e18-841c-bdd79be6bca7",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"first_name": "Jean",
"last_name": "Dupont",
"email": "jean.dupont@acme.com",
"phone": "+33 6 12 34 56 78",
"role": ["buyer", "signer"],
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"address_ids": ["c3d4e5f6-a7b8-9012-cdef-123456789012"],
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}
```
```json Not Found theme={null}
{
"success": false,
"error": "Contact not found"
}
```
# List Contacts
Source: https://docs.qwoty.io/api-reference/contacts/list
GET https://qwoty.app/api/contacts
Retrieve all contacts in your workspace
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Query Parameters
Filter contacts by customer UUID
## Examples
```bash cURL theme={null}
curl "https://qwoty.app/api/contacts?customer_id=550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch(
'https://qwoty.app/api/contacts?customer_id=550e8400-e29b-41d4-a716-446655440000',
{ headers: { Authorization: 'Bearer qwoty_your_token' } },
)
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/contacts',
params={'customer_id': '550e8400-e29b-41d4-a716-446655440000'},
headers={'Authorization': 'Bearer qwoty_your_token'},
)
data = response.json()
```
## Response
`true` if the request succeeded
Array of contact objects
UUID
Workspace UUID
Parent customer UUID
First name
Last name
Email address
Phone number
Assigned roles
External CRM ID
External ERP ID
External accounting ID
Array of address UUIDs linked to this contact. Use `GET /api/addresses/{id}` to fetch each address.
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": [
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"workspace_id": "706eb564-6c4a-4e18-841c-bdd79be6bca7",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"first_name": "Jean",
"last_name": "Dupont",
"email": "jean.dupont@acme.com",
"phone": "+33 6 12 34 56 78",
"role": ["buyer", "signer"],
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"address_ids": ["c3d4e5f6-a7b8-9012-cdef-123456789012"],
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
]
}
```
# Update Contact
Source: https://docs.qwoty.io/api-reference/contacts/update
PATCH https://qwoty.app/api/contacts/{id}
Partially update a contact
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the contact to update
## Request Body
All fields are optional. Only the fields provided will be updated.
Contact's first name
Contact's last name
Email address
Phone number. Set to `null` to unset.
Assigned roles. Allowed values: `"buyer"`, `"signer"`, `"billing"`,
`"shipping"`, `"legal"`, `"admin"`
External CRM ID
External ERP ID
External accounting ID
`customer_id` cannot be changed after creation.
## Examples
```bash cURL theme={null}
curl -X PATCH https://qwoty.app/api/contacts/660e8400-e29b-41d4-a716-446655440001 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"phone": "+33 6 99 88 77 66",
"role": ["buyer", "billing"]
}'
```
```javascript JavaScript theme={null}
const contactId = '660e8400-e29b-41d4-a716-446655440001'
const response = await fetch(`https://qwoty.app/api/contacts/${contactId}`, {
method: 'PATCH',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
phone: '+33 6 99 88 77 66',
role: ['buyer', 'billing'],
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
contact_id = '660e8400-e29b-41d4-a716-446655440001'
response = requests.patch(
f'https://qwoty.app/api/contacts/{contact_id}',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
json={'phone': '+33 6 99 88 77 66', 'role': ['buyer', 'billing']},
)
data = response.json()
```
## Response
`true` if the update succeeded
The updated contact object (same shape as [Get
Contact](/api-reference/contacts/get))
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "660e8400-e29b-41d4-a716-446655440001",
"customer_id": "550e8400-e29b-41d4-a716-446655440000",
"first_name": "Jean",
"last_name": "Dupont",
"email": "jean.dupont@acme.com",
"phone": "+33 6 99 88 77 66",
"role": ["buyer", "billing"],
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-03-24T14:00:00Z"
}
}
```
```json Not Found theme={null}
{
"success": false,
"error": "Contact not found"
}
```
# Create Customer
Source: https://docs.qwoty.io/api-reference/customers/create
POST https://qwoty.app/api/customers
Create a new customer in your workspace
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Request Body
Customer display name
Customer type. Allowed values: `"company"`, `"individual"`
UUID of the customer segment
Logo URL
Legal identifiers for this customer. Each entry must have both `tax` and `tax_value`.
```json theme={null}
[{ "tax": "VAT", "tax_value": "FR12345678901" }]
```
External CRM ID
External ERP ID
External accounting ID
## Examples
```bash cURL theme={null}
curl -X POST https://qwoty.app/api/customers \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corporation",
"type": "company",
"tax_ids": [
{ "tax": "VAT", "tax_value": "FR12345678901" }
],
"id_crm": "crm_001"
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/customers', {
method: 'POST',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Acme Corporation',
type: 'company',
tax_ids: [{ tax: 'VAT', tax_value: 'FR12345678901' }],
id_crm: 'crm_001',
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.post(
'https://qwoty.app/api/customers',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
json={
'name': 'Acme Corporation',
'type': 'company',
'tax_ids': [{'tax': 'VAT', 'tax_value': 'FR12345678901'}],
'id_crm': 'crm_001',
},
)
data = response.json()
```
## Response
`true` if the customer was created
The created customer object (same shape as [Get
Customer](/api-reference/customers/get))
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"workspace_id": "706eb564-6c4a-4e18-841c-bdd79be6bca7",
"name": "Acme Corporation",
"type": "company",
"segment_id": null,
"logo": null,
"id_crm": "crm_001",
"id_erp": null,
"id_accounting": null,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}
```
```json Validation Error theme={null}
{
"error": "Validation error",
"details": [
{ "path": "name", "message": "Name is required" },
{ "path": "type", "message": "Type must be \"company\" or \"individual\"" }
]
}
```
```json Unauthorized theme={null}
{
"error": "Invalid API token"
}
```
# Get Customer
Source: https://docs.qwoty.io/api-reference/customers/get
GET https://qwoty.app/api/customers/{id}
Retrieve a specific customer by ID, including their legal identifiers
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the customer
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/customers/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const customerId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(`https://qwoty.app/api/customers/${customerId}`, {
headers: { Authorization: 'Bearer qwoty_your_token' },
})
const data = await response.json()
```
```python Python theme={null}
import requests
customer_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.get(
f'https://qwoty.app/api/customers/{customer_id}',
headers={'Authorization': 'Bearer qwoty_your_token'},
)
data = response.json()
```
## Response
`true` if the request succeeded
The customer object
UUID
Workspace UUID
Customer name
`company` or `individual`
Segment UUID
Logo URL
Legal identifiers
Legal identifier code (e.g., `"VAT"`, `"SIREN"`)
Legal identifier value (e.g., `"FR12345678901"`)
External CRM ID
External ERP ID
External accounting ID
Array of contact UUIDs linked to this customer. Use `GET /api/contacts/{id}` to fetch each contact.
Array of address UUIDs linked to this customer. Use `GET /api/addresses/{id}` to fetch each address.
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"workspace_id": "706eb564-6c4a-4e18-841c-bdd79be6bca7",
"name": "Acme Corporation",
"type": "company",
"segment_id": null,
"logo": null,
"tax_ids": [
{ "tax": "VAT", "tax_value": "FR12345678901" },
{ "tax": "SIREN", "tax_value": "123456789" }
],
"id_crm": "crm_001",
"id_erp": null,
"id_accounting": null,
"contact_ids": [
"a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"b2c3d4e5-f6a7-8901-bcde-f12345678901"
],
"address_ids": ["c3d4e5f6-a7b8-9012-cdef-123456789012"],
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
}
```
```json Not Found theme={null}
{
"success": false,
"error": "Customer not found"
}
```
# List Customers
Source: https://docs.qwoty.io/api-reference/customers/list
GET https://qwoty.app/api/customers
Retrieve all customers in your workspace
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/customers \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/customers', {
headers: { Authorization: 'Bearer qwoty_your_token' },
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/customers',
headers={'Authorization': 'Bearer qwoty_your_token'},
)
data = response.json()
```
## Response
`true` if the request succeeded
Array of customer objects
UUID
Workspace UUID
Customer name
`company` or `individual`
Segment UUID
Logo URL
External CRM ID
External ERP ID
External accounting ID
Array of contact UUIDs linked to this customer. Use `GET /api/contacts/{id}` to fetch each contact.
Array of address UUIDs linked to this customer. Use `GET /api/addresses/{id}` to fetch each address.
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"workspace_id": "706eb564-6c4a-4e18-841c-bdd79be6bca7",
"name": "Acme Corporation",
"type": "company",
"segment_id": null,
"logo": null,
"id_crm": "crm_001",
"id_erp": null,
"id_accounting": null,
"contact_ids": ["a1b2c3d4-e5f6-7890-abcd-ef1234567890"],
"address_ids": ["c3d4e5f6-a7b8-9012-cdef-123456789012"],
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-01-15T10:30:00Z"
}
]
}
```
# Update Customer
Source: https://docs.qwoty.io/api-reference/customers/update
PATCH https://qwoty.app/api/customers/{id}
Partially update a customer
## Authorization
Bearer token. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the customer to update
## Request Body
All fields are optional. Only the fields provided will be updated.
Customer display name
Customer type. Allowed values: `"company"`, `"individual"`
UUID of the customer segment. Set to `null` to unset.
Logo URL. Set to `null` to unset.
Legal identifiers. **Replaces all existing tax IDs.** Send `[]` to remove all.
Omitting this field leaves existing tax IDs unchanged.
```json theme={null}
[{ "tax": "VAT", "tax_value": "FR98765432100" }]
```
External CRM ID
External ERP ID
External accounting ID
## Examples
```bash cURL theme={null}
curl -X PATCH https://qwoty.app/api/customers/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp (Renamed)",
"tax_ids": [
{ "tax": "VAT", "tax_value": "FR98765432100" }
]
}'
```
```javascript JavaScript theme={null}
const customerId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(`https://qwoty.app/api/customers/${customerId}`, {
method: 'PATCH',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Acme Corp (Renamed)',
tax_ids: [{ tax: 'VAT', tax_value: 'FR98765432100' }],
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
customer_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.patch(
f'https://qwoty.app/api/customers/{customer_id}',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
json={
'name': 'Acme Corp (Renamed)',
'tax_ids': [{'tax': 'VAT', 'tax_value': 'FR98765432100'}],
},
)
data = response.json()
```
## Response
`true` if the update succeeded
The updated customer object (same shape as [Get
Customer](/api-reference/customers/get))
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Acme Corp (Renamed)",
"type": "company",
"segment_id": null,
"logo": null,
"id_crm": "crm_001",
"id_erp": null,
"id_accounting": null,
"created_at": "2026-01-15T10:30:00Z",
"updated_at": "2026-03-24T14:00:00Z"
}
}
```
```json Not Found theme={null}
{
"success": false,
"error": "Customer not found"
}
```
# Get Media
Source: https://docs.qwoty.io/api-reference/media/get
GET https://qwoty.app/api/medias/{id}
Retrieve a media file by ID
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
The unique identifier of the media file
## Response
This endpoint returns a redirect (302) to the actual media file URL. The file can be directly accessed or embedded.
```bash cURL theme={null}
curl --request GET \
--url https://qwoty.app/api/medias/media_123abc \
--location
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/medias/media_123abc', {
redirect: 'follow',
})
// Get the final URL after redirect
const mediaUrl = response.url
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/medias/media_123abc',
allow_redirects=True
)
# Get the final URL
media_url = response.url
```
```html HTML theme={null}
```
```json 302 theme={null}
{
"Location": "https://storage.qwoty.io/files/abc123..."
}
```
```json 400 theme={null}
{
"error": "Media ID is required"
}
```
```json 404 theme={null}
{
"error": "Media not found"
}
```
The media URL is temporary and may expire. Always use the API endpoint to
retrieve the current URL.
# Create Payment Term
Source: https://docs.qwoty.io/api-reference/payment-terms/create
POST https://qwoty.app/api/payment-terms
Create a new payment term with custom installment schedules
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Request Body
Name of the payment term (max 50 characters)
Unique API identifier (slug format, e.g., `net_30_days`)
Internal reference code (max 50 characters)
Whether the payment term is active
External CRM identifier
External ERP identifier
External accounting system identifier
Array of payment installments. Total percentage must equal 100%.
Name of the installment
Percentage of total amount (0-100)
Payment term type. Options: `due_now`, `due_on_receipt`, `net15`, `net30`, `net45`, `net60`, `net90`, `net120`, `eom30`, `eom45`, `eom60`, `po_received`, `custom`
Custom text (required if term is `custom`)
Display order (1, 2, 3...)
The sum of all installment percentages must equal exactly 100%.
## Examples
```bash Simple Payment (100% Net 30) theme={null}
curl -X POST https://qwoty.app/api/payment-terms \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Net 30 Days",
"api_name": "net_30_days",
"reference": "PT001",
"is_active": true,
"installments": [
{
"name": "Full Payment",
"percentage": 100,
"term": "net30",
"order_number": 1
}
]
}'
```
```bash Split Payment (30% + 70%) theme={null}
curl -X POST https://qwoty.app/api/payment-terms \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "30% Deposit + 70% on Delivery",
"api_name": "30_deposit_70_delivery",
"reference": "PT002",
"is_active": true,
"installments": [
{
"name": "Deposit on Order",
"percentage": 30,
"term": "due_now",
"order_number": 1
},
{
"name": "Balance on Delivery",
"percentage": 70,
"term": "due_on_receipt",
"order_number": 2
}
]
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/payment-terms', {
method: 'POST',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Net 30 Days',
api_name: 'net_30_days',
is_active: true,
installments: [
{
name: 'Full Payment',
percentage: 100,
term: 'net30',
order_number: 1,
},
],
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.post(
'https://qwoty.app/api/payment-terms',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'name': 'Net 30 Days',
'api_name': 'net_30_days',
'is_active': True,
'installments': [
{
'name': 'Full Payment',
'percentage': 100,
'term': 'net30',
'order_number': 1
}
]
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The created payment term object
Unique identifier (UUID)
Payment term name
API identifier
Internal reference
Active status
Array of installment objects with id, name, percentage, term, and order\_number
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Net 30 Days",
"api_name": "net_30_days",
"reference": "PT001",
"is_active": true,
"installments": [
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Full Payment",
"percentage": 100,
"term": "net30",
"order_number": 1
}
],
"created_at": "2024-12-21T10:30:00Z"
}
}
```
## Error Responses
```json Validation Error theme={null}
{
"success": false,
"error": "Installment percentages must sum to 100%"
}
```
```json Duplicate API Name theme={null}
{
"success": false,
"error": "Payment term with api_name 'net_30_days' already exists"
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Delete Payment Term
Source: https://docs.qwoty.io/api-reference/payment-terms/delete
DELETE https://qwoty.app/api/payment-terms/{id}
Delete a payment term
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the payment term to delete
You cannot delete a payment term that is currently in use by active quotes or
contracts.
## Examples
```bash cURL theme={null}
curl -X DELETE https://qwoty.app/api/payment-terms/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const paymentTermId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(
`https://qwoty.app/api/payment-terms/${paymentTermId}`,
{
method: 'DELETE',
headers: {
Authorization: 'Bearer qwoty_your_token',
},
},
)
const data = await response.json()
```
```python Python theme={null}
import requests
payment_term_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.delete(
f'https://qwoty.app/api/payment-terms/{payment_term_id}',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates if the deletion was successful
Confirmation message
```json Success Response theme={null}
{
"success": true,
"message": "Payment term deleted successfully"
}
```
## Error Responses
```json Not Found theme={null}
{
"success": false,
"error": "Payment term not found"
}
```
```json In Use theme={null}
{
"success": false,
"error": "Cannot delete payment term: currently in use by 3 active quotes"
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
```json Already Deleted theme={null}
{
"success": false,
"error": "Payment term has already been deleted"
}
```
## Alternative: Deactivate Instead
If you want to stop using a payment term but preserve it for historical records, consider deactivating it instead:
```bash theme={null}
curl -X PUT https://qwoty.app/api/payment-terms/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"is_active": false
}'
```
Deactivating a payment term prevents it from being used in new quotes while
preserving all historical data.
# Get Payment Term
Source: https://docs.qwoty.io/api-reference/payment-terms/get
GET https://qwoty.app/api/payment-terms/{id}
Retrieve a specific payment term by ID
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the payment term
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/payment-terms/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const paymentTermId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(
`https://qwoty.app/api/payment-terms/${paymentTermId}`,
{
headers: {
Authorization: 'Bearer qwoty_your_token',
},
},
)
const data = await response.json()
```
```python Python theme={null}
import requests
payment_term_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.get(
f'https://qwoty.app/api/payment-terms/{payment_term_id}',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The payment term object
Unique identifier (UUID)
Payment term name
API identifier
Internal reference
Active status
External CRM ID
External ERP ID
External accounting ID
Array of installment objects with id, name, percentage, term, custom\_text, and order\_number
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Net 30 Days",
"api_name": "net_30_days",
"reference": "PT001",
"is_active": true,
"id_crm": "crm_123",
"id_erp": "erp_456",
"id_accounting": "acc_789",
"installments": [
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Full Payment",
"percentage": 100,
"term": "net30",
"custom_text": null,
"order_number": 1
}
],
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T10:30:00Z"
}
}
```
## Error Responses
```json Not Found theme={null}
{
"success": false,
"error": "Payment term not found"
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
```json Invalid ID Format theme={null}
{
"success": false,
"error": "Invalid payment term ID format"
}
```
# List Payment Terms
Source: https://docs.qwoty.io/api-reference/payment-terms/list
GET https://qwoty.app/api/payment-terms
Retrieve all payment terms for your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Query Parameters
Filter by active status. Omit to return all payment terms.
Search by name or api\_name
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/payment-terms \
-H "Authorization: Bearer qwoty_your_token"
```
```bash Filter Active Only theme={null}
curl "https://qwoty.app/api/payment-terms?is_active=true" \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/payment-terms', {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/payment-terms',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
Array of payment term objects
Unique identifier (UUID)
Payment term name
API identifier
Internal reference
Active status
External CRM ID
External ERP ID
External accounting ID
Array of installment objects
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Net 30 Days",
"api_name": "net_30_days",
"reference": "PT001",
"is_active": true,
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"installments": [
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Full Payment",
"percentage": 100,
"term": "net30",
"custom_text": null,
"order_number": 1
}
],
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T10:30:00Z"
},
{
"id": "550e8400-e29b-41d4-a716-446655440002",
"name": "30% Deposit + 70% on Delivery",
"api_name": "30_deposit_70_delivery",
"reference": "PT002",
"is_active": true,
"id_crm": "crm_123",
"id_erp": null,
"id_accounting": null,
"installments": [
{
"id": "660e8400-e29b-41d4-a716-446655440003",
"name": "Deposit",
"percentage": 30,
"term": "due_now",
"custom_text": null,
"order_number": 1
},
{
"id": "660e8400-e29b-41d4-a716-446655440004",
"name": "Balance",
"percentage": 70,
"term": "due_on_receipt",
"custom_text": null,
"order_number": 2
}
],
"created_at": "2024-12-21T11:00:00Z",
"updated_at": "2024-12-21T11:00:00Z"
}
]
}
```
## Error Responses
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Update Payment Term
Source: https://docs.qwoty.io/api-reference/payment-terms/update
PUT https://qwoty.app/api/payment-terms/{id}
Update an existing payment term
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the payment term to update
## Request Body
Name of the payment term (max 50 characters)
Unique API identifier (slug format)
Internal reference code (max 50 characters)
Whether the payment term is active
External CRM identifier
External ERP identifier
External accounting system identifier
Array of payment installments. If provided, replaces all existing installments.
Name of the installment
Percentage of total amount (0-100)
Payment term type
Custom text (required if term is `custom`)
Display order (1, 2, 3...)
You can update individual fields or the entire payment term. Omitted fields
remain unchanged.
If updating installments, the sum of all percentages must equal 100%.
## Examples
```bash Update Name Only theme={null}
curl -X PUT https://qwoty.app/api/payment-terms/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Net 30 Days (Updated)"
}'
```
```bash Update Status theme={null}
curl -X PUT https://qwoty.app/api/payment-terms/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"is_active": false
}'
```
```bash Update Complete Payment Term theme={null}
curl -X PUT https://qwoty.app/api/payment-terms/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "50% Deposit + 50% Net 30",
"api_name": "50_deposit_50_net30",
"installments": [
{
"name": "Deposit",
"percentage": 50,
"term": "due_now",
"order_number": 1
},
{
"name": "Balance",
"percentage": 50,
"term": "net30",
"order_number": 2
}
]
}'
```
```javascript JavaScript theme={null}
const paymentTermId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(
`https://qwoty.app/api/payment-terms/${paymentTermId}`,
{
method: 'PUT',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Net 30 Days (Updated)',
is_active: true,
}),
},
)
const data = await response.json()
```
```python Python theme={null}
import requests
payment_term_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.put(
f'https://qwoty.app/api/payment-terms/{payment_term_id}',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'name': 'Net 30 Days (Updated)',
'is_active': True
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The updated payment term object with all fields
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Net 30 Days (Updated)",
"api_name": "net_30_days",
"reference": "PT001",
"is_active": true,
"id_crm": "crm_123",
"id_erp": "erp_456",
"id_accounting": "acc_789",
"installments": [
{
"id": "660e8400-e29b-41d4-a716-446655440001",
"name": "Full Payment",
"percentage": 100,
"term": "net30",
"custom_text": null,
"order_number": 1
}
],
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T14:45:00Z"
}
}
```
## Error Responses
```json Not Found theme={null}
{
"success": false,
"error": "Payment term not found"
}
```
```json Validation Error theme={null}
{
"success": false,
"error": "Installment percentages must sum to 100%"
}
```
```json Duplicate API Name theme={null}
{
"success": false,
"error": "Payment term with api_name 'net_30_days' already exists"
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Get Price
Source: https://docs.qwoty.io/api-reference/prices/get
GET /api/prices/{id}
Retrieve a specific price by ID
Returns detailed information about a specific price, including all pricing configuration and enriched data.
## Authentication
Bearer token for authentication
## Path Parameters
The unique identifier of the price
## Response
Indicates if the request was successful
The complete price object with all fields and enriched data
Unique identifier for the price
ID of the product this price applies to
Name of the product variant (enriched)
ID of the pricebook
Name of the pricebook (enriched)
Type: `fixed` or `dynamic` (enriched)
ID of the currency
Currency name (enriched)
Currency code, e.g., `EUR`, `USD` (enriched)
One of: `flat`, `volume`, `graduated`, `percent`, `cost_based`, `none`
Fixed price amount (for flat model)
Volume pricing tiers
Starting unit for this tier
Ending unit (null for unlimited)
Price per unit in this tier
Fixed fee for this tier
Show volume tiers to customers
Graduated pricing tiers (same structure as volume\_tiers)
Show graduated tiers to customers
Percentage value (0-100) for percent-based pricing
Percentage scope: `all`, `product_type`, or `product`
Multiplier applied to cost for cost-based pricing
Base cost price
Minimum acceptable price
Whether this is a one-time setup fee
Whether this uses pay-as-you-go billing
ID of default sales tax
Tax name (enriched)
Tax rate percentage (enriched)
Fixed tax amount (enriched)
Whether tax is percentage-based (enriched)
ID of billing frequency for recurring prices
Number of billing periods (enriched)
Billing period unit: `day`, `week`, `month`, `year` (enriched)
`forever` or `fixed_period`
Number of periods for fixed engagements
Parent price ID for dynamic pricebooks
External system identifiers
External CRM system identifier
External ERP system identifier
External accounting system identifier
Workspace this price belongs to
Creation timestamp
Last update timestamp
```json Success Response - Flat Pricing theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"product_id": "660e8400-e29b-41d4-a716-446655440001",
"product_variant_name": "Premium Widget",
"pricebook_id": "880e8400-e29b-41d4-a716-446655440003",
"pricebook_name": "Standard Pricebook",
"pricebook_type": "fixed",
"currency_id": "990e8400-e29b-41d4-a716-446655440004",
"currency_name": "Euro",
"currency_api_name": "EUR",
"pricing_model": "flat",
"amount": 99.99,
"volume_tiers": null,
"is_volume_grid_displayed": false,
"graduated_tiers": null,
"is_graduated_grid_displayed": false,
"percent": null,
"price_percentage_type": null,
"cost_multiplier": null,
"cost": 50.0,
"floor_price": 80.0,
"is_setup": false,
"is_pay_as_you_go": false,
"default_tax_id": "aa0e8400-e29b-41d4-a716-446655440005",
"default_tax_name": "VAT 20%",
"default_tax_rate": 20.0,
"default_tax_amount": null,
"default_tax_is_percentage": true,
"billing_frequency_id": null,
"billing_frequency_period": null,
"billing_frequency_unit": null,
"engagement_type": null,
"period_duration": null,
"parent_price_id": null,
"identifiers": {
"crm": "crm_price_001",
"erp": "erp_price_001",
"accounting": "acc_price_001"
},
"workspace_id": "cc0e8400-e29b-41d4-a716-446655440007",
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T14:45:00Z"
}
}
```
```json Success Response - Volume Pricing theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"pricing_model": "volume",
"amount": null,
"volume_tiers": [
{
"first_unit": 1,
"last_unit": 100,
"amount_per_unit": 10.0,
"flat_fee": 0
},
{
"first_unit": 101,
"last_unit": 500,
"amount_per_unit": 8.5,
"flat_fee": 0
},
{
"first_unit": 501,
"last_unit": null,
"amount_per_unit": 7.0,
"flat_fee": 0
}
],
"is_volume_grid_displayed": true,
"currency_api_name": "EUR"
}
}
```
```json Error Response theme={null}
{
"success": false,
"error": "Price not found"
}
```
```bash cURL theme={null}
curl -X GET https://qwoty.app/api/prices/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch(
'https://qwoty.app/api/prices/550e8400-e29b-41d4-a716-446655440000',
{
headers: {
Authorization: 'Bearer qwoty_your_token',
},
},
)
const { success, data } = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/prices/550e8400-e29b-41d4-a716-446655440000',
headers={'Authorization': 'Bearer qwoty_your_token'},
)
data = response.json()
```
# List All Prices
Source: https://docs.qwoty.io/api-reference/prices/list
GET /api/prices
Retrieve all prices for your workspace
Returns a list of all prices in your workspace with enriched data including product variant, pricebook, currency, and tax information.
## Authentication
Bearer token for authentication
## Query Parameters
Maximum number of prices to return. Min: 1, Max: 1000.
Number of prices to skip (for pagination).
## Response
Indicates if the request was successful
Array of price objects with enriched data
Unique identifier for the price
ID of the product this price applies to
Name of the product variant (enriched)
ID of the pricebook containing this price
Name of the pricebook (enriched)
Type of pricebook: `fixed` or `dynamic` (enriched)
ID of the currency for this price
Name of the currency (enriched)
API name/code of the currency, e.g., `EUR`, `USD` (enriched)
Pricing model: `flat`, `volume`, `graduated`, `percent`, `cost_based`, or `none`
Fixed amount for flat pricing model
Array of volume tier objects for volume pricing
Whether to display volume tiers to customers
Array of graduated tier objects for graduated pricing
Whether to display graduated tiers to customers
Percentage value (0-100) for percent pricing
Scope of percentage pricing: `all`, `product_type`, or `product`
Multiplier for cost-based pricing
Cost price
Minimum price threshold
Whether this is a setup or one-time fee
Whether this uses pay-as-you-go billing
ID of the default sales tax
Name of the default tax (enriched)
Tax rate percentage (enriched)
Fixed tax amount (enriched)
Whether tax is percentage-based (enriched)
ID of the billing frequency for recurring prices
Number of periods (enriched)
Unit of period: `day`, `week`, `month`, or `year` (enriched)
Type of engagement: `forever` or `fixed_period`
Duration in periods for fixed engagements
ID of parent price for dynamic pricebooks
External system identifiers
External CRM system identifier
External ERP system identifier
External accounting system identifier
Timestamp of price creation
Timestamp of last update
Workspace this price belongs to
Total number of prices in the workspace
Number of items returned per page
Number of items skipped
```json Success Response theme={null}
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"product_id": "660e8400-e29b-41d4-a716-446655440001",
"product_variant_name": "Premium Widget",
"pricebook_id": "880e8400-e29b-41d4-a716-446655440003",
"pricebook_name": "Standard Pricebook",
"pricebook_type": "fixed",
"currency_id": "990e8400-e29b-41d4-a716-446655440004",
"currency_name": "Euro",
"currency_api_name": "EUR",
"pricing_model": "flat",
"amount": 99.99,
"volume_tiers": null,
"is_volume_grid_displayed": false,
"graduated_tiers": null,
"is_graduated_grid_displayed": false,
"percent": null,
"price_percentage_type": null,
"cost_multiplier": null,
"cost": 50.0,
"floor_price": 80.0,
"is_setup": false,
"is_pay_as_you_go": false,
"default_tax_id": "aa0e8400-e29b-41d4-a716-446655440005",
"default_tax_name": "VAT 20%",
"default_tax_rate": 20.0,
"default_tax_amount": null,
"default_tax_is_percentage": true,
"billing_frequency_id": null,
"billing_frequency_period": null,
"billing_frequency_unit": null,
"engagement_type": null,
"period_duration": null,
"parent_price_id": null,
"identifiers": {
"crm": "crm_price_001",
"erp": "erp_price_001",
"accounting": "acc_price_001"
},
"workspace_id": "cc0e8400-e29b-41d4-a716-446655440007",
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T14:45:00Z"
}
],
"total": 1,
"limit": 100,
"offset": 0
}
```
```json Empty Response theme={null}
{
"success": true,
"data": [],
"total": 0,
"limit": 100,
"offset": 0
}
```
```json Error Response theme={null}
{
"success": false,
"error": "Failed to fetch prices"
}
```
```bash cURL theme={null}
curl -X GET "https://qwoty.app/api/prices?limit=50&offset=0" \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/prices?limit=50&offset=0', {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const { success, data, total, limit, offset } = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/prices',
params={'limit': 50, 'offset': 0},
headers={'Authorization': 'Bearer qwoty_your_token'},
)
body = response.json()
```
# Update Price
Source: https://docs.qwoty.io/api-reference/prices/update
PATCH /api/prices/{id}
Update an existing price configuration
Updates an existing price with partial data (PATCH semantics). Only provided fields are updated. Creation-only fields cannot be modified.
## Authentication
Bearer token for authentication
## Path Parameters
The unique identifier of the price to update
## Request Body
All fields are optional. Only include the fields you want to update.
Pricing model: `flat`, `volume`, `graduated`, `percent`, `cost_based`, or
`none`
Fixed price amount (for flat pricing)
Array of volume tier objects
Whether to display volume tiers to customers
Array of graduated tier objects
Whether to display graduated tiers to customers
Percentage value 0-100
Scope: `all`, `product_type`, or `product`
Multiplier for cost-based pricing
Base cost price
Minimum acceptable price
Whether this is a setup fee
Whether this uses pay-as-you-go billing
ID of the default sales tax
ID of billing frequency
`forever` or `fixed_period`
Number of billing periods
Parent price ID for dynamic pricebooks
External CRM identifier
External ERP identifier
External accounting identifier
## Immutable Fields
The following fields cannot be modified after creation:
* `product_id`
* `pricebook_id`
* `currency_id`
## Response
Indicates if the request was successful
The updated price object with all fields and enriched data
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"product_id": "660e8400-e29b-41d4-a716-446655440001",
"product_variant_name": "Premium Widget",
"pricebook_id": "880e8400-e29b-41d4-a716-446655440003",
"pricebook_name": "Standard Pricebook",
"pricebook_type": "fixed",
"currency_id": "990e8400-e29b-41d4-a716-446655440004",
"currency_name": "Euro",
"currency_api_name": "EUR",
"pricing_model": "flat",
"amount": 129.99,
"volume_tiers": null,
"is_volume_grid_displayed": false,
"graduated_tiers": null,
"is_graduated_grid_displayed": false,
"percent": null,
"price_percentage_type": null,
"cost_multiplier": null,
"cost": 50.0,
"floor_price": 99.99,
"is_setup": false,
"is_pay_as_you_go": false,
"default_tax_id": "aa0e8400-e29b-41d4-a716-446655440005",
"default_tax_name": "VAT 20%",
"default_tax_rate": 20.0,
"default_tax_amount": null,
"default_tax_is_percentage": true,
"billing_frequency_id": null,
"billing_frequency_period": null,
"billing_frequency_unit": null,
"engagement_type": null,
"period_duration": null,
"parent_price_id": null,
"identifiers": {
"crm": "crm_price_updated",
"erp": "erp_price_updated",
"accounting": "acc_price_updated"
},
"workspace_id": "cc0e8400-e29b-41d4-a716-446655440007",
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T15:20:00Z"
}
}
```
```json Not Found Error theme={null}
{
"success": false,
"error": "Price not found"
}
```
```json Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": ["Field 'amount' must be a positive number"]
}
```
```bash cURL theme={null}
curl -X PATCH https://qwoty.app/api/prices/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"amount": 129.99,
"floor_price": 99.99,
"identifiers[crm]": "crm_price_updated",
"identifiers[erp]": "erp_price_updated",
"identifiers[accounting]": "acc_price_updated"
}'
```
```python Python theme={null}
import requests
price_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.patch(
f'https://qwoty.app/api/prices/{price_id}',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'amount': 129.99,
'floor_price': 99.99,
'identifiers[crm]': 'crm_price_updated',
'identifiers[erp]': 'erp_price_updated',
'identifiers[accounting]': 'acc_price_updated'
}
)
data = response.json()
```
# Create Product Parent
Source: https://docs.qwoty.io/api-reference/product-parents/create
POST https://qwoty.app/api/product-parents
Create a new product parent in your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Request Body
Unique API identifier (snake\_case)
Billing model. Allowed values: `one_off`, `recurring`
Product parent name
Internal reference
Product description
Active status
External CRM ID
External ERP ID
External accounting ID
## Examples
```bash cURL theme={null}
curl -X POST https://qwoty.app/api/product-parents \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"api_name": "premium_widget",
"recurrence_type": "recurring",
"name": "Premium Widget",
"description": "High-quality widget for professional use",
"reference": "PROD-001",
"is_active": true
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/product-parents', {
method: 'POST',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
api_name: 'premium_widget',
recurrence_type: 'recurring',
name: 'Premium Widget',
description: 'High-quality widget for professional use',
reference: 'PROD-001',
is_active: true,
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.post(
'https://qwoty.app/api/product-parents',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'api_name': 'premium_widget',
'recurrence_type': 'recurring',
'name': 'Premium Widget',
'description': 'High-quality widget for professional use',
'reference': 'PROD-001',
'is_active': True
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The created product parent object with all fields
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"catalog_ids": ["660e8400-e29b-41d4-a716-446655440001"],
"name": "Premium Widget",
"api_name": "premium_widget",
"recurrence_type": "recurring",
"reference": "PROD-001",
"description": "High-quality widget for professional use",
"is_active": true,
"id_crm": null,
"id_erp": null,
"id_accounting": null,
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T10:30:00Z"
}
}
```
## Error Responses
```json Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": [
"Field 'api_name' is required",
"Field 'recurrence_type' is required (one_off | recurring)"
]
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Get Product Parent
Source: https://docs.qwoty.io/api-reference/product-parents/get
GET https://qwoty.app/api/product-parents/{id}
Retrieve a specific product parent by ID
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the product parent
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/product-parents/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const productParentId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(
`https://qwoty.app/api/product-parents/${productParentId}`,
{
headers: {
Authorization: 'Bearer qwoty_your_token',
},
},
)
const data = await response.json()
```
```python Python theme={null}
import requests
product_parent_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.get(
f'https://qwoty.app/api/product-parents/{product_parent_id}',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The product parent object
Unique identifier (UUID)
Product parent name
API identifier
Billing model (`one_off` or `recurring`)
Linked catalog IDs
Linked product IDs
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Premium Widget",
"api_name": "premium_widget",
"recurrence_type": "recurring",
"catalog_ids": ["660e8400-e29b-41d4-a716-446655440001"],
"product_ids": ["770e8400-e29b-41d4-a716-446655440002"]
}
}
```
## Error Responses
```json Not Found theme={null}
{
"success": false,
"error": "Product parent not found"
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# List Product Parents
Source: https://docs.qwoty.io/api-reference/product-parents/list
GET https://qwoty.app/api/product-parents
Retrieve a list of all product parents in your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/product-parents \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/product-parents', {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/product-parents',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
Array of product parent objects
Unique identifier (UUID)
Product parent name
API identifier
Internal reference
Product description
Active status
Billing model (`one_off` or `recurring`)
Linked catalog IDs
Linked product IDs
External CRM ID
External ERP ID
External accounting ID
ISO 8601 timestamp
ISO 8601 timestamp
```json Success Response theme={null}
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"catalog_ids": ["660e8400-e29b-41d4-a716-446655440001"],
"product_ids": ["770e8400-e29b-41d4-a716-446655440002"],
"name": "Premium Widget",
"api_name": "premium_widget",
"recurrence_type": "recurring",
"reference": "PROD-001",
"description": "High-quality widget for professional use",
"is_active": true,
"id_crm": "crm_123",
"id_erp": "erp_456",
"id_accounting": "acc_789",
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T10:30:00Z"
}
]
}
```
## Error Responses
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Update Product Parent
Source: https://docs.qwoty.io/api-reference/product-parents/update
PATCH https://qwoty.app/api/product-parents/{id}
Update an existing product parent
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the product parent to update
## Request Body
Billing model (`one_off` or `recurring`)
Product parent name
Product description
Active status
External CRM ID
External ERP ID
External accounting ID
`reference` is not part of the product parent payload for PATCH (it is stored on the default variant). Sending `reference` in the body results in a validation error.
## Examples
```bash cURL theme={null}
curl -X PATCH https://qwoty.app/api/product-parents/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Product Name",
"description": "Updated description",
"is_active": false
}'
```
```javascript JavaScript theme={null}
const productParentId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(
`https://qwoty.app/api/product-parents/${productParentId}`,
{
method: 'PATCH',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Updated Product Parent Name',
description: 'Updated description',
recurrence_type: 'one_off',
}),
},
)
const data = await response.json()
```
```python Python theme={null}
import requests
product_parent_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.patch(
f'https://qwoty.app/api/product-parents/{product_parent_id}',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'name': 'Updated Product Parent Name',
'description': 'Updated description',
'recurrence_type': 'one_off'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The updated product parent object with all fields
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Updated Product Parent Name",
"api_name": "updated_product_parent_name",
"description": "Updated description",
"is_active": true,
"recurrence_type": "one_off",
"id_crm": "crm_123",
"id_erp": "erp_456",
"id_accounting": "acc_789",
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T14:45:00Z"
}
}
```
## Error Responses
```json Not Found theme={null}
{
"success": false,
"error": "Product parent not found"
}
```
```json Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": ["Field 'api_name' is creation-only and cannot be updated"]
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Create Product
Source: https://docs.qwoty.io/api-reference/products/create
POST https://qwoty.app/api/products
Create a new product (master + default variant) in your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Request Body
Unique API identifier (snake\_case). Can only contain lowercase letters,
numbers and underscores.
Billing model. Allowed values: `one_off`, `recurring`
Product name
Internal reference code for the default variant
Product description
Active status
Array of catalog UUIDs to link the product to. The workspace default catalog
is always included automatically.
Array of product category UUIDs to assign to this product.
Stock Keeping Unit
Number of units per pack
Ledger account code
External CRM identifier
External ERP identifier
External accounting identifier
Shipping weight
Weight unit. Allowed values: `kg`, `lbs`
Shipping height
Shipping length
Shipping width
Length unit (e.g., `cm`, `in`)
ISO country code of origin
HS code for customs
## Examples
```bash cURL theme={null}
curl -X POST https://qwoty.app/api/products \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"api_name": "premium_widget",
"recurrence_type": "one_off",
"name": "Premium Widget",
"description": "High-quality widget for professional use",
"reference": "PROD-001",
"is_active": true,
"catalog_ids": ["660e8400-e29b-41d4-a716-446655440001"],
"category_ids": ["770e8400-e29b-41d4-a716-446655440002"],
"inventory[sku]": "WID-PRE-001",
"settings[unit_per_pack]": 1,
"accounting[ledger_account]": "4010",
"identifiers[crm]": "crm_123",
"identifiers[erp]": "erp_456",
"identifiers[accounting]": "acc_789",
"shipping[weight]": 2.5,
"shipping[weight_unit]": "kg",
"shipping[height]": 10,
"shipping[length]": 30,
"shipping[width]": 20,
"shipping[length_unit]": "cm",
"shipping[country_of_origin]": "FR",
"shipping[harmonized_system_code]": "8471.30"
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/products', {
method: 'POST',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
api_name: 'premium_widget',
recurrence_type: 'one_off',
name: 'Premium Widget',
description: 'High-quality widget for professional use',
reference: 'PROD-001',
is_active: true,
catalog_ids: ['660e8400-e29b-41d4-a716-446655440001'],
category_ids: ['770e8400-e29b-41d4-a716-446655440002'],
'inventory[sku]': 'WID-PRE-001',
'settings[unit_per_pack]': 1,
'accounting[ledger_account]': '4010',
'identifiers[crm]': 'crm_123',
'identifiers[erp]': 'erp_456',
'identifiers[accounting]': 'acc_789',
'shipping[weight]': 2.5,
'shipping[weight_unit]': 'kg',
'shipping[height]': 10,
'shipping[length]': 30,
'shipping[width]': 20,
'shipping[length_unit]': 'cm',
'shipping[country_of_origin]': 'FR',
'shipping[harmonized_system_code]': '8471.30',
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.post(
'https://qwoty.app/api/products',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'api_name': 'premium_widget',
'recurrence_type': 'one_off',
'name': 'Premium Widget',
'description': 'High-quality widget for professional use',
'reference': 'PROD-001',
'is_active': True,
'catalog_ids': ['660e8400-e29b-41d4-a716-446655440001'],
'category_ids': ['770e8400-e29b-41d4-a716-446655440002'],
'inventory[sku]': 'WID-PRE-001',
'settings[unit_per_pack]': 1,
'accounting[ledger_account]': '4010',
'identifiers[crm]': 'crm_123',
'identifiers[erp]': 'erp_456',
'identifiers[accounting]': 'acc_789',
'shipping[weight]': 2.5,
'shipping[weight_unit]': 'kg',
'shipping[height]': 10,
'shipping[length]': 30,
'shipping[width]': 20,
'shipping[length_unit]': 'cm',
'shipping[country_of_origin]': 'FR',
'shipping[harmonized_system_code]': '8471.30',
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The created product object (variant structure, identical to `GET /api/products/{id}`)
Product ID (UUID) — this is the default variant ID
Parent product ID (UUID)
Workspace ID (UUID)
Product name
API identifier (creation-only, cannot be modified)
Internal reference
Product description
Active status
Always `true` for the default variant
Catalog IDs the product was linked to (includes the workspace default catalog)
Category IDs linked to this product
Array containing the default variant ID (same as `id`)
Settings: `unit_per_pack`, `product_type`, `language_id`, `unit_of_measure`, `recurrence_type`
Shipping info: `weight`, `weight_unit`, `height`, `length`, `width`, `length_unit`, `country_of_origin`, `harmonized_system_code`
Inventory info: `sku`
External identifiers: `crm`, `erp`, `accounting`
Accounting info: `ledger_account`
Option values (empty array for default variant)
Prices created across all pricebooks for this product
Price ID (UUID)
Pricebook ID (UUID)
API name of the pricebook
Primary image ID (null at creation)
Parent product primary image ID
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "880e8400-e29b-41d4-a716-446655440003",
"product_parent_id": "550e8400-e29b-41d4-a716-446655440000",
"workspace_id": "990e8400-e29b-41d4-a716-446655440004",
"name": "Premium Widget",
"api_name": "premium_widget",
"reference": "PROD-001",
"description": "High-quality widget for professional use",
"description_inherit_from_product": true,
"primary_image_inherit_from_product": true,
"is_active": true,
"is_default": true,
"catalog_ids": ["660e8400-e29b-41d4-a716-446655440001"],
"category_ids": ["770e8400-e29b-41d4-a716-446655440002"],
"product_ids": ["880e8400-e29b-41d4-a716-446655440003"],
"settings": {
"unit_per_pack": 1,
"product_type": null,
"language_id": null,
"unit_of_measure": null,
"recurrence_type": "one_off"
},
"shipping": {
"weight": 2.5,
"weight_unit": "kg",
"height": 10,
"length": 30,
"width": 20,
"length_unit": "cm",
"country_of_origin": "FR",
"harmonized_system_code": "8471.30"
},
"inventory": {
"sku": "WID-PRE-001"
},
"identifiers": {
"crm": "crm_123",
"erp": "erp_456",
"accounting": "acc_789"
},
"accounting": {
"ledger_account": "4010"
},
"options": [],
"prices": [
{
"id": "price-001",
"pricebook_id": "pb-001",
"pricebook_api_name": "standard"
}
],
"primary_image_id": null,
"parent_primary_image_id": null,
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T10:30:00Z"
}
}
```
## Error Responses
```json Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": [
"Field 'api_name' is required",
"Field 'recurrence_type' is required (one_off | recurring)"
]
}
```
```json Duplicate api_name theme={null}
{
"success": false,
"error": "Validation error",
"details": ["A product with api_name 'premium_widget' already exists"]
}
```
```json Catalog Not Found theme={null}
{
"success": false,
"error": "Validation error",
"details": ["Catalog(s) not found: 660e8400-e29b-41d4-a716-446655440001"]
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Get Product
Source: https://docs.qwoty.io/api-reference/products/get
GET https://qwoty.app/api/products/{id}
Retrieve a specific product by ID
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the product
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/products/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const productId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(`https://qwoty.app/api/products/${productId}`, {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
product_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.get(
f'https://qwoty.app/api/products/{product_id}',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The product object
Unique identifier (UUID)
ID of parent product (UUID)
Workspace ID (UUID)
Product name (display name combining parent + variant options)
API identifier (creation-only)
Internal reference
Product description
Whether to inherit description from parent product
Whether to inherit primary image from parent product
Active status
Whether this is the default product
Catalogs linked to the parent product
Catalog ID (UUID)
Catalog API name
Categories linked to the parent product
Category ID (UUID)
Category API name
Product settings and configuration
Number of units per pack
Parent product type
Parent language code (e.g. `en`, `fr`)
Parent unit of measure
Parent recurrence type (`one_off` or `recurring`)
Shipping information
Shipping weight
Shipping weight unit (kg, lbs)
Shipping height
Shipping length
Shipping width
Shipping length unit (cm, in, etc.)
Country of origin for shipping
HS code for customs
Inventory information
Stock Keeping Unit
External system identifiers
External CRM ID
External accounting ID
External ERP ID
Accounting information
Ledger account code
UUID of the primary media for this product. Null if none is set.
When `primary_image_inherit_from_product` is `true` and this field is null,
use `parent_primary_image_id` as the effective image.
UUID of the primary media of the parent product. Read-only.
Useful when `primary_image_inherit_from_product` is `true`.
Variant option values (e.g., color, size)
Option definition ID
Option name (e.g., "Color")
Option API name
Option value ID
Option value (e.g., "Blue")
Value API name
Prices associated with this product across all pricebooks
Price ID (UUID)
Pricebook ID (UUID)
API name of the pricebook
Pricing model (`flat`, `volume`, `graduated`, `percent`, `cost_based`, `none`)
Unit price. Only populated when `pricing_model` is `flat`, otherwise `null`.
ISO 8601 timestamp
ISO 8601 timestamp
User ID who created the product
User ID who last updated the product
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"product_parent_id": "660e8400-e29b-41d4-a716-446655440001",
"workspace_id": "880e8400-e29b-41d4-a716-446655440003",
"name": "Consulting Services - Senior",
"api_name": "consulting_services_senior",
"reference": "SERV-001",
"description": "Senior consultant hourly rate",
"description_inherit_from_product": true,
"primary_image_inherit_from_product": true,
"is_active": true,
"is_default": true,
"catalogs": [
{
"id": "770e8400-e29b-41d4-a716-446655440002",
"api_name": "catalog_main"
}
],
"categories": [],
"settings": {
"unit_per_pack": 1,
"product_type": "service",
"language_code": "en",
"unit_of_measure": "hour",
"recurrence_type": "recurring"
},
"shipping": {
"weight": null,
"weight_unit": null,
"height": null,
"length": null,
"width": null,
"length_unit": null,
"country_of_origin": null,
"harmonized_system_code": null
},
"inventory": {
"sku": "CONS-SEN-001"
},
"identifiers": {
"crm": "crm_123",
"accounting": "acc_789",
"erp": "erp_456"
},
"accounting": {
"ledger_account": "4010"
},
"primary_image_id": "cc0e8400-e29b-41d4-a716-446655440020",
"parent_primary_image_id": "dd0e8400-e29b-41d4-a716-446655440021",
"options": [
{
"option_id": "aa0e8400-e29b-41d4-a716-446655440010",
"option_name": "Seniority",
"option_api_name": "seniority",
"value_id": "bb0e8400-e29b-41d4-a716-446655440011",
"value_name": "Senior",
"value_api_name": "senior"
}
],
"prices": [
{
"id": "ee0e8400-e29b-41d4-a716-446655440030",
"pricebook_id": "ff0e8400-e29b-41d4-a716-446655440031",
"pricebook_api_name": "standard",
"pricing_model": "flat",
"amount": 1990
}
],
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T10:30:00Z",
"created_by": "aa1e8400-e29b-41d4-a716-446655440099",
"updated_by": "aa1e8400-e29b-41d4-a716-446655440099"
}
}
```
## Error Responses
```json Not Found theme={null}
{
"success": false,
"error": "Product not found"
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
```json Invalid ID Format theme={null}
{
"success": false,
"error": "Invalid product ID format"
}
```
## Notes
This endpoint provides direct access to a product using only the product ID.
For accessing products through their parent product, you can also use:
* `GET /api/product-parents/{id}/products/{product_id}`
# List Products
Source: https://docs.qwoty.io/api-reference/products/list
GET https://qwoty.app/api/products
Retrieve products in your workspace with pagination and filters
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Query Parameters
Number of results per page (max 200)
Page number (1-based)
Filter by external identifier. Matches against `identifiers[erp]`,
`identifiers[crm]`, and `identifiers[accounting]`.
Filter by exact API name
Filter by SKU (`inventory[sku]`)
Filter by internal reference
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/products \
-H "Authorization: Bearer qwoty_your_token"
```
```bash With Filters theme={null}
curl "https://qwoty.app/api/products?limit=30&sku=CONS-SEN-001" \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/products?limit=30', {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/products',
params={'limit': 30},
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Total number of products matching the query (across all pages)
URL to fetch the next page, or `null` if this is the last page
Array of product objects for the current page
Unique identifier (UUID)
ID of parent product (UUID)
Workspace ID (UUID)
Product name (display name combining parent + variant options)
API identifier
Internal reference
Product description
Whether description is inherited from the parent product
Whether the primary image is inherited from the parent product
Active status
Whether this is the default product under its parent
Catalogs linked to the parent product
Catalog ID (UUID)
Catalog API name
Categories linked to the parent product
Category ID (UUID)
Category API name
Product settings and configuration
Shipping information
Inventory information
External system identifiers
Accounting information
Option values for this product (e.g., color, size)
Option definition ID (UUID)
Option name (e.g., "Color")
Option API name
Option value ID (UUID)
Option value (e.g., "Blue")
Value API name
Prices associated with this product across all pricebooks
Price ID (UUID)
Pricebook ID (UUID)
API name of the pricebook
Pricing model (`flat`, `volume`, `graduated`, `percent`, `cost_based`, `none`)
Unit price. Only populated when `pricing_model` is `flat`, otherwise `null`.
UUID of the primary media for this product. Null if none is set.
UUID of the primary media of the parent product. Read-only.
```json Success Response theme={null}
{
"total": 42,
"next": "https://qwoty.app/api/products?limit=30&page=2",
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"product_parent_id": "660e8400-e29b-41d4-a716-446655440001",
"workspace_id": "880e8400-e29b-41d4-a716-446655440003",
"name": "Consulting Services - Senior",
"api_name": "consulting_services_senior",
"reference": "SERV-001",
"description": "Senior consultant hourly rate",
"description_inherit_from_product": true,
"primary_image_inherit_from_product": true,
"is_active": true,
"is_default": true,
"catalogs": [
{
"id": "770e8400-e29b-41d4-a716-446655440002",
"api_name": "catalog_main"
}
],
"categories": [],
"settings": {
"unit_per_pack": 1,
"product_type": "service",
"language_code": "en",
"unit_of_measure": "hour",
"recurrence_type": "recurring"
},
"shipping": {
"weight": null,
"weight_unit": null,
"height": null,
"length": null,
"width": null,
"length_unit": null,
"country_of_origin": null,
"harmonized_system_code": null
},
"inventory": {
"sku": "CONS-SEN-001"
},
"identifiers": {
"crm": null,
"accounting": null,
"erp": null
},
"accounting": {
"ledger_account": null
},
"options": [
{
"option_id": "opt-001",
"option_name": "Seniority",
"option_api_name": "seniority",
"value_id": "val-001",
"value_name": "Senior",
"value_api_name": "senior"
}
],
"prices": [
{
"id": "price-001",
"pricebook_id": "pb-001",
"pricebook_api_name": "standard",
"pricing_model": "flat",
"amount": 1990
}
],
"primary_image_id": null,
"parent_primary_image_id": null,
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T10:30:00Z"
}
]
}
```
## Error Responses
```json Unauthorized theme={null}
{
"error": "Invalid API token"
}
```
# Update Product
Source: https://docs.qwoty.io/api-reference/products/update
PATCH https://qwoty.app/api/products/{id}
Update an existing product
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the product to update
## Request Body
Product name
Product description
Whether to inherit description from parent product
Whether to inherit primary image from parent product
Internal reference
Active status
Category UUIDs to link to the parent product. Replaces all existing category
links.
Stock Keeping Unit
Number of units per pack
Ledger account code
External CRM ID
External ERP ID
External accounting ID
Shipping weight
Shipping weight unit (kg, lbs)
Shipping height
Shipping length
Shipping width
Shipping length unit (cm, in, etc.)
Country of origin for shipping
HS code for customs
UUID of the primary media to associate with this product. Set to `null` to
remove the primary image.
Fields such as `api_name` are creation-only: they may appear on the resource but cannot be sent in a PATCH body (the API returns `400` with a validation detail if they are).
## Examples
```bash cURL theme={null}
curl -X PATCH https://qwoty.app/api/products/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Product Name",
"description": "Updated description",
"is_active": false
}'
```
```javascript JavaScript theme={null}
const productId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(`https://qwoty.app/api/products/${productId}`, {
method: 'PATCH',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Updated Product Name',
description: 'Updated description',
is_active: false,
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
product_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.patch(
f'https://qwoty.app/api/products/{product_id}',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'name': 'Updated Product Name',
'description': 'Updated description',
'is_active': False
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The updated product object with all fields, including the `prices` array
```json Success Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"product_parent_id": "660e8400-e29b-41d4-a716-446655440001",
"workspace_id": "880e8400-e29b-41d4-a716-446655440003",
"name": "Updated Product Name",
"api_name": "updated_product_name",
"reference": "PROD-001",
"description": "Updated description",
"description_inherit_from_product": true,
"primary_image_inherit_from_product": true,
"is_active": false,
"is_default": true,
"catalogs": [
{
"id": "770e8400-e29b-41d4-a716-446655440002",
"api_name": "catalog_main"
}
],
"categories": [],
"settings": {
"unit_per_pack": 1,
"product_type": "physical",
"language_code": null,
"unit_of_measure": null,
"recurrence_type": "one_off"
},
"shipping": {
"weight": 2.8,
"weight_unit": "kg",
"length": 30,
"width": 20,
"height": 10,
"length_unit": "cm",
"country_of_origin": "FR",
"harmonized_system_code": "8471.30"
},
"inventory": {
"sku": "WID-PRE-001"
},
"identifiers": {
"crm": "crm_123",
"accounting": "acc_789",
"erp": "erp_456"
},
"accounting": {
"ledger_account": "4010"
},
"primary_image_id": null,
"parent_primary_image_id": null,
"options": [],
"prices": [
{
"id": "ee0e8400-e29b-41d4-a716-446655440030",
"pricebook_id": "ff0e8400-e29b-41d4-a716-446655440031",
"pricebook_api_name": "standard",
"pricing_model": "flat",
"amount": 1990
}
],
"created_at": "2024-12-21T10:30:00Z",
"updated_at": "2024-12-21T14:45:00Z"
}
}
```
## Error Responses
```json Not Found theme={null}
{
"success": false,
"error": "Product not found"
}
```
```json Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": {
"name": "Name must be at least 2 characters"
}
}
```
```json Validation Error (read-only field) theme={null}
{
"error": "Validation error",
"details": ["Field 'reference' is read-only or unknown"]
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Get Quote
Source: https://docs.qwoty.io/api-reference/quotes/get
GET https://qwoty.app/api/quote/{id}
Retrieve the complete quote for a specific project
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the project
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/quote/550e8400-e29b-41d4-a716-446655440000 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const id = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(`https://qwoty.app/api/quote/${id}`, {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.get(
f'https://qwoty.app/api/quote/{id}',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
The quote data wrapper
Complete quote information
Unique quote identifier (UUID)
Human-readable quote number
Quote status (e.g., 'draft', 'sent', 'accepted')
Indicates whether the quote is currently published
First recipient send date (`email_sent_timestamp` of the first sent recipient)
Quote owner information
Owner profile UUID
First name
Last name
Email address
External system identifiers: `crm`, `erp`, `accounting`
ISO 8601 expiration date
Currency code (e.g., 'EUR', 'USD')
Language code (e.g., 'en', 'fr')
Quote title
Associated CRM opportunity ID
Pre-signed URL (valid 1 hour) pointing to the generated PDF. `null` if no PDF has been generated yet for this quote (e.g. the quote has never been downloaded or signed).
URL to customer dealroom
Business unit information including brand, address, logo, and tax IDs
Customer information including company details, contacts, billing and shipping addresses
Customer UUID
Entity type: `company` or `individual`
Company or individual name
External system identifiers: `crm`, `erp`, `accounting`
List of contacts with `id`, `first_name`, `last_name`, `email`, `role`
Billing address with `line1`, `line2`, `city`, `postal_code`, `country` (full name), `country_code` (ISO 3166-1 alpha-2)
Shipping address with `line1`, `line2`, `city`, `postal_code`, `country` (full name), `country_code` (ISO 3166-1 alpha-2)
Key/value map of custom field values defined for this customer. Keys are the API names of the custom field definitions. Only present when at least one custom field has a value.
Complete pricing structure with sections, line items, and periods
Associated catalog data
Associated pricebook data
Markup configuration (enabled, percent)
Display settings (is\_prices\_hidden, etc.)
Array of quote sections with line items, organized by phases/periods
Financial summary (breaking contract)
One-off totals: `net_amount`, `tax_amount`, `total_amount`
Recurring totals by interval with `interval`, `interval_count`, and amounts
KPI metrics: `mrr`, `arr`, `tcv`
Line-level and global discounts applied
Sales tax configuration and amounts
Payment terms and installments
Accepted payment methods
Customer account balance information
Key/value map of custom field values defined on the quote. Keys are the `api_name` of each custom field definition. All fields with a defined `api_name` are included; the value may be an empty string when no value has been set.
Content blocks for quote customization (text, images, etc.)
Associated contracts with signature status
Signature recipients grouped by `signers` and `viewers`, with `signature_type` and `signing_order`
Creation/update timestamps and context metadata
```json Success Response (Simplified) theme={null}
{
"success": true,
"data": {
"quote": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"number": "Q-2024-001",
"status": "sent",
"publish": true,
"issue_date": "2026-03-10T09:15:00.000Z",
"owner": {
"id": "2f4c94f0-a27d-45e3-8b5e-b4ad0c9f57c9",
"first_name": "Paul",
"last_name": "Martin",
"email": "paul@acme.com",
"external_ids": {
"crm": "CRM-7654321",
"erp": "ERP-7654321",
"accounting": "ACC-7654321"
}
},
"expiration_date": "2024-12-31T23:59:59Z",
"currency": "EUR",
"language": "en",
"title": "Q4 2024 Services Proposal",
"opportunity_id": "opp_123456",
"pdf_link": "https://storage.qwoty.io/project/path/to/quote.pdf?token=...&expires=1744233600",
"dealroom_link": "https://qwoty.app/dealroom/abc123",
"business_unit": {
"id": "bu_123",
"type": "company",
"brand": "Acme",
"name": "Acme Corporation",
"logo": {
"url": "https://storage.qwoty.io/logos/acme.png",
"alt": "Acme Logo"
},
"tax_ids": [
{
"code": "VAT",
"value": "FR12345678901"
}
],
"address": {
"line1": "123 Main Street",
"city": "Paris",
"postal_code": "75001",
"country": "France",
"country_code": "FR"
}
},
"customer": {
"id": "cust_456",
"type": "company",
"name": "Customer Inc.",
"external_ids": {
"crm": "CRM-1234567",
"erp": null,
"accounting": "ACC-1234567"
},
"contacts": [
{
"id": "contact_789",
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@customer.com",
"role": "decision_maker"
}
],
"billing_address": {
"line1": "456 Customer Ave",
"line2": null,
"city": "Lyon",
"postal_code": "69001",
"country": "France",
"country_code": "FR"
},
"shipping_address": null,
"custom_properties": {
"industry": "SaaS",
"account_manager": "Sophie Dubois"
}
},
"pricing_table": {
"settings": {
"is_prices_hidden": false
},
"sections": [
{
"id": "section_001",
"type": "standard",
"rank": 1,
"name": "Professional Services",
"phases": [
{
"id": "period_001",
"name": "Year 1",
"rank": 1,
"line_items": [
{
"id": "item_001",
"rank": 1,
"product": {
"product_id": "prod_123",
"name": "Consulting Services",
"unit_of_measure": "hour"
},
"quantity": 100,
"pricing": {
"model": "per_unit",
"list_price": 150.0,
"currency": "EUR"
}
}
]
}
]
}
]
},
"summary": {
"one_off": {
"net_amount": 50000.0,
"tax_amount": 10000.0,
"total_amount": 60000.0
},
"recurring": [
{
"interval": "month",
"interval_count": 1,
"net_amount": 5000.0,
"tax_amount": 1000.0,
"total_amount": 6000.0
}
],
"metrics": {
"mrr": 5000.0,
"arr": 60000.0,
"tcv": 180000.0
}
},
"recipients": {
"signature_type": "restricted",
"signing_order": "parallel",
"signers": [
{
"rank": 1,
"name": "Michel Lee",
"email": "michel.lee@external.com",
"type": "external",
"status": "not_viewed"
}
],
"viewers": [
{
"name": "Emmanuel Laureau",
"email": "emmanuel.laureau@company.com",
"user_id": "usr_001",
"type": "internal",
"status": "not_viewed"
}
]
},
"custom_properties": {
"sales_channel": "inbound",
"contract_duration": "12"
},
"metadata": {
"created_at": "2024-12-01T10:00:00Z",
"updated_at": "2024-12-15T14:30:00Z",
"template_id": "tmpl_123",
"workspace_id": "ws_123",
"organisation_id": "org_123"
}
}
}
}
```
## Error Responses
```json Not Found theme={null}
{
"success": false,
"error": "Project not found"
}
```
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
```json Quote Not Generated theme={null}
{
"success": false,
"error": "Quote has not been generated for this project"
}
```
## Notes
The quote endpoint returns a comprehensive structure including:
* **Seller & Customer**: Complete company and contact information, including custom field values (`custom_properties`)
* **Pricing Table**: All products/services organized by sections and time periods
* **Summary**: One-off totals, recurring totals by interval, and metrics (`mrr`, `arr`, `tcv`)
* **Discounts & Taxes**: Applied discounts and tax calculations
* **Content & Contracts**: Customizable content blocks and signature-ready contracts
* **Recipients**: Signature recipients grouped by signers/viewers
This endpoint is typically used to:
* Display quotes in customer dealrooms
* Generate PDF documents
* Integrate with CRM systems
* Track quote metrics and performance
# Get Quote PDF
Source: https://docs.qwoty.io/api-reference/quotes/get-pdf
GET https://qwoty.app/api/projects/{id}/pdf
Download a quote PDF (signed when available, otherwise unsigned)
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
UUID of the project
## Query Parameters
Force PDF regeneration before download. Use `true` to bypass freshness checks.
## Behavior
* If the project is `accepted` and a signed PDF exists, the signed file is returned.
* Otherwise, an unsigned PDF is returned (generated on demand if needed).
* The response is a file download with `application/pdf` content type.
**Source:** `src/app/api/projects/[id]/pdf/route.ts` (kept outside `app/api/(bff)/` as a documented, tool-friendly HTTP contract).
## Examples
```bash cURL theme={null}
curl --request GET \
--url "https://qwoty.app/api/projects/550e8400-e29b-41d4-a716-446655440000/pdf?force=false" \
--header "Authorization: Bearer qwoty_your_token" \
--output quote.pdf
```
```javascript JavaScript theme={null}
const projectId = '550e8400-e29b-41d4-a716-446655440000'
const response = await fetch(
`https://qwoty.app/api/projects/${projectId}/pdf?force=false`,
{
headers: {
Authorization: 'Bearer qwoty_your_token',
},
},
)
if (!response.ok) {
const error = await response.json()
throw new Error(error.error)
}
const blob = await response.blob()
```
```python Python theme={null}
import requests
project_id = '550e8400-e29b-41d4-a716-446655440000'
response = requests.get(
f'https://qwoty.app/api/projects/{project_id}/pdf?force=false',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
if response.status_code != 200:
raise Exception(response.json().get('error', 'Request failed'))
with open('quote.pdf', 'wb') as f:
f.write(response.content)
```
## Response
Always `application/pdf` on success.
Attachment filename. Example: `quote-Q-2024-001.pdf` or
`quote-Q-2024-001-signed.pdf`.
```text 200 application/pdf theme={null}
(binary PDF file)
```
```json 404 theme={null}
{
"error": "Project not found"
}
```
```json 500 theme={null}
{
"error": "Failed to prepare PDF"
}
```
# List Quotes
Source: https://docs.qwoty.io/api-reference/quotes/list
GET https://qwoty.app/api/quote
Retrieve a list of quotes in your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/quote \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/quote', {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/quote',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates if the request was successful
Array of quote summaries
Unique quote ID (project UUID)
Human-readable quote number
Quote title
Quote status
Last update timestamp (ISO 8601)
```json Success Response theme={null}
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"project_number": "Q-2026-001",
"title": "Enterprise Plan",
"status": "draft",
"updated_at": "2026-03-09T10:30:00Z"
}
]
}
```
## Error Responses
```json Unauthorized theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
# Create API Token
Source: https://docs.qwoty.io/api-reference/tokens/create
POST https://qwoty.app/api/tokens
Create a new API token for authentication
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Request Body
A descriptive name for the API token
Optional expiration date in ISO 8601 format (e.g., "2024-12-31T23:59:59Z")
Optional permissions configuration for the token
## Response
Indicates if the operation was successful
The created API token object
Unique identifier for the token
Name of the token
The actual token value (only returned on creation)
Expiration date of the token
Creation timestamp
```bash cURL theme={null}
curl --request POST \
--url https://qwoty.app/api/tokens \
--header 'Content-Type: application/json' \
--data '{
"name": "Production API Token",
"expires_at": "2025-12-31T23:59:59Z"
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/tokens', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Production API Token',
expires_at: '2025-12-31T23:59:59Z',
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.post(
'https://qwoty.app/api/tokens',
json={
'name': 'Production API Token',
'expires_at': '2025-12-31T23:59:59Z'
}
)
data = response.json()
```
```json 200 theme={null}
{
"success": true,
"data": {
"id": "tok_123abc",
"name": "Production API Token",
"token": "qwoty_live_abc123def456...",
"expires_at": "2025-12-31T23:59:59Z",
"created_at": "2024-01-15T10:30:00Z"
}
}
```
```json 400 theme={null}
{
"error": "Invalid request data",
"details": {
"name": ["Required field"]
}
}
```
# Delete API Token
Source: https://docs.qwoty.io/api-reference/tokens/delete
DELETE https://qwoty.app/api/tokens/{id}
Delete an API token
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
The unique identifier of the API token to delete
## Response
Indicates if the operation was successful
Confirmation message
```bash cURL theme={null}
curl --request DELETE \
--url https://qwoty.app/api/tokens/tok_123abc
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/tokens/tok_123abc', {
method: 'DELETE',
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.delete('https://qwoty.app/api/tokens/tok_123abc')
data = response.json()
```
```json 200 theme={null}
{
"success": true,
"message": "API token deleted successfully"
}
```
```json 400 theme={null}
{
"error": "Token ID is required"
}
```
```json 404 theme={null}
{
"error": "Token not found"
}
```
Once deleted, an API token cannot be recovered. Any applications using this
token will immediately lose access.
# List API Tokens
Source: https://docs.qwoty.io/api-reference/tokens/list
GET https://qwoty.app/api/tokens
Retrieve all API tokens for your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Query Parameters
This endpoint does not require any parameters.
## Response
Indicates if the operation was successful
Array of API token objects
Unique identifier for the token
Name of the token
Expiration date of the token
Creation timestamp
Last usage timestamp
```bash cURL theme={null}
curl --request GET \
--url https://qwoty.app/api/tokens
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/tokens')
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get('https://qwoty.app/api/tokens')
data = response.json()
```
```json 200 theme={null}
{
"success": true,
"data": [
{
"id": "tok_123abc",
"name": "Production API Token",
"expires_at": "2025-12-31T23:59:59Z",
"created_at": "2024-01-15T10:30:00Z",
"last_used_at": "2024-01-20T14:22:00Z"
},
{
"id": "tok_456def",
"name": "Development Token",
"expires_at": null,
"created_at": "2024-01-10T08:15:00Z",
"last_used_at": "2024-01-19T16:45:00Z"
}
]
}
```
```json 400 theme={null}
{
"error": "Failed to retrieve tokens"
}
```
# Authentication
Source: https://docs.qwoty.io/developers/authentication
Learn how to authenticate with the Qwoty API
## API Tokens
Qwoty uses API tokens to authenticate requests. You can generate and manage API tokens from your dashboard under **Settings** → **Developer** → **API Tokens**.
## Authentication Header
Include your API token in the `Authorization` header of each request:
```bash theme={null}
Authorization: Bearer qwoty_your_api_token_here
```
## Using the API Playground
To test endpoints in the documentation:
1. **Look for the Authorize button** (🔒) in the top-right corner of any API endpoint page
2. Click it and paste your token (with or without "Bearer " prefix)
3. The token will be automatically included in all subsequent requests
**Alternative method:**
If the Authorize button is not visible, you can manually add the header in the playground:
* Open any endpoint
* Find the "Headers" section
* Add: `Authorization: Bearer qwoty_your_token`
## Complete Example
```bash theme={null}
curl https://qwoty.app/api/payment-terms \
-H "Authorization: Bearer qwoty_your_api_token_here"
```
Never share your API token in publicly accessible areas such as GitHub,
client-side code, or public forums.
## Token Format
API tokens follow this format:
* Prefix: `qwt_prod_`
* Length: Variable, typically 64-128 characters
* Example: `qwt_prod_e75fc200cd7ea8da9bebf...`
## Workspace Scope
Each API token is scoped to a specific workspace. All API operations will:
* Only access data within that workspace
* Respect the token's permissions
* Enforce workspace-level rate limits
## Security Best Practices
* Use environment variables - Never commit tokens to version control -
Rotate tokens regularly - Use different tokens for different environments
(dev, staging, production)
Each token has specific permissions. Create tokens with minimal required
permissions for each use case.
If a token is compromised, immediately revoke it from your dashboard and
generate a new one.
## Error Codes
### Authentication Errors
| Status Code | Error | Description |
| ----------- | ------------ | ------------------------------------------------ |
| `401` | Unauthorized | Missing or invalid API token |
| `403` | Forbidden | Token doesn't have permission for this operation |
### Example Error Response
```json theme={null}
{
"success": false,
"error": "Invalid API token"
}
```
## Need Help?
Contact our support team at [support@qwoty.io](mailto:support@qwoty.io) for any questions about authentication.
# Certifications & Regulatory Compliance
Source: https://docs.qwoty.io/developers/compliance/certifications-and-regulatory-compliance
An overview of Qwoty's compliance certifications and regulatory standards.
## Compliance Status Overview
| Certification | Status |
| ------------- | ------- |
| SOC 2 | Planned |
| ISO 27001 | Planned |
***
## SOC 2
**Status:** Planned
SOC 2 is a framework for managing and auditing the security, availability, processing integrity, confidentiality, and data privacy in cloud and IT service organizations, established by the American Institute of Certified Public Accountants (AICPA).
***
## ISO 27001
**Status:** Planned
ISO 27001 is an international standard for managing information security, specifying requirements for establishing, implementing, maintaining, and continually improving an information security management system (ISMS).
Qwoty already implements many ISO 27001-aligned controls — including role-based access control, AES-256 encryption at rest, TLS 1.2+ in transit, and 24/7 security monitoring. Formal certification is on the roadmap.
***
## See Also
* [E-Sign Compliance](/developers/compliance/e-sign-compliance) — ESIGN Act, UETA, and eIDAS signature compliance
* [Signature Levels](/developers/compliance/signature-levels) — eIDAS and other signature level compliance
# E-Sign Compliance
Source: https://docs.qwoty.io/developers/compliance/e-sign-compliance
How Qwoty supports legally valid electronic signatures across major jurisdictions.
## ESIGN Act (United States)
The Electronic Signatures in Global and National Commerce Act (ESIGN Act) is a U.S. federal law enacted in 2000. It ensures that electronic signatures and records have the same legal validity as paper documents and handwritten signatures in interstate and foreign commerce.
### Key Requirements
| Requirement | Description |
| ----------------------- | ----------------------------------------------------------------------------------------- |
| **Intent to Sign** | Signers must demonstrate clear intent to sign the document |
| **Consent** | All parties must agree to conduct the transaction electronically |
| **Consumer Disclosure** | For consumer transactions, specific disclosures must be provided before obtaining consent |
| **Record Retention** | Electronic records must be accurately preserved and accessible for later reference |
| **Association** | The signature must be associated with the record being signed |
### Exclusions
The ESIGN Act does not apply to certain document types, including:
* Wills, codicils, and testamentary trusts
* Family law documents (adoption, divorce)
* Court orders and official court documents
* Cancellation of utility services
* Documents related to hazardous materials transportation
***
## UETA (United States)
The Uniform Electronic Transactions Act (UETA) is a model law adopted by 49 U.S. states (all except New York, which has its own Electronic Signatures and Records Act). UETA provides a legal framework for electronic signatures and records at the state level.
### Relationship to ESIGN
UETA and the ESIGN Act have similar requirements and purposes. The federal ESIGN Act allows states to modify or supersede certain ESIGN provisions if they adopt UETA or an equivalent law. In practice, the requirements for electronic signatures under both laws align closely.
### Key Requirements
* Intent to sign demonstrated by the signer
* Consent to conduct transactions electronically
* Retention of records in their original electronic form
* Attribution of the signature to the signer
***
## eIDAS (European Union)
The Electronic Identification, Authentication and Trust Services (eIDAS) regulation governs electronic signatures across all EU member states. eIDAS establishes three levels of electronic signatures, each with different requirements and legal effects.
### Signature Levels
| Level | Description | Legal Effect |
| ------------------- | -------------------------------------------------------------------------------------- | -------------------------------------------------- |
| **Simple (SES)** | Basic electronic signature with no specific technical requirements | Admissible as evidence; legal effect varies by use |
| **Advanced (AES)** | Uniquely linked to signer, capable of identifying signer, under sole control | Higher evidentiary weight than SES |
| **Qualified (QES)** | AES created by a qualified signature creation device, based on a qualified certificate | Equivalent to handwritten signature across the EU |
#### Simple Electronic Signatures (SES)
SES is the baseline level. Any data in electronic form attached to or logically associated with other electronic data, used by the signatory to sign, qualifies as an SES. There are no specific technical requirements beyond demonstrating intent to sign.
#### Advanced Electronic Signatures (AES)
AES must meet additional criteria:
* Uniquely linked to the signatory
* Capable of identifying the signatory
* Created using signature creation data under the signatory's sole control
* Linked to the signed data in a way that detects subsequent changes
#### Qualified Electronic Signatures (QES)
QES requires:
* A qualified certificate issued by a qualified trust service provider
* Creation using a qualified electronic signature creation device
* Identity verification compliant with eIDAS requirements
QES carries the same legal standing as a handwritten signature in all EU member states.
***
## Other Jurisdictions
Electronic signature laws exist in most countries. Below are selected examples:
| Jurisdiction | Framework | Notes |
| ------------------ | --------------------------------------------- | ------------------------------------------------------------ |
| **United Kingdom** | UK eIDAS / Electronic Communications Act 2000 | Post-Brexit, UK maintains eIDAS-like framework |
| **Canada** | PIPEDA, provincial laws | Federal and provincial laws govern e-signatures |
| **Australia** | Electronic Transactions Act 1999 | Generally technology-neutral approach |
| **Switzerland** | ZertES | Swiss federal law with qualified signature requirements |
| **Brazil** | MP 2200-2, ICP-Brasil | PKI-based framework for digital signatures |
| **India** | IT Act 2000, Aadhaar e-KYC | Recognizes electronic signatures; Aadhaar-based verification |
| **China** | Electronic Signature Law | Requires reliable electronic signatures for certain uses |
| **Japan** | Electronic Signatures Act | Three-tier system similar to eIDAS |
Requirements vary significantly by jurisdiction. Some transactions may require specific signature types or have exclusions similar to the ESIGN Act.
***
## How Qwoty Supports Compliance
Qwoty provides features that support compliance with e-signature laws across jurisdictions.
### Intent to Sign
* Signers must actively interact with signature fields to apply their signature
* The signing interface clearly indicates the document being signed
* Signers receive a copy of the completed document
### Consent
* Recipients receive clear notification that they are being asked to sign electronically
* The signing process requires affirmative action from the signer
### Record Retention
* Signed documents are stored and accessible to all parties
* Original documents and audit trails are preserved
* Documents can be downloaded in their signed form at any time
### Document Integrity
* All completed documents are cryptographically sealed
* Any modification after signing invalidates the digital signature
* PDF readers can verify the document has not been altered
### Signer Identification
* Email-based delivery establishes signer identity
* Optional access codes add an extra layer of verification
* Signing activity is logged with timestamps and metadata
***
## Audit Trails
Qwoty maintains a complete audit trail for each document, recording:
| Event | Recorded Data |
| ------------------ | -------------------------------------- |
| Document creation | Timestamp, creator identity |
| Recipient addition | Recipient details, assigned fields |
| Document sent | Timestamp, delivery method |
| Document viewed | Timestamp, viewer identity, IP address |
| Field completed | Timestamp, field type, signer identity |
| Document completed | Timestamp, final document hash |
The audit trail provides evidence of the signing process — who signed, when they signed, and the full sequence of events — supporting the legal enforceability of the signed document.
***
## What Qwoty Does NOT Provide
Qwoty supports compliance with Simple Electronic Signature (SES) requirements. The following are not currently provided:
| Capability | Status |
| ----------------------------------------- | ------------------------------------------------------------------------------------- |
| **Qualified Electronic Signatures (QES)** | Not supported; requires integration with qualified trust service providers |
| **Advanced Electronic Signatures (AES)** | Partial support; full AES requires identity verification services |
| **Identity Verification (KYC)** | Not built-in; optional integrations may be available |
| **Qualified Certificates** | Not issued; would require becoming a qualified trust service provider |
| **Industry-Specific Compliance** | Features for specific regulations (e.g., healthcare, finance) depend on configuration |
For transactions requiring AES or QES, consult with legal counsel about appropriate solutions.
***
This page provides general information about electronic signature laws for educational purposes only. It does not constitute legal advice.
Electronic signature requirements vary by jurisdiction, transaction type, and specific circumstances. Some documents may have specific legal requirements that electronic signatures cannot satisfy.
Consult qualified legal counsel in your jurisdiction to determine whether electronic signatures are appropriate for your specific use case and what requirements must be met.
***
# GDPR
Source: https://docs.qwoty.io/developers/compliance/gdpr
How Qwoty handles personal data and supports your GDPR compliance obligations.
## Qwoty's Role
When using Qwoty, two distinct data processing roles apply:
| Role | Description |
| ------------------- | --------------------------------------------------------------------------------- |
| **Data Controller** | You (the organisation using Qwoty) determine the purposes and means of processing |
| **Data Processor** | Qwoty processes personal data on your behalf according to your instructions |
As the **data controller**, you are responsible for:
* Obtaining appropriate consent or legal basis for processing
* Informing data subjects about how their data is used
* Responding to data subject access requests
* Ensuring compliance with GDPR requirements
As the **data processor**, Qwoty:
* Processes data only according to your documented instructions
* Implements appropriate technical and organisational security measures
* Assists with data subject requests when needed
* Maintains records of processing activities
***
## Data Processing
Qwoty processes personal data necessary to provide its services:
| Data Category | Examples | Purpose |
| ------------------ | ---------------------------------------------- | --------------------------------------- |
| **Identity Data** | Name, email address | User accounts, recipient identification |
| **Document Data** | Uploaded PDFs, field values | Document storage and signing |
| **Signature Data** | Signature images, signing timestamps | Recording signing actions |
| **Audit Data** | IP addresses, browser information, action logs | Audit trail and verification |
Data is processed for the following purposes:
* Delivering documents to recipients
* Recording signatures and other recipient actions
* Generating signed documents with audit trails
* Sending email notifications and transactional communications
***
## Data Storage Locations
Qwoty primarily stores data within the European Economic Area (EEA). Application data and document storage use AWS infrastructure in the EU (Ireland and Paris). Backups are maintained in geographically separate EU locations.
***
## Data Subject Rights
GDPR grants individuals specific rights regarding their personal data. As the data controller, you are responsible for fulfilling these requests:
| Right | Description |
| ----------------- | -------------------------------------------------------------------------- |
| **Access** | Data subjects can request a copy of their personal data |
| **Rectification** | Data subjects can request correction of inaccurate data |
| **Erasure** | Data subjects can request deletion of their data ("right to be forgotten") |
| **Portability** | Data subjects can request their data in a machine-readable format |
| **Restriction** | Data subjects can request limited processing of their data |
| **Objection** | Data subjects can object to certain types of processing |
Qwoty will assist with data subject requests where technically feasible. Up to five (5) requests per calendar month, or those requiring fewer than two (2) hours of effort, are included at no additional charge. Beyond this threshold, assistance is billed at Qwoty's then-current professional services rate.
***
## Data Deletion
Qwoty supports data deletion to help fulfil erasure requests:
* Users can delete their own accounts
* Account deletion removes profile data and authentication credentials
* Administrators can remove members from the organisation
* Document owners can delete documents at any stage
* Deletion removes the document, recipient data, and associated audit logs
Upon termination of the agreement, Qwoty will:
* Delete all personal data from primary systems within **10 business days**
* Delete data from backup systems within **90 days**, in line with the standard backup retention cycle
Customers have **30 days** from the termination date to export their data from the platform before deletion proceeds.
Consult with legal counsel to establish appropriate retention policies, particularly for signed contracts where legal obligations may require you to retain records independently.
***
## Data Processing Agreement
A Data Processing Agreement (DPA) is required by GDPR when a data controller engages a data processor. Qwoty's DPA is incorporated directly into the General Terms and Conditions — accepting the GTC constitutes acceptance of the DPA.
The DPA covers:
* Qwoty's obligations as a data processor
* Sub-processor authorisation and change notification (30-day advance notice)
* Technical and organisational security measures (Annex 2)
* International data transfer mechanisms
* A DPA is available upon request [support@qwoty.io](mailto:contact@qwoty.io).
***
This page is provided for informational purposes only and does not constitute legal advice. GDPR compliance depends on your specific circumstances, including how you use Qwoty, what data you process, and your organisation's obligations. Consult with qualified legal counsel to determine your GDPR obligations, draft appropriate privacy notices, establish lawful bases for processing, and implement compliant data handling procedures.
***
# Signature levels
Source: https://docs.qwoty.io/developers/compliance/signature-levels
Understand the different levels of electronic signatures supported under compliance standards like eIDAS.
## Signature Levels
Qwoty supports multiple levels of electronic signatures based on compliance frameworks such as the EU’s eIDAS regulation. Each level offers different technical requirements and legal recognition.
Qwoty seals all signed documents cryptographically, regardless of signature level, to prevent any alterations after signing.
## **🇺🇸 ESIGN (Electronic Signatures in Global and National Commerce) Act**
**Compliant**
The Electronic Signatures in Global and National Commerce Act (ESIGN Act) is a U.S. federal law that ensures the legal validity and enforceability of electronic signatures and records in commerce.
### **Main Requirements**
* **Intent to Sign**: “Parties must demonstrate their intent to sign \[..]”
* **Consent**: “The ESIGN Act requires that all parties involved in a transaction consent to the use of electronic signatures and records \[..]”
* **Consumer Disclosures**: Before obtaining their consent, financial institutions must provide the consumer a clear and conspicuous statement informing the consumer \[..]
* **Record Retention**: Electronic Records must be maintained for later access by signers.
* **Security**: The ESIGN Act does not mandate specific security measures, but it does require that parties take reasonable steps to ensure the security and integrity of electronic signatures and records. This may include implementing encryption, access controls, and authentication measures.
## **UETA (Uniform Electronic Transactions Act)**
**Compliant**
The Uniform Electronic Transactions Act is a law that provides a legal framework for the use of electronic signatures and records in electronic transactions, ensuring they have the same validity and enforceability as paper documents and handwritten signatures.
### **Main Requirements**
See *ESIGN*
## **🇪🇺 eIDAS**
Status: **Compliant for Level 1 - SES (Simple Electronic Signatures)**
eIDAS (Electronic Identification, Authentication and Trust Services) is an EU regulation that standardizes electronic identification and trust services for secure and seamless electronic transactions across European member states.
### **Level 1 - SES (Simple Electronic Signatures)**
eIDAS SES (Simple Electronic Signature) is a basic electronic signature with minimal security features.
### **Main Requirements**
* Visual Signature
* Clear Intent to Sign
### **Level 2 - AES (Advanced Electronic Signatures)**
Status: Planned
eIDAS AES (Advanced Electronic Signature) provides a higher level of security with unique identification of the signer and data integrity.
### **Main Requirements**
* Cryptographic Signature Sealing the Document against tampering
* Signing Using Dedicated Hardware (Hardware Security Module)
* Embedding Signer Identity in the Cryptographic Signature
* Being a Government Audited Trusted Qualified Services Provider
### **Level 3 - QES (Qualified Electronic Signatures)**
Status: Planned
eIDAS QES (Qualified Electronic Signature) is the highest security level, legally equivalent to a handwritten signature within the EU.
### **Main Requirements**
* Cryptographic Signature Sealing the Document against tampering
* Signing using dedicated hardware (Hardware Security Module)
* Embedding Signer Identity in the Cryptographic Signature
* Being a government-trusted qualified services provider
* eIDAS-compliant identification before signing using local passports or similar
## **🇨🇭 ZertES**
Status: Planned
ZertES is a Swiss Federal law that regulates electronic signature compliance.
# Build with LLMs
Source: https://docs.qwoty.io/developers/core-concepts/build-with-llms
Use Qwoty documentation and APIs with AI assistants and LLMs.
You can use large language models (LLMs) to assist in building Qwoty integrations. This page explains how to access our documentation in LLM-friendly formats and how to connect AI assistants directly to your Qwoty account.
## Using documentation with LLMs
### llms.txt
The [llms.txt file](https://llmstxt.org) is an industry standard that helps LLMs index content efficiently, similar to how a sitemap helps search engines. AI tools use this file to understand your documentation structure and find relevant content.
We automatically host an `llms.txt` file that lists all available pages: [docs.qwoty.io/llms.txt](https://docs.qwoty.io/llms.txt)
### skill.md
The [skill.md specification](https://agentskills.io/specification) is a structured, machine-readable format that describes what AI agents can accomplish with Qwoty. While `llms.txt` tells agents where to find information, `skill.md` tells them what capabilities are available, what inputs are required, and what constraints apply.
We automatically host a `skill.md` file: [docs.qwoty.io/skill.md](https://docs.qwoty.io/skill.md)
Agents can process this file using the [skills CLI](https://www.npmjs.com/package/skills):
```bash theme={null}
npx skills add docs.qwoty.io/skill.md
```
### Documentation MCP Server
The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) is an open standard that allows AI assistants to connect directly to external services. When connected to our documentation MCP server, AI tools can search our docs during response generation — providing more accurate answers than generic web searches.
Connect your AI tools to our documentation MCP server at `https://docs.qwoty.io/mcp`:
1. Navigate to the [Connectors](https://claude.ai/settings/connectors) page in the Claude settings
2. Select **Add custom connector**
3. Add the server name (e.g., `qwoty-docs`) and URL: `https://docs.qwoty.io/mcp`
4. Select **Add**
5. In a conversation, open the search and tools menu and select the **qwoty-docs** connector to activate it
Run the following command to add the Qwoty Docs MCP server:
```bash theme={null}
claude mcp add --transport http qwoty-docs https://docs.qwoty.io/mcp
```
1. Use `Command` + `Shift` + `P` (`Ctrl` + `Shift` + `P` on Windows) to open the command palette
2. Search for "Open MCP settings"
3. Select **Add custom MCP** to open the `mcp.json` file
4. Configure the server:
```json theme={null}
{
"mcpServers": {
"qwoty-docs": {
"url": "https://docs.qwoty.io/mcp"
}
}
}
```
1. Create a `.vscode/mcp.json` file in your project
2. Configure the server:
```json theme={null}
{
"servers": {
"qwoty-docs": {
"type": "http",
"url": "https://docs.qwoty.io/mcp"
}
}
}
```
# Error handling
Source: https://docs.qwoty.io/developers/core-concepts/error-handling
Learn how to handle errors when using Qwoty’s API.
The Qwoty API uses conventional HTTP response codes to indicate the success or failure of an API request. This page details how to handle errors effectively and what to expect in error responses.
## HTTP Status Codes
The API uses standard HTTP status codes to indicate the success or failure of requests:
* **2xx Success:** The request was successful
* **4xx Client Error:** The request was invalid or cannot be processed
* **5xx Server Error:** An internal server error occurred
## Error Response Format
When an error occurs, the API returns a JSON response with a `message` property containing a human-readable description of the error:
```json theme={null}
{
"message": "The requested resource was not found"
}
```
## Common Error Codes
### 400 Bad Request
The request was malformed or contains invalid parameters.
Example:
```json theme={null}
{
"message": "Cannot assign a subscription to a customer without a currency set"
}
```
### 401 Unauthorized
The request lacks valid authentication credentials.
Example:
```json theme={null}
{
"message": "Missing authentication token"
}
```
### 404 Not Found
The requested resource was not found.
Example:
```json theme={null}
{
"message": "Product not found"
}
```
### 429 Too Many Requests
The request rate limit has been exceeded. See **Rate limiting** for more details.
## Getting Help
If you encounter an error that you cannot resolve, please:
1. Check the error message for guidance
2. Verify your request format and parameters
3. Ensure your authentication credentials are valid
4. Contact our support team with the error details and request
# Rate limiting
Source: https://docs.qwoty.io/developers/core-concepts/rate-limiting
Learn Qwoty’s API rate limits
Our API implements rate limiting to ensure fair usage and protect the system from abuse. This page details our rate limiting policies and how to handle them effectively.
## Rate Limits
| Operation Type | Limit | Window |
| -------------------------------- | ----: | ---------: |
| Read (GET) | 200 | 10 seconds |
| Write (POST, PUT, PATCH, DELETE) | 50 | 10 seconds |
## Rate Limit Headers
API responses include the following rate limit headers:
* `X-RateLimit-Limit`: The maximum number of requests allowed in the current window
* `X-RateLimit-Remaining`: The number of requests remaining in the current window
* `X-RateLimit-Reset`: The date and time when the rate limit window resets
* `Retry-After`: The minimum number of seconds to wait before retrying (only when the limit has been reached)
## Handling Rate Limits
When you exceed the rate limit, the API will return a **429 Too Many Requests** HTTP status code. The response body will be:
```text theme={null}
Too many requests
```
## Best Practices
1. **Monitor Rate Limits:** Track the `X-RateLimit-Remaining` header to anticipate when you’re approaching the limit.
2. **Implement Backoff:** When you receive a 429 response:
* Wait until the date specified in the `X-RateLimit-Reset` header.
* Consider implementing exponential backoff for retries.
3. **Batch Requests:** When possible, combine multiple operations into a single request.
4. **Caching:** Cache responses when appropriate to reduce the number of API calls.
## Example Response Headers
### Rate limit not exceeded
```http theme={null}
HTTP/1.1 200 OK
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 150
X-RateLimit-Reset: 2025-04-15T10:18:08.107Z
```
### Rate limit exceeded
```http theme={null}
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 200
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 2025-04-15T10:18:08.107Z
Retry-After: 5
```
## Custom Rate Limits
If your application requires higher rate limits, please contact our support team. We’ll work with you to establish appropriate limits based on your use case.
# Webhooks
Source: https://docs.qwoty.io/developers/core-concepts/webhooks
Learn how Qwoty’s webhooks work, how to receive events, and best practices for consuming them.
Webhooks are automated messages sent from Qwoty when something happens in our system. They have a specific payload for each action they notify and are sent as HTTP POST requests to an HTTPS endpoint on your server. Webhooks help you integrate your application with Qwoty and react to events in real time.
## Add a Webhook Endpoint
To start listening to webhook messages sent by Qwoty, go to your webhook settings in the dashboard, click **Add Webhook**, provide a public HTTPS URL you control, and select the event types you want to receive. You can add multiple endpoints if you want to segregate events by destination.
## Event Types
Qwoty supports a variety of webhook event types for entities such as customers, quotes and more. When an event occurs, Qwoty sends a webhook POST to your endpoint with a JSON payload containing:
```json theme={null}
{
"event": "quote.update",
"workspace_id": "13324049",
"associated_object_id": "44",
"associated_object_type": "quote"
}
```
## Consuming Webhooks
Webhook deliveries are always HTTPS POST requests containing a JSON body. Your endpoint should:
1. **Quickly return a 2xx success** status before performing complex logic to avoid timeouts.
2. **Verify webhook signatures and timestamps** to ensure authenticity and guard against replay attacks.
### Best Practices
* **Idempotent processing:** Guard against duplicate events by ensuring your handler logic can safely process the same event more than once.
* **Fetch latest entity state:** Since events might be retried with outdated data, consider fetching the latest state of the related object after receiving a webhook.
* **Static IP whitelisting:** If your webhook endpoint is behind a firewall, allow traffic from Qwoty’s webhook server IPs.
## Managing Webhooks
Qwoty supports API operations to:
* Create webhook endpoints
* List webhook endpoints
* Retrieve a specific webhook endpoint
* Update webhook endpoints
* Delete webhook endpoints
## Event Delivery and Retry Schedule
When Qwoty attempts to deliver webhook events:
* If your endpoint responds with a non-2xx status, Qwoty will retry delivery on a schedule (e.g., seconds, minutes, hours apart).
* Event delivery is **not guaranteed in strict order**, so design your processing logic accordingly.
# Pennylane
Source: https://docs.qwoty.io/developers/integrations/billing/pennylane
Synchronisez vos devis Qwoty vers Pennylane — factures, devis et abonnements créés automatiquement à la signature.
## Ce que fait cette intégration
À chaque signature d'un devis Qwoty, l'intégration Pennylane crée automatiquement les documents comptables correspondants dans votre espace Pennylane :
* **Devis one-off** → une ou plusieurs factures (selon vos conditions de paiement) ou un devis
* **Abonnements** → un abonnement récurrent par phase tarifaire
* **Client inconnu** → le client est créé automatiquement dans Pennylane si besoin
***
## Prérequis
### Créer une clé API Pennylane
Rendez-vous dans **Paramètres → API** dans votre espace Pennylane et créez une clé en mode **Read and write** avec les scopes suivants :
* `Manage billing subscriptions`
* `Manage customer invoices`
* `Manage clients`
* `Manage quotes`
* `Customer mandates`
Consultez la [documentation Pennylane](https://pennylane.readme.io/docs/generating-my-api-token) pour le détail des étapes de création de clé API.
### Configurer un premier numéro de document
Avant d'utiliser l'intégration, Pennylane requiert qu'un **premier numéro de facture et de devis** soit défini dans votre espace — sans quoi la création de documents échoue. Configurez-les dans **Paramètres → Numérotation** avant de lancer votre premier sync.
Cette étape est requise aussi bien sur la sandbox que sur votre environnement live. Sans numérotation configurée, l'intégration retournera une erreur à la création du premier document.
### Tester sur la sandbox
Pennylane met à disposition un environnement de sandbox pour tester l'intégration sans impacter vos données de production. Les tests sur sandbox sont **recommandés** avant mise en production.
Pensez à configurer la numérotation sur votre sandbox également, et à utiliser une clé API sandbox distincte de votre clé de production.
***
## Configuration
### Documents
Détermine le type de document créé dans Pennylane pour vos devis **one-off** (produits sans abonnement).
| Valeur | Comportement |
| ----------- | ------------------------------------------------------------------- |
| **Invoice** | Crée une ou plusieurs **factures** selon vos conditions de paiement |
| **Quote** | Crée un **devis** dans Pennylane |
Ce paramètre n'a pas d'effet sur les sections **abonnement** — celles-ci créent toujours un `billing_subscription`.
***
### Factures
Définit si les factures sont créées en **brouillon** ou **finalisées**.
| Valeur | Comportement |
| ------- | ------------------------------------------------------------------------------ |
| **Yes** | Facture créée en brouillon — vous devez la valider manuellement dans Pennylane |
| **No** | Facture finalisée immédiatement à la création |
Une facture finalisée ne peut plus être modifiée. Utilisez le mode brouillon si vous souhaitez vérifier les montants avant envoi.
Envoie automatiquement la facture au client par email après sa création dans Pennylane.
| Valeur | Comportement |
| ------- | -------------------------------------------------------------------------- |
| **Yes** | La facture est envoyée par email à l'adresse du contact principal du devis |
| **No** | La facture est créée sans envoi automatique |
L'envoi email utilise le template configuré dans Pennylane. Ce paramètre s'applique uniquement aux factures — les devis ne sont jamais envoyés par email automatiquement.
***
### Abonnements
Définit le mode de création des abonnements récurrents dans Pennylane.
| Valeur | Comportement |
| ----------------------- | ----------------------------------------------------------------------------- |
| **Awaiting validation** | L'abonnement est créé en attente de validation — vous le validez manuellement |
| **Finalized** | L'abonnement est activé immédiatement |
| **Email** | Pennylane envoie un email de validation au client |
Conditions de paiement appliquées aux factures générées par l'abonnement.
| Valeur | Délai |
| ---------------- | ----------------------- |
| **Upon receipt** | Paiement dès réception |
| **30 days** | Paiement sous 30 jours |
| **45 days** | Paiement sous 45 jours |
| **60 days** | Paiement sous 60 jours |
| **End of month** | Paiement en fin de mois |
Méthode de paiement associée à l'abonnement.
| Valeur | Description |
| --------------------------- | --------------------------------------------- |
| **Offline (wire transfer)** | Virement bancaire — aucune action automatique |
| **GoCardless direct debit** | Prélèvement automatique via GoCardless |
Si vous choisissez **GoCardless direct debit**, activez également **Send GoCardless SEPA mandate request** pour que votre client reçoive automatiquement la demande de mandat.
Envoie automatiquement une demande de mandat SEPA GoCardless au client après la création de l'abonnement.
| Valeur | Comportement |
| ------- | --------------------------------------------------------------------- |
| **Yes** | Une demande de mandat est envoyée au contact principal via GoCardless |
| **No** | Aucune demande automatique |
Ce paramètre ne fonctionne que si **Subscription payment method** est réglé sur **GoCardless direct debit** et que votre compte Pennylane est connecté à GoCardless.
***
## Comportements automatiques
### Création du client
Si le client du devis n'existe pas encore dans Pennylane, il est créé automatiquement avec les informations suivantes :
* Nom de l'entreprise
* Adresse de facturation
* Email du contact principal
* Référence externe (ID Qwoty) — permet d'éviter les doublons lors des prochains devis
### Conditions de paiement et installments
Si votre devis Qwoty contient des **conditions de paiement échelonnées** (acompte + solde), l'intégration crée **une facture par échéance** :
* Les montants sont calculés proportionnellement au pourcentage de chaque échéance
* La première facture suit le statut brouillon/finalisé configuré
* Les factures suivantes sont toujours créées en **brouillon**
### Abonnements multi-phases
Si votre devis contient un plan tarifaire avec **plusieurs phases** (ex : tarif lancement puis tarif standard), l'intégration crée **un abonnement par phase** dans Pennylane, avec la date de démarrage calculée automatiquement.
### Remises globales
Les remises globales appliquées sur l'ensemble du devis sont reportées sur chaque document Pennylane via le champ `discount`.
***
## Questions fréquentes
Non. L'intégration vérifie d'abord si un client avec la même référence externe (ID Qwoty) existe déjà dans Pennylane. Si c'est le cas, il est réutilisé. Si vous avez renseigné manuellement l'ID Pennylane sur la fiche client dans Qwoty, cette valeur est utilisée directement sans aucune recherche.
La TVA est lue depuis les paramètres de chaque produit dans Qwoty et convertie au format Pennylane (ex : 20% → `FR_200`). Si aucune TVA n'est configurée sur un produit, la ligne est créée en exonérée (`exempt`).
L'intégration crée les deux types de documents indépendamment : une facture (ou devis) pour la partie one-off, et un abonnement pour la partie récurrente.
Vérifiez que votre espace Pennylane a un template d'email configuré et que l'adresse email du contact principal est renseignée sur le devis Qwoty. L'envoi email ne fonctionne que sur les **factures finalisées** — si votre facture est en brouillon, l'email ne sera pas envoyé.
Deux conditions sont nécessaires : (1) **Subscription payment method** doit être réglé sur **GoCardless direct debit**, et (2) votre compte Pennylane doit être connecté à GoCardless. Si l'une des deux conditions n'est pas remplie, la demande ne peut pas être envoyée.
# Stripe Billing
Source: https://docs.qwoty.io/developers/integrations/billing/stripe-billing
Automatically creates a Stripe Subscription Schedule when a commercial proposal is signed, including one-off setup fees and multi-phase recurring plans.
## Overview
The Stripe Billing integration transforms a signed Qwoty order into a Stripe Subscription Schedule. Each pricing plan phase becomes a Stripe phase with its own items, billing frequency, and duration. One-off charges (setup fees, hardware, onboarding) are attached as invoice items on the first billing cycle. Discounts are applied as native Stripe coupons at the item, phase, or add-invoice-item level.
If the order contains a start delay, the integration automatically creates a trial period of that duration before billing begins.
## Authentication
Go to [dashboard.stripe.com](https://dashboard.stripe.com) and sign in.
Navigate to **Developers → API keys → Create restricted key**.
Grant write access to: **Customers**, **Subscriptions**, **Subscription Schedules**, **Invoices**, **Tax rates**, **Coupons**.
Copy the key starting with `sk_live_` (or `sk_test_` for testing).
Paste the key into the **Workspace API token** field below.
If you use multiple Stripe accounts (one per business unit), set **Token scope** to `business_unit` and provide one key per business unit ID.
## Settings
### Authentication
| Key | Default | Description |
| --------------------- | ----------- | ---------------------------------------------------------------------------------------- |
| `token_scope` | `workspace` | `workspace` — one global key. `business_unit` — one key per business unit. **Required.** |
| `workspace_api_token` | — | Stripe secret key (if `token_scope = workspace`). **Required.** |
### Billing
| Key | Default | Description |
| ------------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `collection_method` | `charge_automatically` | How Stripe collects payment. `charge_automatically` charges the customer's saved payment method. `send_invoice` sends an invoice by email. |
| `days_until_due` | `30` | Days the customer has to pay (only used when `collection_method = send_invoice`). |
| `payment_behavior` | `allow_incomplete` | Behavior if the first payment fails. `allow_incomplete` — creates the subscription in `incomplete` status. `default_incomplete` — `incomplete` only if payment is required. `error_if_incomplete` — returns an error and does not create the subscription. |
### Taxes
| Key | Default | Description |
| ---------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tax_mode` | `manual` | `automatic` — Stripe Tax computes tax rates automatically (requires Stripe Tax activation). `manual` — tax rates are taken from the Qwoty order and synced to Stripe. |
When `tax_mode = automatic`, no tax rates from Qwoty are sent to Stripe. Stripe Tax must be configured in your Stripe Dashboard.
### Trial
| Key | Default | Description |
| -------------------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `trial_end_behavior` | `create_invoice` | What happens if the customer has no payment method when the trial ends. `create_invoice` — generates an invoice anyway. `pause` — pauses the subscription. `cancel` — cancels the subscription. |
A trial is created automatically when the order's billing start type is `sign_day` with a `delay_days > 0`. The trial lasts exactly `delay_days` days.
### Post-creation
| Key | Default | Description |
| ------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `post_action` | `none` | Action after the schedule is created. `none` — nothing. `send_email` — sends a confirmation email via Qwoty. |
## Output
### What gets created in Stripe
**1. Customer** (if not already linked)
The integration looks up the customer by their Qwoty ID (stored in Stripe metadata), then by email. If not found, a new Stripe customer is created and the Stripe ID is written back to Qwoty (`external_ids.accounting`).
**2. Coupons** (one per unique discount amount)
For each discount in the order, the integration looks up an existing Stripe coupon with the ID `qwoty_{orderId}_{amountCents}_{currency}_{duration}` before creating a new one. All Qwoty coupons are prefixed with `qwoty_` for easy identification in your Stripe Dashboard.
**3. Tax rates** (manual mode only)
Tax rates are resolved via `external_ids.accounting` on each tax entry. If not found, the integration searches Stripe by percentage and country, then creates a new tax rate if absent.
**4. Subscription Schedule**
| CPQ source | Stripe field | Notes |
| ----------------------- | -------------------------- | -------------- |
| Customer resolved | `customer` | |
| `start.type = sign_day` | `start_date = "now"` | |
| `start.type = date` | `start_date` | Unix timestamp |
| Last phase `forever` | `end_behavior = "release"` | Auto-renews |
| Last phase with end | `end_behavior = "cancel"` | Cancels at end |
| `order.id` | `metadata.quote_id` | |
| `order.quote_number` | `metadata.quote_number` | |
| `order.opportunity_id` | `metadata.opportunity_id` | If set |
**Phases** — one per CPQ plan phase:
| CPQ source | Stripe field | Notes |
| -------------------------- | ------------ | --------------------------------- |
| `end.duration` | `iterations` | Converted to billing cycle count |
| `end.date` | `end_date` | Unix timestamp |
| `end.forever` | *(absent)* | Last phase, unlimited |
| `delay_days > 0` (phase 0) | `trial_end` | Unix timestamp, now + delay\_days |
**Items per phase** — one per billable line item:
| CPQ source | Stripe field | Notes |
| ------------------------- | ------------------------------ | --------------------------- |
| `product.name` | `price_data.product_data.name` | |
| `amounts.unit_price` | `price_data.unit_amount` | Gross price × 100 (cents) |
| `billing_frequency` | `price_data.recurring` | interval + interval\_count |
| `amounts.discount_amount` | `items[M].discounts[0].coupon` | Coupon `duration = forever` |
| Tax resolved | `default_tax_rates[]` | Manual mode only |
**One-off items** (from product sections, added to phase 0):
| CPQ source | Stripe field | Notes |
| ------------------------- | ------------------------------------------ | ------------------------ |
| `product.name` | `price_data.product_data.name` | |
| `amounts.unit_price` | `price_data.unit_amount` | Gross price × 100 |
| `amounts.discount_amount` | `add_invoice_items[M].discounts[0].coupon` | Coupon `duration = once` |
| Global discounts | `phases[0].discounts[0].coupon` | Coupon `duration = once` |
| Tax resolved | `add_invoice_items[M].tax_rates[]` | Manual mode only |
### Example — 2-phase subscription with setup fee
A Qwoty order with one setup fee and two 12-month recurring phases produces:
```text theme={null}
Subscription Schedule
├── start_date: now
├── end_behavior: cancel
├── Phase 1 (12 iterations)
│ ├── add_invoice_items
│ │ └── "Setup Fee" — 1 400 € HT
│ └── items
│ └── "Licence Enterprise" — 490 €/month × 10 seats
│ └── coupon: qwoty_xxx_4900_eur_forever (-49 €/month)
└── Phase 2 (12 iterations)
└── items
└── "Licence Enterprise" — 590 €/month × 10 seats
```
All prices are sent as gross amounts (HT). Tax is applied by Stripe at invoice generation time, either automatically (Stripe Tax) or via the resolved tax rates.
# Stripe - Invoice
Source: https://docs.qwoty.io/developers/integrations/billing/stripe-invoice
Automatically create and send a Stripe invoice to your customer when a quote is accepted.
## Overview
The **Stripe — Invoice** integration creates a Stripe invoice from the line items of a Qwoty quote and optionally finalizes and delivers it to your customer.
Depending on your settings, the invoice can be sent directly via Stripe, or paired with a Stripe Checkout payment link and delivered by email via Qwoty with the invoice PDF attached.
***
## Authentication
Go to **Stripe Dashboard → Developers → API keys** and copy your secret key (`sk_live_...` in production, `sk_test_...` in test mode).
The secret key requires the following Stripe permissions:
* **Customers** — read and write (create, search)
* **Invoices** — read and write (create, finalize, send, attach payment)
* **Invoice items** — write (create)
* **Tax rates** — read and write (list, create)
* **Checkout Sessions** — write (create)
* **Webhook endpoints** — read and write (list, create)
Two token scopes are available depending on your organization:
**Workspace** — a single Stripe key shared across all quotes. Enter it in the **Workspace API token** field.
**Business unit** — a different Stripe key per business unit. Enter a JSON map in the **Business unit API tokens** field:
```json theme={null}
{
"business_unit_id_1": "sk_live_...",
"business_unit_id_2": "sk_live_..."
}
```
In **Business unit** mode, the quote must be associated with a business unit whose ID is present in the map. If not, the invoice will not be created.
***
## Behavior
### Customer
The integration automatically finds or creates a Stripe customer for each quote:
1. If a Stripe customer ID is already saved on the Qwoty contact (`external_ids.accounting`), it is used directly.
2. Otherwise, a search is performed in Stripe — first by Qwoty contact UUID, then by billing email.
3. If no match is found, a new Stripe customer is created with the contact's name, email, and billing address. The Stripe customer ID is then saved back to the Qwoty contact for future use.
### Line items
One Stripe invoice item is created per billable product in the quote's `product` sections. If a line item has a discount, a second negative item is added on the same line.
Global discounts on the quote are added as a single negative invoice item at the bottom.
Only sections of type `product` are included. Recurring sections (`plan`) are not supported in this integration.
### Taxes
Tax rates are resolved automatically for each line item:
1. If a Stripe Tax Rate ID is already saved on the Qwoty tax (`external_ids.accounting`), it is used directly.
2. Otherwise, the integration searches existing Stripe Tax Rates by percentage and country (parsed from the Qwoty `api_name` format — e.g. `vat_fr_200` → France, 20%).
3. If no match is found, a new Tax Rate is created in Stripe and its ID is saved back to Qwoty.
Tax rates are always exclusive (prices shown before tax).
### Invoice status
If **Create invoice as Draft**, the invoice is created but not finalized. No email or payment link is sent regardless of other settings.
If **Create invoice as Open**, the invoice is finalized and one of the following actions is triggered:
### Post-finalization actions
| Setting | Action |
| ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Send invoice by email (via Stripe)** | Stripe sends the finalized invoice directly to the customer's email. |
| **Send payment link + invoice (via Qwoty)** | A Stripe Checkout payment link is created for the first installment amount. An email is sent via Qwoty with the invoice PDF attached and a link to pay. Once the customer completes payment, the Checkout Session is automatically attached to the invoice via a Stripe webhook. |
If both options are disabled, the invoice is finalized but not sent.
The **Send payment link + invoice** option requires a Stripe webhook to be registered. The integration handles this automatically on first use — no manual configuration is needed.
***
## Settings
| Setting | Values | Default | Description |
| ------------------------------------------- | -------------- | -------------- | ----------------------------------------------------------------------------------------------------- |
| **Create invoice as** | Draft / Open | Draft | `Draft`: invoice is created but not finalized. `Open`: invoice is finalized immediately. |
| **Send invoice by email (via Stripe)** | Yes / No | No | Stripe sends the finalized invoice to the customer. Only active when invoice is Open. |
| **Send payment link + invoice (via Qwoty)** | Yes / No | No | Sends a Stripe Checkout payment link and the invoice PDF via email. Only active when invoice is Open. |
| **Payment method** | Stripe default | Stripe default | Payment methods presented to the customer on the Checkout page. |
***
## Examples
**Accepted quote — 4 installments of €2,275.50**
The quote has 4 installments. The integration creates a Stripe invoice for the full amount and sends a Checkout payment link for the first installment (€2,275.50) by email.
| Field | Value |
| ------------------- | ------------------ |
| Invoice total | €9,102.00 |
| First installment | €2,275.50 (25%) |
| Payment link amount | €2,275.50 |
| PDF attached | Stripe invoice PDF |
| Link expiry | 24 hours |
Once the customer pays the Checkout Session, the payment is automatically attached to the invoice. The invoice status moves to `partially_paid`, and subsequent installments can be sent the same way.
# HubSpot
Source: https://docs.qwoty.io/developers/integrations/crm/hubspot
Connect Qwoty to HubSpot and manage quotes directly from your CRM, with automatic synchronization of customers, contacts, deals, and products.
Connect Qwoty to HubSpot and manage quotes directly from your CRM, with automatic synchronization of customers, contacts, deals, and products.
***
## Overview
The HubSpot integration connects Qwoty to your HubSpot instance in two ways.
**Embedded widget**: A Qwoty card appears directly on HubSpot Deal records, letting your team create and manage quotes without leaving HubSpot.
**Data synchronization**: Commercial data is automatically synchronized between Qwoty and HubSpot — customers, contacts, projects, and products.
Once HubSpot is connected and the Qwoty card is added to your Deal layout, the integration is ready to use.
***
## Data synchronization
| Qwoty | | HubSpot |
| --------------- | - | ------- |
| Customer | ↔ | Company |
| Primary contact | ← | Contact |
| Project (Quote) | → | Deal |
| Product | → | Product |
***
## Before you connect
Make sure that:
* You have a HubSpot account with admin permissions.
* You have access to your Qwoty workspace settings.
* Your HubSpot user can install and configure connected apps.
***
## Connect HubSpot
In Qwoty, open your workspace settings and navigate to **Developer → Integrations**.
Find HubSpot in the list of available CRM integrations and start the connection.
Select the HubSpot account to link with Qwoty and follow the authorization process to completion.
Return to Qwoty after authorization. HubSpot is now connected.
***
## Enable the Qwoty card in HubSpot
After connecting, a HubSpot administrator must enable the Qwoty card so it can appear on Deal records.
In HubSpot, click the ⚙️ **Settings** icon.
In the left sidebar, navigate to **Integrations → Connected Apps**.
Select **Qwoty** from the list of installed apps and open the **App Cards** tab.
Enable the Qwoty card for the **Deals** object.
***
## Add the Qwoty card to Deal records
After enabling the card, add it to the Deal record layout.
Open any Deal record in HubSpot.
Click **Customize record** in the top-right corner.
In the list of available cards, search for **Qwoty**. Drag and drop it to the desired position — for example, in the right sidebar or within a tab.
Click **Save** to apply the changes.
The Qwoty card now appears on Deal records and lets your team create and manage quotes directly from HubSpot.
***
## Synchronization behavior
**Order generation**
By default, accepting a quote in Qwoty does not automatically generate an order. Enable the **Generate an order after quote acceptance** toggle in the integration settings to activate this behavior.
**Deal amount**
Choose which Qwoty value updates the HubSpot Deal amount field:
| Option | Description |
| ------------- | --------------------------------------- |
| None | The Deal amount is not updated by Qwoty |
| TCV | Total Contract Value |
| MRR | Monthly Recurring Revenue |
| ARR | Annual Recurring Revenue |
| First Payment | Amount of the first payment due |
Field mappings between Qwoty and HubSpot are configurable per object from the integration settings. Each field can be set to sync in one direction (Qwoty → HubSpot or HubSpot → Qwoty) or bidirectionally (↔). Custom Qwoty fields can also be mapped to HubSpot properties.
**Customer — Company**
| Qwoty field | Direction | Notes |
| ------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------- |
| Addresses | HubSpot → Qwoty | Requires all of the following HubSpot fields to be filled: `address`, `address2`, `city`, `state`, `zip`, `hs_country_code` |
| Tax ID | HubSpot → Qwoty | |
| ERP ID | Configurable | |
| Custom fields | Bidirectional | Mappable to any HubSpot company property |
**Project (Quote) — Deal**
Quote fields sync from Qwoty to HubSpot. Available fields include Project ID, Project Number, Status, Title, Expiration Date, MRR, ARR, TCV, One-off Total, First Payment Total Due, Gross Margin, Discount Rate, Issue Date, Number of Views, and Last Active. Custom Qwoty project fields can be mapped bidirectionally to HubSpot Deal properties.
**Contact — Contact**
| Qwoty field | Direction |
| ------------- | --------------- |
| Role | HubSpot → Qwoty |
| ERP ID | HubSpot → Qwoty |
| Accounting ID | HubSpot → Qwoty |
**Product — Product**
Product fields sync from Qwoty to HubSpot. Available fields include Product ID, Reference, Category, Active status, Recurrence Type, Description, Product Type, Unit of Measure, Unit per Pack, SKU, Accounting ID, Ledger Account, ERP ID, Product Parent ID, and Catalog IDs.
Qwoty quote statuses can be mapped to HubSpot Deal pipeline stages. When a quote status changes in Qwoty, the corresponding Deal stage is updated automatically in HubSpot. This stage change can then be used as an enrollment trigger in HubSpot workflows.
The active pipeline is selected from the integration settings. Default mappings:
| Qwoty status | HubSpot stage |
| ---------------- | ------------------------ |
| Draft | Qualified To Buy |
| Pending approval | Decision Maker Bought-In |
| Sent | Contract Sent |
| Accepted | Closed Won |
| Declined | Closed Lost |
| Expired | — |
These mappings can be changed to match your HubSpot pipeline configuration.
***
## Object mapping
| Qwoty object | HubSpot object | Direction |
| --------------- | -------------- | --------------- |
| Customer | Company | Bidirectional |
| Primary contact | Contact | HubSpot → Qwoty |
| Project (Quote) | Deal | Qwoty → HubSpot |
| Product | Product | Qwoty → HubSpot |
***
## HubSpot permissions
The HubSpot user authorizing the connection must have permission to:
* Install connected apps.
* Read and write Company, Contact, Deal, and Product records.
* Access and configure connected app settings (to enable the Qwoty card).
Missing HubSpot permissions may prevent some records from being created or updated, or prevent the Qwoty card from appearing on Deal records.
***
## Disconnect HubSpot
To stop the integration, open the HubSpot integration from your Qwoty workspace settings and disconnect it.
Disconnecting HubSpot stops future synchronization and removes the Qwoty card from HubSpot Deal records. It does not automatically delete records previously created in HubSpot.
***
## Troubleshooting
Make sure you have admin access to the HubSpot account you are connecting. If the authorization page does not load, try again in a private browser window.
Confirm that the card has been enabled in HubSpot under **Settings → Integrations → Connected Apps → Qwoty → App Cards**, and that it has been added to the Deal record layout.
Check that the Deal is linked to the correct Qwoty project. Verify that the relevant field mappings are configured in the integration settings. If the Deal amount is not updating, check that the deal amount setting is not set to *None*.
An address is only created in Qwoty if all of the following HubSpot Company fields are filled in: `address`, `address2`, `city`, `state`, `zip`, `hs_country_code`.
Make sure the product is active in Qwoty. Inactive products are not synchronized to HubSpot.
Verify that the Qwoty status is mapped to a HubSpot pipeline stage in the integration settings, and that the correct pipeline is selected.
***
## Need help?
If the integration remains unavailable after checking the connection and permissions, contact Qwoty support with:
* The affected Qwoty record.
* The corresponding HubSpot record, when available.
* The approximate time of the synchronization attempt.
* A description of the expected result.
# Pipedrive
Source: https://docs.qwoty.io/developers/integrations/crm/pipedrive
Learn how to integrate Qwoty with Pipedrive and manage your quoting directly from your CRM.
The Qwoty - Pipedrive integration provides a seamless connection between your Pipedrive instance and Qwoty. With this integration, you can embed Qwoty’s interface directly inside Pipedrive and manage quotes, without leaving your CRM.
## Overview
The integration allows:
* Embedding a dedicated Qwoty widget on Pipedrive record pages.
* Creating and managing quotes, subscriptions, and customer records inside Pipedrive.
* Optional automatic synchronization of selected Qwoty entities (quotes, subscriptions, invoices) with Pipedrive custom objects for workflows, reporting, and automation.
## Prerequisites
Before you connect Pipedrive with Qwoty:
1. You must have a valid **Pipedrive** account.
2. You must have access to your **Qwoty** account integration settings.
## Installation
**Connect Pipedrive**
1. Connect to [Qwoty app](https://qwoty.app)
2. Go to the **Setting** -> **Develope**r -> **Integration**
3. On se Pipedrive card clic on Connect
4. Select the account to link with Qwoty and follow the process to the end
After enabling the integration:
* You can manage integration on the setup page
* A **Qwoty card** appears on Pipedrive deal pages. This card provides quick actions like creating a quote directly from Pipedrive’s interface.
## Synchronization
Once connected, the integration can optionally synchronize selected data:
* Quotes
* Product
* Order
This keeps your Pipedrive CRM objects up to date with billing and revenue data from Qwoty for automations, reporting, and workflows.
## Benefits
* **Unified workflow:** Sales and finance teams can operate from Pipedrive while using Qwoty’s billing capabilities.
* **Reduced manual work:** No need for duplicate data entry across systems.
* **Improved visibility:** Billing data appears alongside CRM records for deals, contacts, and companies.
# Salesforce
Source: https://docs.qwoty.io/developers/integrations/crm/salesforce
Connect Qwoty to Salesforce and automatically synchronize customers, contacts, opportunities, products, quotes, and orders.
## Overview
The Salesforce integration synchronizes your commercial data between Qwoty and Salesforce.
Once Salesforce is connected, the integration setup starts automatically. No manual workflow configuration is required.
Qwoty automatically prepares the synchronization flow after the Salesforce connection is authorized.
## Data synchronization
```mermaid theme={null}
flowchart LR
subgraph QWOTY["Qwoty"]
direction TB
Q_CUSTOMER["Customer"]
Q_CONTACT["Primary contact"]
Q_QUOTE["Project (Quote)"]
Q_LINE_ITEMS["Line items"]
Q_PHASES["Phases"]
Q_ORDER["Order
on acceptance"]
Q_PRODUCT["Product"]
end
subgraph SALESFORCE["Salesforce"]
direction TB
SF_ACCOUNT["Account"]
SF_CONTACT["Contact"]
SF_OPPORTUNITY["Opportunity"]
SF_LINE_ITEM["OpportunityLineItem"]
SF_SCHEDULE["OpportunityLineItemSchedule"]
SF_ORDER["Order + OrderItem"]
SF_PRODUCT["Product2 + PricebookEntry"]
end
Q_CUSTOMER --> SF_ACCOUNT
Q_CONTACT --> SF_CONTACT
Q_QUOTE -->|"PATCH"| SF_OPPORTUNITY
Q_LINE_ITEMS -->|"CREATE"| SF_LINE_ITEM
Q_PHASES -->|"CREATE"| SF_SCHEDULE
Q_ORDER -->|"CREATE"| SF_ORDER
Q_PRODUCT -->|"UPSERT"| SF_PRODUCT
classDef qwoty fill:#EFF6FF,stroke:#005FFF,color:#0F172A,stroke-width:1.5px;
classDef salesforce fill:#F8FAFC,stroke:#64748B,color:#0F172A,stroke-width:1.5px;
class Q_CUSTOMER,Q_CONTACT,Q_QUOTE,Q_LINE_ITEMS,Q_PHASES,Q_ORDER,Q_PRODUCT qwoty;
class SF_ACCOUNT,SF_CONTACT,SF_OPPORTUNITY,SF_LINE_ITEM,SF_SCHEDULE,SF_ORDER,SF_PRODUCT salesforce;
style QWOTY fill:transparent,stroke:#CBD5E1,stroke-width:1px
style SALESFORCE fill:transparent,stroke:#CBD5E1,stroke-width:1px
```
A Qwoty **Customer** is synchronized with a Salesforce **Account**.
```text theme={null}
The customer's **primary contact** is synchronized with a Salesforce
**Contact**.
```
A Qwoty **Project (Quote)** is associated with a Salesforce **Opportunity**.
```text theme={null}
When the project changes in Qwoty, the corresponding opportunity is
updated using a `PATCH` operation.
```
Each Qwoty quote line item creates a Salesforce **OpportunityLineItem**.
```text theme={null}
Each phase associated with a line item creates an
**OpportunityLineItemSchedule**.
```
When a quote is accepted, Qwoty creates the corresponding Salesforce **Order** and **OrderItem** records.
A Qwoty **Product** is synchronized with Salesforce **Product2** and **PricebookEntry** records.
```text theme={null}
The synchronization uses an `UPSERT` operation:
- Existing records are updated.
- Missing records are created.
```
## Before you connect
Make sure that:
\* You have access to your Qwoty workspace settings. \* You can authorize access to the target Salesforce organization. \* Your Salesforce user has permission to access the objects used by the integration. \* Products that should be synchronized contain the required pricing information.
Missing Salesforce permissions may prevent some records from being created or updated.
## Connect Salesforce
In Qwoty, open your workspace settings and navigate to **Integrations**.
Find Salesforce in the list of available CRM integrations and start the connection.
Sign in to Salesforce and approve the requested access.
Return to Qwoty after authorization.
```text theme={null}
The integration setup starts automatically. You do not need to configure or
activate a separate synchronization workflow.
```
Once the setup is complete, Qwoty can begin synchronizing supported records
with Salesforce.
## Synchronization behavior
Records that do not already exist in Salesforce are created when the corresponding synchronization event occurs.
```text theme={null}
This applies to supported objects that use `CREATE` or `UPSERT`.
```
Existing Salesforce opportunities are updated when their associated Qwoty projects change.
```text theme={null}
Products and pricing records may also be updated through the `UPSERT`
operation.
```
An order is created in Salesforce when the associated quote is accepted in Qwoty.
```text theme={null}
The synchronization creates both the Salesforce **Order** and its related
**OrderItem** records.
```
Qwoty preserves the logical relationships between synchronized records.
```text theme={null}
For example:
- A primary contact is linked to its customer account.
- An opportunity line item is linked to its opportunity.
- A line-item schedule is linked to its opportunity line item.
- An order item is linked to its order.
```
## Object mapping
| Qwoty object | Salesforce object | Operation |
| ------------------- | --------------------------- | ----------- |
| Customer | Account | Synchronize |
| Primary contact | Contact | Synchronize |
| Project (Quote) | Opportunity | `PATCH` |
| Line item | OpportunityLineItem | `CREATE` |
| Phase | OpportunityLineItemSchedule | `CREATE` |
| Accepted order | Order | `CREATE` |
| Accepted order line | OrderItem | `CREATE` |
| Product | Product2 | `UPSERT` |
| Product price | PricebookEntry | `UPSERT` |
## Salesforce permissions
The Salesforce user authorizing the connection must be able to access the objects required by the integration.
The integration requires access to:
```text theme={null}
- Account
- Contact
- Opportunity
- OpportunityLineItem
- OpportunityLineItemSchedule
```
To synchronize accepted quotes, the integration requires access to:
```text theme={null}
- Order
- OrderItem
```
To synchronize products and pricing, the integration requires access to:
```text theme={null}
- Product2
- PricebookEntry
```
## Disconnect Salesforce
To stop future synchronization, open the Salesforce integration from your Qwoty workspace settings and disconnect it.
Disconnecting Salesforce stops future synchronization. It does not automatically delete records that were previously created in Salesforce.
## Troubleshooting
Verify that:
```text theme={null}
- You are signing in to the correct Salesforce organization.
- Your Salesforce user is active.
- Your user is allowed to authorize connected applications.
- Your browser is not blocking the authorization window.
```
Confirm that the Qwoty project is correctly associated with a Salesforce opportunity.
```text theme={null}
Also verify that the Salesforce user used for the connection has permission
to update the Opportunity object and the relevant fields.
```
Check that:
```text theme={null}
- The corresponding opportunity exists.
- The product is available in Salesforce.
- A valid PricebookEntry exists.
- The connected Salesforce user can create OpportunityLineItem records.
```
Verify that the related opportunity line item was created successfully.
```text theme={null}
A phase can only be synchronized after its parent
OpportunityLineItem is available.
```
Confirm that:
```text theme={null}
- The quote has reached the accepted state.
- The related Salesforce account and opportunity are available.
- Orders are enabled in the Salesforce organization.
- The connected user can create Order and OrderItem records.
```
Verify that the product contains the required information and that the connected user can create or update Product2 and PricebookEntry records.
## Need help?
If the integration remains unavailable after checking the connection and permissions, contact Qwoty support with:
* The affected Qwoty record.
* The corresponding Salesforce record, when available.
* The approximate time of the synchronization attempt.
* A description of the expected result.
# Google Chat
Source: https://docs.qwoty.io/developers/integrations/messaging/google-chat
Send a notification message and/or a PDF download link to a Google Chat space when a quote is signed.
## Overview
This integration posts a message to a Google Chat space when a quote is signed in Qwoty. Optionally, it also sends a card with a direct download link to the quote PDF.
***
## Authentication
Google Chat incoming webhooks are self-authenticating — the webhook URL contains the space ID and key. No additional token is required.
1. In Google Chat, open the target space → **Apps & integrations** → **Add webhooks**
2. Give the webhook a name (e.g. `Qwoty`) and click **Save**
3. Copy the generated **Webhook URL** and paste it into the **Token** field
The webhook URL grants write access to the space. Treat it as a secret and do not share it publicly.
Each Google Chat space requires its own webhook URL. If you want notifications in multiple spaces, create a separate integration for each.
***
## Settings
| Key | Default | Description |
| --------------- | ------- | ----------------------------------------------- |
| `token` | — | Google Chat incoming webhook URL. **Required.** |
| `document_send` | `true` | Send a PDF download card after the message. |
***
## Output
### Message
A message is posted in the configured space in the **language of the quote** (EN, FR, DE, IT, PT, ES).
| Quote field | Used for |
| -------------------------------------- | ------------------------- |
| `owner.first_name` + `owner.last_name` | Sales rep full name |
| `customer.name` | Company name |
| `summary.one_off.net_amount` | One-off net amount |
| `summary.metrics.mrr` | Monthly recurring revenue |
**Example (FR):**
```text theme={null}
🎉 BREAKING NEWS :
Emmanuel Laureau vient de signer l'entreprise Hubspot — One-off : 100,00 €
```
Amounts equal to zero are not displayed. A quote with One-off = 0 and MRR = 150 will show only `MRR : 150,00 €`.
If the quote has no owner, the message uses a generic format: `New signature — Company`.
### PDF card
If **Send document link** is enabled, a card is posted with a button linking directly to the quote PDF.
The PDF link points to `https://qwoty.app/api/projects/{id}/pdf`. The recipient must have access to Qwoty to download the file.
# Microsoft Teams
Source: https://docs.qwoty.io/developers/integrations/messaging/microsoft-teams
Send a notification message and/or a PDF download card to a Microsoft Teams channel when a quote is signed.
## Overview
This integration posts a message to a Microsoft Teams channel when a quote is signed in Qwoty. Optionally, it also sends a card with a direct download link to the quote PDF.
***
## Authentication
Teams incoming webhooks are now created via the **Workflows app** — the legacy Connectors method has been replaced.
1. In Teams, right-click the target channel → **Workflows**
2. Search for **"Send webhook alerts to a channel"** and select it
3. Rename the workflow if needed, authenticate with your account, then click **Next**
4. Select the **Team** and **Channel** where notifications should be posted, then click **Add workflow**
5. Copy the generated **HTTP POST URL** and paste it into the **Token** field in Qwoty
To retrieve the URL later: open the **Workflows** app in Teams → select the workflow → **Edit** → expand **When a Teams webhook request is received**.
The webhook URL grants write access to the channel. Treat it as a secret and do not share it publicly.
Webhook availability depends on your organization's settings. If the template is not visible, contact your IT admin.
***
## Settings
| Key | Default | Description |
| --------------- | ------- | --------------------------------------------------------- |
| `token` | — | Teams incoming webhook URL (HTTP POST URL). **Required.** |
| `document_send` | `true` | Send a PDF download card after the message. |
***
## Output
### Message
A message card is posted in the configured channel in the **language of the quote** (EN, FR, DE, IT, PT, ES).
| Quote field | Used for |
| -------------------------------------- | ------------------------- |
| `owner.first_name` + `owner.last_name` | Sales rep full name |
| `customer.name` | Company name |
| `summary.one_off.net_amount` | One-off net amount |
| `summary.metrics.mrr` | Monthly recurring revenue |
**Example (FR):**
```text theme={null}
🎉 BREAKING NEWS :
Emmanuel Laureau vient de signer l'entreprise Hubspot — One-off : 100,00 €
```
Amounts equal to zero are not displayed. A quote with One-off = 0 and MRR = 150 will show only `MRR : 150,00 €`.
If the quote has no owner, the message uses a generic format: `New signature — Company`.
### PDF card
If **Send document link** is enabled, an Adaptive Card is posted with a button linking directly to the quote PDF.
```text theme={null}
┌─────────────────────────────────┐
│ 📄 20260404-Hubspot.pdf │
│ │
│ [ Download PDF ] │
└─────────────────────────────────┘
```
The PDF link points to `https://qwoty.app/api/projects/{id}/pdf`. The recipient must have access to Qwoty to download the file.
# Slack
Source: https://docs.qwoty.io/developers/integrations/messaging/slack
Send a notification message and/or the quote PDF to a Slack channel when a quote is signed.
## Overview
This integration posts a message to a Slack channel when a quote is signed in Qwoty. Optionally, it also uploads the quote PDF to the same channel.
***
## Authentication
You need a **Slack Bot Token** (`xoxb-...`) to authenticate requests.
1. Go to [api.slack.com/apps](https://api.slack.com/apps) → **Create New App** → **From scratch**
2. Under **OAuth & Permissions**, add the following Bot Token Scopes:
* `chat:write` — to post messages
* `files:write` — to upload PDFs
3. Click **Install to Workspace**, then copy the **Bot User OAuth Token**
4. Invite the bot to your target channel: `/invite @YourAppName`
The bot must be invited to the channel before it can post messages or upload files. API calls will return `not_in_channel` otherwise.
Use the **Channel ID** rather than the channel name (e.g. `C0123ABC456`). Find it by clicking the channel name → **About** tab → bottom of the panel.
***
## Settings
| Key | Default | Description |
| ----------------- | ------- | ------------------------------------------- |
| `token` | — | Slack Bot Token (`xoxb-...`). **Required.** |
| `message_channel` | — | Target channel ID or name. **Required.** |
| `document_send` | `true` | Upload the quote PDF to the channel. |
***
## Output
### Message
A message is posted in the configured channel in the **language of the quote** (EN, FR, DE, IT, PT, ES).
| Quote field | Used for |
| -------------------------------------- | ------------------------- |
| `owner.first_name` + `owner.last_name` | Sales rep full name |
| `customer.name` | Company name |
| `summary.one_off.net_amount` | One-off net amount |
| `summary.metrics.mrr` | Monthly recurring revenue |
**Example (FR):**
```text theme={null}
🎉 BREAKING NEWS :
Emmanuel Laureau vient de signer l'entreprise Hubspot — One-off : 100,00 €
```
**Example (EN):**
```text theme={null}
🎉 BREAKING NEWS :
Emmanuel Laureau just signed Hubspot — One-off: 100.00
```
Amounts equal to zero are not displayed. A quote with One-off = 0 and MRR = 150 will show only `MRR : 150,00 €`.
If the quote has no owner, the message uses a generic format: `New signature — Company`.
### PDF upload
If **Send document** is enabled, the quote PDF is uploaded to the same channel.
The file is named: `YYYYMMDD-CompanyName.pdf`
If the quote has no PDF, the upload is skipped even if Send document is enabled.
# Stripe - Payment link
Source: https://docs.qwoty.io/developers/integrations/payment-link/stripe-payment-link
Automatically send a Stripe payment link to your customer as soon as a quote is accepted.
## Overview
The **Stripe — Payment Link** integration creates a Stripe-hosted payment page and sends it to your customer by email as soon as a Qwoty quote is accepted.
The payment link is pre-configured with the amount of the first installment defined on the quote. Your customer simply clicks to pay.
***
## Authentication
### Stripe
Go to **Stripe Dashboard → Developers → API keys** and copy your secret key (`sk_live_...` in production, `sk_test_...` in test mode).
Two modes are available depending on your organization:
**Workspace** — a single Stripe key for all your quotes. Enter it in the **Workspace API token** field.
**Business unit** — a different Stripe key per business unit. Enter a JSON map in the **Business unit API tokens** field:
```json theme={null}
{
"business_unit_id_1": "sk_live_...",
"business_unit_id_2": "sk_live_..."
}
```
***
## Behavior
### Payment link
A Stripe payment link is created for each accepted quote. The amount is the **first installment** defined in the quote's payment terms (`payment_terms.installments[0]`).
If the quote has no payment terms or no installment defined, no payment link will be created.
The link expires **24 hours** after creation.
### Payment email
An email is automatically sent to the customer's primary contact with the payment link.
## Settings
| Setting | Values | Default | Description |
| ---------------------------- | -------------------------- | --------- | ----------------------------------------------------------------------------------------------------------- |
| **Token scope** | Workspace / Business unit | Workspace | Use a single Stripe key for the whole workspace, or one key per business unit. |
| **Workspace API token** | Text | — | Stripe secret key. Visible only when Token scope = Workspace. |
| **Business unit API tokens** | JSON | — | Map `{ "bu_id": "sk_..." }`. Visible only when Token scope = Business unit. |
| **Submit button label** | Pay / Book / Donate / Auto | Pay | Text displayed on the Stripe Checkout submit button. |
| **Collect billing address** | Auto / Required | Auto | `Auto`: Stripe collects the address only when required by the payment method. `Required`: always collected. |
| **Allow promotion codes** | Yes / No | No | Allows the customer to enter a Stripe promo code on the payment page. |
| **Collect phone number** | Yes / No | No | Asks the customer for their phone number on the payment page. |
***
## Examples
**Accepted quote — €588 in French**
The quote has one installment of €588 (100%) and the language is `fr`. The integration creates a payment link and sends the following email to the customer.
| Field | Value |
| ----------- | -------------------------------------------- |
| To | Customer's primary contact email |
| Subject | `Acmé vous a envoyé une demande de paiement` |
| Amount | €588.00 |
| Link expiry | 24 hours after quote acceptance |
| CTA | Link to the Stripe payment page |
# Introduction
Source: https://docs.qwoty.io/developers/introduction
Welcome to the Qwoty API documentation
## Welcome to Qwoty API
Visit [qwoty.app](https://qwoty.app) and sign in to your account
Go to **Settings** → **Developer** → **API Tokens**
Click **Create API Token** and give it a descriptive name
Copy and securely store your API token. It starts with `qwoty_`
Your API token will only be shown once. Store it securely!
## Make Your First Request
Let's retrieve your payment terms using cURL:
```bash theme={null}
curl https://qwoty.app/api/payment-terms \
-H "Authorization: Bearer qwoty_your_api_token_here"
```
Replace `qwoty_your_api_token_here` with your actual API token
## Example Response
```json theme={null}
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Net 30",
"api_name": "net_30",
"is_active": true,
"installments": [
{
"name": "Full Payment",
"percentage": 100,
"term": "net30",
"order_number": 1
}
]
}
]
}
```
## Using Different Languages
```javascript JavaScript (fetch) theme={null}
const response = await fetch('https://qwoty.app/api/payment-terms', {
headers: {
Authorization: 'Bearer qwoty_your_api_token_here',
},
})
const data = await response.json()
console.log(data)
```
```python Python (requests) theme={null}
import requests
headers = {
'Authorization': 'Bearer qwoty_your_api_token_here'
}
response = requests.get(
'https://qwoty.app/api/payment-terms',
headers=headers
)
data = response.json()
print(data)
```
```typescript TypeScript theme={null}
interface PaymentTerm {
id: string
name: string
api_name: string
is_active: boolean
installments: Array<{
name: string
percentage: number
term: string
order_number: number
}>
}
const response = await fetch('https://qwoty.app/api/payment-terms', {
headers: {
Authorization: 'Bearer qwoty_your_api_token_here',
},
})
const data: { success: boolean; data: PaymentTerm[] } = await response.json()
```
## Next Steps
Get your API key and make your first request in minutes
Learn how to authenticate your API requests
Explore all available API resources and endpoints
Need help? Our team is here to assist you
## API Features
Our API follows REST principles with predictable resource-oriented URLs,
accepts JSON-encoded request bodies, and returns JSON-encoded responses.
Clear error messages with appropriate HTTP status codes help you quickly
identify and resolve issues.
All API operations are scoped to your workspace, ensuring data security and
isolation.
## Base URL
All API requests should be made to:
```
https://qwoty.app/api
```
## Need Help?
[support@qwoty.io](mailto:support@qwoty.io)
Access your Qwoty dashboard
# Fair Use Policy
Source: https://docs.qwoty.io/developers/policies/fair-use-policy
How Qwoty keeps plans generous and sustainable for everyone.
We like to overdeliver, but we cannot overcommit.
Our plans are designed to be generous and flexible without forcing customers into rigid volume limits they may never use. Estimating usage at scale is hard, especially over short periods. This fair use policy exists to keep plans sustainable while allowing us to add more value wherever possible without overformalising restrictions.
***
## Spirit of the Plan
Use your plan as much as you like — it is meant to offer a lot. Please respect the spirit and intended scope of the account.
**What happens if I go beyond the scope of this policy?** We will ask you to upgrade to a fitting plan or discuss custom pricing. We will not block your account without reaching out first. You can message us with any questions at [contact@qwoty.io](mailto:contact@qwoty.io).
### Do
* Sign as many documents as you need with your plan for your single business or organisation
* Use the API and automation tools to streamline your signing and sales workflows
* Experiment with plans and integrations while testing what you want to build
### Don't
* Use an individual account API to power a platform or product serving end customers
* Run a large company signing thousands of documents per day on a small team plan
* Expect enterprise-level support on a standard plan
* Overthink this policy
***
## Usage Limits by Plan
The following limits apply per user, per month, across all Qwoty plans:
| Limit | E-sign (€15) | Business (€45) | Pro (€75) |
| ------------------ | ------------ | -------------- | ---------- |
| Signatures / month | 150 | 150 | 150 |
| API calls / month | 1,000 | 5,000 | 20,000 |
| Catalog products | 1,000 | 5,000 | Unlimited |
| Storage | 50 GB | 50 GB | 50 GB |
| Rate limiting | 10 req/sec | 10 req/sec | 10 req/sec |
| SLA | — | — | 99% |
In case of exceeding usage limits, Qwoty may notify you and offer an upgrade, invoice overages at the prevailing rate, or temporarily suspend access until regularisation.
***
## Rate Limits
API and application requests are rate-limited to protect service availability for all users. Limits apply per user or API token.
When a limit is reached, requests return a `429 Too Many Requests` response with a `Retry-After` header indicating when to retry.
Rate limits may vary by plan. Pro and Enterprise plans can include higher or custom limits. Contact [sales](mailto:contact@qwoty.io) for details.
***
## Fair Support
Fair support includes reasonable application-level help for getting the most out of Qwoty. We will help you get unstuck and point you in the right direction when issues come up. Support is provided in good faith and within reasonable time and effort limits.
If you are unsure whether something falls within fair use or fair support, reach out — we are happy to talk it through.
***
# Privacy Policy
Source: https://docs.qwoty.io/developers/policies/privacy-policy
How Qwoty collects, uses, and protects your data.
Our full Privacy Policy is available at [qwoty.io/privacy](https://qwoty.io/privacy).
# Security
Source: https://docs.qwoty.io/developers/policies/security
How Qwoty protects your data and documents across infrastructure, application, and operational layers.
## Security Practices
* **Code review**: All code changes require review before merging
* **Dependency management**: Dependencies are regularly updated and monitored for vulnerabilities
* **Static analysis**: Automated security scanning is part of the CI/CD pipeline
* **Vulnerability scanning**: Automated dependency and vulnerability scanning runs continuously on the codebase
* **Penetration testing**: External third-party penetration testing is planned as part of the ongoing security roadmap (not yet completed)
* **Role-based access**: Teams support member, manager, and admin roles with distinct permissions
* **API token scoping**: API tokens can be scoped to specific teams and set with expiration dates
* **Session management**: Users can view and revoke active sessions
* **Audit logging**: Document actions are logged with timestamps and IP addresses
***
## Infrastructure Security
The Qwoty cloud service uses the following security measures:
| Layer | Implementation |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Hosting** | Supabase (database, authentication, storage) and Vercel (application hosting); Supabase's underlying infrastructure runs on Amazon Web Services (EU region) |
| **Network** | TLS 1.2+ for all connections |
| **Database** | Managed database with automated backups and geographic redundancy |
| **Storage** | AES-256 encrypted object storage for documents |
| **Monitoring** | 24/7 infrastructure monitoring and alerting |
| **Updates** | Regular security patches applied to all infrastructure |
***
## Data Encryption
All data transmitted to and from Qwoty is encrypted using TLS 1.2 or higher. This includes:
* Web application traffic
* API requests
* Email delivery (when supported by the receiving server)
* Webhook payloads
For Qwoty Cloud:
* Database contents are encrypted at rest using AES-256
* Document storage uses encrypted object storage
* Backups are encrypted with geographic redundancy
* Encryption keys are managed with regular key rotation
***
## Authentication Security
### Supported Authentication Methods
| Method | Description |
| ----------------------------- | ------------------------------------------------ |
| **Email and password** | Traditional authentication with hashed passwords |
| **OAuth providers** | Google and Microsoft authentication |
| **Team SSO** | SAML-based single sign-on for enterprise teams |
| **Two-factor authentication** | TOTP-based 2FA with recovery codes |
### Password Requirements
* Minimum length enforced
* Passwords are hashed using bcrypt before storage
* Password reset tokens are time-limited and single-use
### Session Security
* Sessions can be viewed and revoked from account settings
* Session tokens are rotated on authentication events
* Idle sessions expire after a configurable period
***
## Vulnerability Disclosure
Qwoty operates a responsible disclosure process for security vulnerabilities.
If you discover a security vulnerability, please report it by emailing:
[**security@qwoty.io**](mailto:security@qwoty.io)
Include the following information:
* Description of the vulnerability
* Steps to reproduce
* Potential impact
* Any suggested fixes (optional)
| Stage | Timeline |
| --------------------- | ------------------- |
| **Acknowledgment** | Within 48 hours |
| **Initial triage** | Within 5 days |
| **Status update** | Within 10 days |
| **Resolution target** | Depends on severity |
* Qwoty application code
* Authentication and authorization flaws
* Data exposure vulnerabilities
* Injection vulnerabilities
* Cross-site scripting (XSS)
* Cross-site request forgery (CSRF)
* Social engineering attacks
* Denial of service attacks
* Issues in third-party dependencies (report to the upstream project)
* Issues requiring physical access to a user's device
* Theoretical vulnerabilities without proof of concept
We acknowledge security researchers who responsibly disclose vulnerabilities. With your permission, we will credit you when the fix is released.
Do not publicly disclose vulnerabilities until they have been addressed. Public disclosure of unpatched vulnerabilities puts users at risk.
***
## Security Updates
### Notification
Security updates are announced through:
* The [Qwoty changelog](https://qwoty.io/changelog)
* Direct email notification to affected customers when relevant
### Update Policy
* Critical vulnerabilities are patched as quickly as possible
* Release notes include all security-related changes
***
## Contact
For security-related inquiries:
* **Security vulnerabilities**: [security@qwoty.io](mailto:security@qwoty.io)
* **General questions**: [contact@qwoty.io](mailto:contact@qwoty.io)
* **Mailing address**: DOYE SAS, 18 rue Vignon, 75009 Paris, France
***
## Related
* [Privacy Policy](https://qwoty.io/legals/privacy) — Data handling practices
* [GDPR & DPA](/docs/compliance/gdpr) — Data protection and subprocessor information
* [Certifications](/docs/compliance/certifications) — SOC 2, ISO 27001, and regulatory compliance
* [E-Sign Compliance](/docs/compliance/esign) — Electronic signature legal frameworks
# Terms of Service
Source: https://docs.qwoty.io/developers/policies/terms-of-service
The terms that govern your use of Qwoty.
Our full Terms of Service are available at [qwoty.io/terms](https://qwoty.io/terms).
# Quickstart
Source: https://docs.qwoty.io/developers/quickstart
Get up and running with the Qwoty API in a few minutes.
# Glossary
Source: https://docs.qwoty.io/developers/resources/glossary
A complete reference of CPQ terms, Qwoty concepts, and interface vocabulary.
## CPQ Fundamentals
The end-to-end sales process of configuring a product or service offering, pricing it according to business rules, and generating a commercial proposal (quote). Qwoty automates and governs each step of this process.
A priced commercial proposal sent to a prospect. In Qwoty, a quote is the central object of the sales workflow, progressing through a series of statuses from **Draft** to **Accepted**. Also referred to as *Proposition* or *Offer*.
A single product entry within a quote. Each line includes a product, quantity, unit price, and applicable discount.
A revision of a quote after modification. Qwoty tracks the history of changes to maintain a complete audit trail.
The total value of a contract over its entire duration, including all one-time and recurring charges.
The conversion rate of quotes — the percentage of proposals that result in a signed deal.
A selling assistant that guides sales reps step-by-step through quote creation: selecting the right template, catalog, pricebook, and products. Reduces configuration errors and speeds up the process.
***
## Quote Statuses
Each quote moves through a defined lifecycle. The main flow is:
```
Draft → Pending Approval → Approved → Published → Pending Signature → Accepted
```
| Status | Description | Color |
| --------------------- | ------------------------------------------------- | ------ |
| **Draft** | Being created or edited | Gray |
| **Pending Approval** | Submitted for manager validation | Orange |
| **Changes Requested** | Edits requested by the approver; returns to Draft | Orange |
| **Approved** | Validated and ready to publish | Blue |
| **Published** | Visible to the client in the DealRoom | Blue |
| **Pending Signature** | Signature process in progress | Blue |
| **Accepted / Won** | Signed by the client ✅ — triggers Order creation | Green |
| **Expired** | Validity date has passed | Gray |
| **Lost** | Declined by the prospect | Red |
| **Canceled** | Manually canceled | Gray |
***
## Products
A unique identifier for a product or variant. Displayed as the **Reference** field in Qwoty.
A product billed once — e.g., setup fees, hardware purchases, perpetual licenses.
A product billed at a defined interval — e.g., SaaS subscriptions, annual licenses, maintenance contracts.
A specific version of a product defined by configurable attributes (size, color, tier, etc.). Variants share the same product page but can have distinct prices, images, and characteristics. Qwoty auto-generates all combinations from variant options you define.
A file associated with a product or variant: image, datasheet, video, technical specification. Stored in the **Media Library** and usable in quotes and the DealRoom.
A pre-configured pack of several products sold together, with quantities and discounts already set. Reusable in quotes in one click. Example: *Starter Pack = 3 licenses + 1 training session + 1 year support, 15% discount applied*.
An optional complementary product that can be added to a main product or quote.
A logical grouping of similar products to simplify navigation in the catalog. Configured under **Product Options > Categories**.
A visual builder for creating configurable products with complex option rules (dependencies, exclusions, constraints). Accessible at `/products/configurator` — Admin only.
***
## Pricing
A curated grouping of products made available for a specific sales context. Examples: *France Catalog*, *Premium Catalog*, *Partner Catalog*. Linked to one or more pricebooks and assigned to quote templates.
A price list defining the selling prices of products for a given segment, channel, country, or currency. Two types:
* **Fixed**: absolute prices per product, indexed on a catalog.
* **Dynamic**: derived from another pricebook with a multiplier or percentage offset applied.
Displayed as **Price Grid** in the interface.
The price per single unit of a product. Displayed as **UP** in quote line columns.
A progressive pricing model where each quantity bracket is billed at its own rate. Example: units 1–10 at €10, units 11–50 at €8, 51+ at €6.
The unit price is determined by the total quantity ordered and applied to all units. Example: order 50+ units → all units billed at the 50+ tier rate.
Periodic billing at a defined interval (monthly, quarterly, annually). Used for subscriptions and SaaS licenses.
Fixed unit price multiplied by the quantity entered in the quote.
Price calculated as a percentage of a reference value (total amount, another product's value, etc.). Suited for service fees, commissions, or proportional surcharges.
Price calculated from the cost price with a margin or multiplier applied. Guarantees minimum profitability on each quote line.
A progressive pricing plan where price or quantity evolves automatically over time according to a defined schedule. Used for contracts with onboarding phases or progressive commitment tiers.
A reduction applied to a price. Two modes in Qwoty:
* **Manual**: the sales rep applies a discount directly on the quote or per line.
* **Preconfigured**: rules set by admins, activated in one click based on criteria (product type, quote amount, segment, team, payment terms, etc.).
A minimum or maximum constraint applied to prices or discounts to enforce commercial policy compliance.
The minimum authorized price. A guardrail that prevents selling below cost or below policy thresholds.
The maximum authorized price or discount. A guardrail that prevents excessive discounting or overcharging.
The predictable monthly revenue generated by recurring subscriptions.
The predictable annual revenue generated by recurring subscriptions.
The difference between the selling price and the cost price. Displayed as a real-time KPI in the quote editor sidebar.
***
## Templates & Configuration
The core sales funnel configuration object. A template pre-defines all the rules and resources for a given type of sale: catalogs, pricebooks, allowed discounts, payment terms, payment methods, DealRoom forms, contract templates, content blocks, approval workflow, KPIs, and authorized teams. When a sales rep creates a quote, they select a template that automatically configures all of these elements — ensuring commercial compliance, consistency, and speed.
The association between a quote template (sales funnel) and a contract model. Ensures the right contract document is automatically attached to each type of proposal.
Configuration template for orders generated from accepted quotes. Defines the structure and rules of the resulting purchase order.
The actual text of a contract document, enriched with **dynamic variables** automatically populated from quote and client data: `{{client.name}}`, `{{quote.total}}`, `{{start_date}}`, etc. Written once, reused indefinitely.
A pre-written, versioned marketing section (company overview, solution description, case study, team introduction, etc.) that sales reps insert into quotes in one click. Blocks are assigned to templates so Qwoty auto-inserts the right content per sale type.
***
## DealRoom
The shared online space between the sales rep and the client. A quote must be in **Published** status to be visible in the DealRoom. The client can view the proposal, comment, negotiate, complete the checkout form, and sign — all from a single link.
Client interactions tracked within the DealRoom: page views, sections read, time spent, actions taken (form submitted, signed, etc.). Used to prioritize follow-ups.
A participant on the client side who has access to the DealRoom (viewer, approver, or signer).
Uploading and sharing supplementary files directly in the DealRoom: datasheets, presentations, contractual annexes.
The DealRoom step where the client finalizes their order: billing form, shipping form, custom fields, and payment confirmation.
*(Add-on)* A dedicated space for recurring clients — distinct from the one-time DealRoom. Clients can browse their full history of quotes, orders, and contracts, download documents, and track order status autonomously.
Automatic follow-up emails sent to clients when a shared quote has not been viewed or signed after a configurable delay. Reduces lost opportunities without manual action.
***
## Contracts & Signatures
Legally binding digital signature integrated directly into Qwoty. Two standards supported:
* **eIDAS** (European standard, level 1) for European markets.
* **UETA** (Uniform Electronic Transactions Act) for the US market.
Two signing modes: **Open** (anyone with the link can sign) and **Restricted** (only named signataries can sign).
The process of annotating and proposing modifications to a contract document, typically during negotiation. Available in contract editing mode.
A purchase order automatically generated when a quote reaches **Accepted** status. The order captures the committed products, quantities, and prices.
A container that groups all the quotes and orders from the same deal. Provides a unified view of the commercial relationship over time.
A complete log of all actions and validations performed on a quote, order, or contract. Ensures traceability and compliance.
***
## Subscriptions & Lifecycle
A recurring engagement between the client and the vendor, billed at regular intervals.
The process of extending an active subscription for another period.
Selling a higher-tier product or plan to an existing client.
Selling a complementary product to an existing client.
Increasing the scope of an existing subscription (more users, more features, more volume).
Reducing the scope of an existing subscription.
Aligning the end dates of multiple subscriptions so they renew simultaneously.
Customer loss — the cancellation of a subscription. A key metric for recurring revenue businesses.
***
## Approvals & Workflows
An automated validation circuit triggered when a quote meets certain configurable conditions: high amount, low margin, excessive discount, specific client, etc. The quote is blocked until approved. Prevents non-compliant commercial commitments.
The person (manager or admin) designated to validate a quote submitted for approval.
Automatic forwarding of an approval request to a higher level when the primary approver is unavailable or a threshold is exceeded.
A visual editor for configuring the business rules that govern Qwoty's behavior: approval conditions, association rules (catalog, pricebook, discount), pricing logic. No technical development required.
***
## Organization & Users
The top-level account in Qwoty. An organization contains one or more workspaces.
An isolated working environment with its own data, users, and configuration. Multiple workspaces can coexist within one organization (e.g., by country, business unit, or environment).
A legal entity (subsidiary, brand, or company) operating within a workspace. Each Business Unit has its own name, legal information, VAT number, and visual identity (logo, colors, typography) — injected into generated documents and the DealRoom.
Defines the permissions and access level of a user. Qwoty provides four system roles:
| Role | Description |
| ------------- | -------------------------------------- |
| **Admin** | Full access, system configuration |
| **Manager** | Team management, approvals, reporting |
| **Sales Rep** | Quote creation and management, clients |
| **Viewer** | Read-only access |
Custom roles with granular permissions can also be created.
A group of users sharing the same access to templates, catalogs, and discounts. Assigning a quote template to a team automatically grants all its members access to the same commercial resources.
***
## Configuration & Settings
The set of Qwoty data objects (products, clients, quotes, contracts, etc.) and their fields. **Standard** objects are fixed; **Manageable** objects can be extended with custom fields to adapt Qwoty to specific business needs.
An additional field added to a Manageable object to capture business-specific information. Each custom field has a name, an API Name (stable technical identifier), and a data type.
A stable, immutable technical identifier assigned to objects, fields, roles, teams, and other entities. Used in integrations and the Qwoty API to ensure consistency over time, even if display names change.
Configurable payment schedules composed of one or more milestones, each with a percentage of the total amount and a due date (e.g., Net 30, 3 installments, payment on receipt). Assigned to quote templates.
The payment modes offered to clients in quotes and in the DealRoom (credit card, wire transfer, direct debit, check, etc.). Each method includes client-facing instructions. Assigned to quote templates.
Configurable tax rules covering VAT by country, GST, US Sales Tax, eco-taxes, and local taxes. Each tax defines a type, name, country/region of application, and calculation method (percentage or fixed amount).
Qwoty supports multiple ISO currencies for international quoting. A default currency is set at workspace creation; additional currencies can be enabled. Prices are configured per currency in pricebooks.
Automatic quote numbering format configured by combining free text, an incremental counter, and date variables. Example format: `PROJ-YYYY-XXXX`.
Settings controlling the layout and formatting of PDF exports generated from quotes and contracts.
The centralized file repository for all assets used in Qwoty: product images, logos, datasheets, marketing assets, contractual documents. Organized by category (Product, Content, Contract) and file type.
***
## Integrations & Technical
A system managing customer relationships and sales pipelines. Qwoty integrates natively with **HubSpot**, **Pipedrive**, and **Salesforce** for bidirectional contact, company, and opportunity synchronization.
An enterprise management system covering finance, operations, inventory, and more. Qwoty can synchronize product and order data with ERP systems via API or native integrations.
Synchronization in both directions between Qwoty and a connected system (CRM, ERP): changes in either platform are reflected in the other.
An automatic HTTP notification sent to an external system when a specific event occurs in Qwoty (e.g., quote accepted, order created). Configured under Settings > Developers > Webhooks.
Qwoty's native REST API exposing all objects. Each object has a UUID (Qwoty identifier) and a stable API Name. Used for CRM/ERP synchronization and advanced automation. Keys and documentation available at Settings > Developers > API.
Upload of CSV files for bulk creation or update of data: products, variants, clients, prices. Enables catalog and client base initialization from existing system exports.
The Qwoty integration marketplace listing available connectors: ERP, accounting tools, payment solutions, communication platforms, and other business applications.
*(Add-on)* Stock management module: tracks inventory levels per product and variant, synchronizes with external inventory systems (ERP, WMS), and can condition product availability in quotes based on real-time stock.
***
## Interface Vocabulary
| Term | Definition | Location |
| ---------------------- | -------------------------------------------------------------------------------------------------------- | ------------------ |
| **Dashboard** | Home page with KPIs and recent activity | Home `/` |
| **Sidebar / Left nav** | Main navigation panel on the left | Left of screen |
| **Header** | Top bar with primary actions | Top of screen |
| **Breadcrumb** | Hierarchical navigation trail | Below the header |
| **Quick actions** | Contextual action buttons | Top right |
| **Filters** | Search and refinement criteria | Above lists |
| **List view** | Default table-style display | Default |
| **Kanban view** | Column-based display by status | Toggle option |
| **KPI** | Key Performance Indicator — a real-time business metric shown in the quote editor sidebar and dashboards | Sidebar, Dashboard |
| **Quote Insights** | Real-time client engagement data (DealRoom views, time spent, actions) | Quote detail |
# Import master products
Source: https://docs.qwoty.io/user-guide/data-migration/how-tos/import-master-products
Create master products that group multiple variants together. Always import these first.
A master product is the parent that groups multiple variants — for example, a "Premium T-Shirt" master with `Small`, `Medium`, `Large` variants. Always import master products **before** their variants and prices, since they're the parent that everything else references.
You must have the **Admin** role with **Data → Import CSV** authorized. See [Manage roles](/user-guide/settings/users/manage-roles) for permission details.
## Open the import
In the left sidebar, click **Settings**.
Under the **Data** section, click **Import/Export**.
Tick the **Master products** card at the top of the page. The Import and Export sections below adapt.
Get the sample CSV with the right column headers.
Drop your prepared file. The 4-step wizard runs: **Upload → Mapping → Confirm → Result**.
## CSV columns reference
The Master products import uses the same template as Products — the difference is in **how you populate the rows**. For master products, each row creates a parent that variants will reference. You leave variant-specific fields empty.
| Column | Required | Type | Description |
| ---------------------------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------- |
| `product_id` | — | UUID | Leave empty to **create** a new master. Set to an existing UUID to **update**. |
| `product_name` | \* | Text | Master product display name shown in the catalog and on quotes. |
| `product_api_name` | — | Text | Stable API identifier. **Strongly recommended** — variants will reference the master via this name. |
| `reference` | — | Text | Internal reference shown to sales reps. |
| `product_description` | — | Text | Long-form description shown on quotes and in the Dealroom. |
| `primary_image_id` | — | UUID | UUID of an image already uploaded to the Qwoty media library. |
| `settings[recurrence_type]` | \* | Enum | `one_off` for one-time purchase, `recurring` for subscriptions. All variants of this master inherit this value. |
| `settings[is_active]` | — | Boolean | `true` (default) to make the master visible in catalogs, `false` to archive. |
| `settings[product_type]` | — | Enum | `physical`, `service`, or `subscription`. Drives quote behaviour and reporting. |
| `settings[unit_of_measure]` | — | Text | Display unit (for example, `unit`, `kg`, `hour`). |
| `settings[language_code]` | — | Text | Two-letter language code for localized content (`en`, `fr`, `de`). |
| `settings[unit_per_pack]` | — | Number | If the master is sold in packs, the number of units per pack. |
| `catalog_api_names` | — | Text | Comma-separated catalog API names. Empty value attaches the workspace default catalog. |
| `category_api_names` | — | Text | Comma-separated category API names. |
| `inventory[sku]` | — | Text | Master-level SKU (variants override with their own). |
| `identifiers[erp]` | — | Text | External ERP identifier. |
| `identifiers[crm]` | — | Text | External CRM identifier. |
| `identifiers[accounting]` | — | Text | External accounting identifier. |
| `accounting[ledger_account]` | — | Text | Ledger account code used for accounting reports. |
| `shipping[weight]` | — | Number | Net weight. |
| `shipping[weight_unit]` | — | Enum | `kg`, `g`, `lb`, `oz`. |
| `shipping[height]` | — | Number | Height in `shipping[length_unit]`. |
| `shipping[length]` | — | Number | Length in `shipping[length_unit]`. |
| `shipping[width]` | — | Number | Width in `shipping[length_unit]`. |
| `shipping[length_unit]` | — | Enum | `cm`, `mm`, `m`, `in`. |
| `shipping[country_of_origin]` | — | Text | ISO 2-letter country code (`FR`, `DE`, `CN`). Used for customs and trade documents. |
| `shipping[harmonized_system_code]` | — | Text | Harmonized System (HS) code for international shipping and customs. |
When importing a master, you typically leave variant-specific fields like `inventory[sku]` empty if each variant has its own SKU. Use the master row to set defaults that apply across all variants (recurrence type, product type, shipping dimensions for non-varying physical products).
## Common scenarios
### Create master products without variants
For products that don't have variants — a single SKU master that will later have one or more variants attached:
```csv theme={null}
product_id,product_name,product_api_name,settings[recurrence_type],settings[product_type],catalog_api_names
,Premium T-Shirt,premium_tshirt,one_off,physical,france
,Annual SaaS Plan,annual_saas_plan,recurring,subscription,saas_catalog
,Consulting Day,consulting_day,one_off,service,services
```
After this import, you can add variants to each master via the [Products import](/user-guide/data-migration/how-tos/import-products), referencing them by `product_api_name`.
### Create master products with full shipping data
For physical goods that need detailed shipping info:
```csv theme={null}
product_id,product_name,product_api_name,settings[recurrence_type],settings[product_type],shipping[weight],shipping[weight_unit],shipping[height],shipping[length],shipping[width],shipping[length_unit],shipping[country_of_origin],shipping[harmonized_system_code]
,Premium T-Shirt,premium_tshirt,one_off,physical,0.2,kg,2,30,25,cm,FR,610910
,Coffee Mug,coffee_mug,one_off,physical,0.4,kg,12,9,9,cm,DE,691200
```
### Update existing master products
When `product_id` is set, the row updates rather than creates:
```csv theme={null}
product_id,product_description,settings[is_active]
6ddb0e85-1d1e-4c11-b45d-c9c7ff7c62e8,Premium 100% organic cotton t-shirt with reinforced seams.,true
9b7ccfa9-1234-5678-aabb-c0ffee123456,Discontinued — replaced by SKU-MUG-V2.,false
```
Only the columns you include are updated. Other fields stay untouched.
## Troubleshooting
They shared the same `product_name` and neither had a `product_api_name`. Re-import with a unique `product_api_name` per master.
Use exactly `one_off` or `recurring` (lowercase, with underscore). Common mistakes: `monthly`, `yearly`, `subscription`, `one-off` (with dash).
Qwoty matches catalog API names exactly (case-sensitive). Check the API name in **Catalog & Products → Catalogs** and re-import. For multiple catalogs in one cell, use commas with no spaces and wrap the cell in double quotes: `"france,partner_pricing"`.
The image must exist in the Qwoty media library before the import. Upload your images first, then export the media list to retrieve their UUIDs.
Boolean values must be lowercase: `true` or `false`. Uppercase `TRUE` or `FALSE` is rejected.
## Next steps
Now that masters exist, import the variants that reference them.
Once products exist, attach prices to them.
Universal formatting rules.
Conceptual reference on the master/variant structure.
# Import and export prices
Source: https://docs.qwoty.io/user-guide/data-migration/how-tos/import-prices
Update pricebook prices in bulk by exporting your existing data, editing the CSV, and reimporting it.
Prices in Qwoty are created automatically — every time a pricebook is set up, all relevant products receive a price entry. Your job is to fill in the values, not to create the entries from scratch.
```mermaid theme={null}
flowchart LR
A["Pricebook created"] -->|"Qwoty auto-generates"| B["1 price entry per\nproduct × billing period\npricing_model = None"]
B --> C["Export CSV\n(id already exists\nfor every row)"]
C --> D["Edit in spreadsheet\nSet pricing_model\n& amounts"]
D --> E["Reimport"]
E --> F["Prices live ✓"]
F -->|"Need to update"| C
```
When you create a pricebook, Qwoty automatically generates one price entry per product and per billing period — all set to `None`. Those entries already have an `id`. The export-then-reimport flow is therefore the same whether you are configuring prices for the first time or updating existing ones.
The standard workflow is: **export your existing prices → edit the CSV → reimport it**. The `id` column is the only key Qwoty uses to match rows; all other fields are updated in place.
You must have the **Admin** role to access Import & Export. Pricebooks must already exist before you can configure their prices.
## Access
Navigate to **Settings → Import & Export**, then select the **Prices** section.
The page has two panels: **Import** (upload a CSV) and **Export** (download your current data).
## Update prices
In the **Export** panel, choose what to download:
* **All data** — every price across all pricebooks
* **Filter by pricebook** — one pricebook at a time
* **Filter by catalog** — all prices for products in a given catalog
Click **Export CSV**. The file contains one row per price, with the `id` column pre-filled for every existing entry.
Open the file in your spreadsheet editor and fill in or update the pricing columns. The following columns are read-only context — they identify what each row represents and must not be changed:
* `id`, `product_api_name`, `pricebook_api_name`, `currency_code`, `type`, `period_unit`, `period`
The columns you configure are: `pricing_model`, `amount`, `percent`, `cost`, `floor_price`, `vat_code`, `engagement_type`, `period_duration_month`, `pay_as_you_go`, `identifiers[*]`, and tier columns.
Rows with an empty `pricing_model` (or `None`) are placeholder prices waiting to be configured. Set the model and the relevant amount columns for those rows.
Do not add new rows. Prices are created automatically when pricebooks are set up — rows not present in the export do not correspond to any existing price and will be rejected on import.
In the **Import** panel, click **Upload File** and drop your edited CSV.
The import wizard runs in four steps: **Upload → Mapping → Confirm → Result**. Review the mapping screen before confirming to make sure columns are matched correctly.
Once the import completes, your prices are live in the corresponding pricebooks. Open a pricebook to verify the changes.
## The six pricing models
Each row declares its model in the `pricing_model` column. The required columns depend on the model.
| Model | Use it for |
| ------------------ | -------------------------------------------------------------------- |
| `Flat` | One fixed amount — the most common case |
| `Cost based` | Selling price computed as a multiplier on the product's defined cost |
| `Percent` | A percentage of a reference value, calculated at quote time |
| `Graduated Tiered` | Each tier applies progressively to volumes within that tier |
| `Volume Tiered` | The entire quantity is priced at the tier matching the total |
| `None` | Placeholder — the price is filled in manually at quote time |
`pricing_model` values are case-sensitive. Use exactly: `Flat`, `Cost based` (with a space), `Percent`, `Graduated Tiered`, `Volume Tiered`, `None`. Any other casing is rejected.
## CSV columns reference
| Column | Required | Type | Description |
| ------------------------- | -------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `id` | ✱ | UUID | Price ID — the only key used on import. Never change this value. |
| `product_api_name` | — | Text | Read-only context. The API name of the associated product. |
| `pricebook_api_name` | — | Text | Read-only context. The API name of the associated pricebook. |
| `currency_code` | — | Text | Read-only context. ISO 4217 code (`EUR`, `USD`, `GBP`…). |
| `type` | — | Enum | Read-only context. `one-time` or `recurring`. |
| `period_unit` | — | Enum | Read-only context. Billing period unit (`day`, `week`, `month`, `year`). |
| `period` | — | Number | Read-only context. Number of period units per cycle (`1` = monthly, `3` = quarterly). |
| `pricing_model` | ✱ | Enum | `Flat`, `Cost based`, `Percent`, `Graduated Tiered`, `Volume Tiered`, `None`. |
| `vat_code` | — | Text | VAT identifier in `vat__` format (e.g. `vat_fr_200` = France 20%). Leave empty for no default VAT. |
| `floor_price` | — | Number | Minimum allowed price (guardrail when discounts are applied). |
| `amount` | ✱ if `Flat` | Number | The fixed price per unit. |
| `percent` | ✱ if `Percent` | Number | The percentage value (`8.95` = 8.95%). |
| `percent[multiplier]` | — | Number | Optional multiplier on the percent calculation. |
| `percent[perimeter]` | — | Text | Scope the percentage applies to (e.g. `product`). |
| `percent[externals_id]` | — | UUID | Reference ID for the percent target. |
| `cost` | — | Number | For `Flat`: internal cost reference used for margin reporting. For `Cost based`: the multiplier applied to the product's cost to compute the selling price. |
| `pay_as_you_go` | — | Boolean | `true` for usage-based billing where the amount is computed at consumption time. |
| `engagement_type` | — | Enum | `forever` (open-ended) or `fixed` (commitment period). |
| `period_duration_month` | — | Number | Commitment duration in months. Required when `engagement_type` is `fixed`. |
| `identifiers[erp]` | — | Text | External ERP reference for this price. |
| `identifiers[crm]` | — | Text | External CRM reference for this price. |
| `identifiers[accounting]` | — | Text | External accounting reference for this price. |
| `tiers[starting_unit][N]` | ✱ if tiered | Number | Lower bound of tier N (inclusive). N is 0-based: `[0]`, `[1]`, `[2]`… |
| `tiers[ending_unit][N]` | — | Number | Upper bound of tier N (inclusive). Leave empty for "no upper limit". |
| `tiers[price][N]` | ✱ if tiered | Number | Per-unit price for tier N. |
| `tiers[flat_fee][N]` | — | Number | Optional flat fee added on top of tier N (`Graduated Tiered` only). |
Tier indexes are 0-based: the first tier is `tiers[starting_unit][0]`, the second is `tiers[starting_unit][1]`, and so on.
## Examples by pricing model
### Flat one-time
A fixed amount per unit, with an internal cost reference for margin reporting:
```csv theme={null}
id,currency_code,type,pricing_model,vat_code,floor_price,amount,cost
a3b97966-...,EUR,one-time,Flat,vat_fr_200,13900,14900,8700
```
### Flat recurring (monthly, open-ended)
A fixed monthly fee with no commitment period:
```csv theme={null}
id,currency_code,type,pricing_model,vat_code,amount,period_unit,period,engagement_type
bf56bdd5-...,EUR,recurring,Flat,vat_fr_200,0,month,1,forever
```
### Flat recurring (annual, fixed commitment)
An annual price with a 12-month commitment:
```csv theme={null}
id,currency_code,type,pricing_model,vat_code,amount,period_unit,period,engagement_type,period_duration_month
d1b65444-...,EUR,recurring,Flat,vat_fr_200,1100,year,1,fixed,12
```
### Cost based
Selling price is computed as the product's defined cost × the multiplier in `cost`. A `floor_price` sets the minimum:
```csv theme={null}
id,currency_code,type,pricing_model,vat_code,floor_price,cost
dcc190c5-...,EUR,one-time,Cost based,vat_fr_200,10,2
```
### Percent
A percentage of a reference product's price, calculated at quote time:
```csv theme={null}
id,currency_code,type,pricing_model,vat_code,percent,percent[perimeter],percent[externals_id],period_unit,period,engagement_type
5a68792e-...,EUR,recurring,Percent,vat_fr_200,8.95,product,b150e52a-...,month,1,forever
```
### Graduated tiered
The first 10 units at €49, everything above at €39:
```csv theme={null}
id,currency_code,type,pricing_model,vat_code,period_unit,period,tiers[starting_unit][0],tiers[ending_unit][0],tiers[price][0],tiers[flat_fee][0],tiers[starting_unit][1],tiers[ending_unit][1],tiers[price][1],tiers[flat_fee][1]
ac06fe31-...,EUR,recurring,Graduated Tiered,vat_fr_200,month,1,1,10,49,0,11,,39,0
```
For 15 units: `10 × €49 + 5 × €39 = €685`.
### Volume tiered
The entire quantity is priced at the tier matching the total volume:
```csv theme={null}
id,currency_code,type,pricing_model,vat_code,tiers[starting_unit][0],tiers[ending_unit][0],tiers[price][0],tiers[flat_fee][0],tiers[starting_unit][1],tiers[ending_unit][1],tiers[price][1],tiers[flat_fee][1]
...,EUR,one-time,Volume Tiered,vat_fr_200,1,100,6.00,0,101,,5.00,0
```
For 150 units: `150 × €5.00 = €750` (everything at the tier matching 150).
### None (placeholder)
The product appears in the pricebook but has no configured price. Sales reps fill in the amount manually on each quote:
```csv theme={null}
id,currency_code,type,pricing_model,vat_code
5f7efcbd-...,EUR,one-time,None,vat_fr_200
```
## Troubleshooting
`pricing_model` is case-sensitive. Use exactly: `Flat`, `Cost based` (with a space), `Percent`, `Graduated Tiered`, `Volume Tiered`, `None`. Lowercase variants or underscores (`cost_based`) are rejected.
The `id` column must contain a UUID that exists in your workspace. Only rows from an export of your own workspace are valid. Do not copy IDs from another workspace or create new ones manually.
Qwoty orders tiers by `tiers[starting_unit][N]` value, not by index position. Make sure `tiers[starting_unit][0]` is the smallest value. Overlapping ranges trigger a validation error.
Use the `vat__` format, all lowercase, with the rate as a number without a decimal point: `vat_fr_200` for 20%, `vat_fr_055` for 5.5%, `vat_de_190` for 19%. Leave the cell empty when no VAT applies.
When `engagement_type` is `fixed`, you must also fill in `period_duration_month` with the number of months (e.g. `12` for a 12-month commitment). Leaving it empty with `engagement_type = fixed` causes the field to be ignored.
The `percent` column takes a number, not a fraction: `8.95` means 8.95%, not 0.0895. Combine with `floor_price` to set a minimum charge.
## Related
Conceptual deep-dive on each pricing model and when to use it.
Set up the pricebook before exporting its prices to configure.
Products must exist before their prices can be configured.
Universal formatting rules for all Qwoty imports.
# Import products
Source: https://docs.qwoty.io/user-guide/data-migration/how-tos/import-products
Import products and product variants linked to existing master products and catalogs.
The Products import creates the actual sellable items: simple products with no variants, or variants attached to a master product. Import master products **first** if your products reference them.
You must have the **Admin** role with **Data → Import CSV** authorized.
## Open the import
In the left sidebar, click **Settings**.
Under the **Data** section, click **Import/Export**.
Tick the **Products** card at the top of the page.
Get the sample CSV with the right columns.
Drop your file. The 4-step wizard runs: **Upload → Mapping → Confirm → Result**.
## CSV columns reference
The Products template uses the same columns as Master products. The difference is what you populate — for products and variants, the rows reference an existing master through `parent_product_api_name` or are standalone single-variant items.
| Column | Required | Type | Description |
| ---------------------------------- | -------- | ------- | ------------------------------------------------------------------------------------------ |
| `product_id` | — | UUID | Leave empty to **create** a new product. Set to an existing UUID to **update**. |
| `product_name` | \* | Text | Product or variant display name (for example, `Premium T-Shirt - Blue / M`). |
| `product_api_name` | — | Text | Stable API identifier for this specific product or variant. |
| `reference` | — | Text | Internal reference shown to sales reps. |
| `product_description` | — | Text | Long-form description shown on quotes and in the Dealroom. |
| `primary_image_id` | — | UUID | UUID of an image already uploaded to the Qwoty media library. |
| `settings[recurrence_type]` | \* | Enum | `one_off` or `recurring`. Must match the master's recurrence type if attached to a master. |
| `settings[is_active]` | — | Boolean | `true` (default) or `false`. |
| `settings[product_type]` | — | Enum | `physical`, `service`, or `subscription`. |
| `settings[unit_of_measure]` | — | Text | Display unit (for example, `unit`, `kg`, `hour`). |
| `settings[language_code]` | — | Text | Two-letter language code (`en`, `fr`, `de`). |
| `settings[unit_per_pack]` | — | Number | Number of units per pack, when sold in packs. |
| `catalog_api_names` | — | Text | Comma-separated catalog API names. Empty value attaches the workspace default catalog. |
| `category_api_names` | — | Text | Comma-separated category API names. |
| `inventory[sku]` | — | Text | SKU for this product or variant. |
| `identifiers[erp]` | — | Text | External ERP identifier. |
| `identifiers[crm]` | — | Text | External CRM identifier. |
| `identifiers[accounting]` | — | Text | External accounting identifier. |
| `accounting[ledger_account]` | — | Text | Ledger account code for accounting reports. |
| `shipping[weight]` | — | Number | Net weight. |
| `shipping[weight_unit]` | — | Enum | `kg`, `g`, `lb`, `oz`. |
| `shipping[height]` | — | Number | Height in `shipping[length_unit]`. |
| `shipping[length]` | — | Number | Length in `shipping[length_unit]`. |
| `shipping[width]` | — | Number | Width in `shipping[length_unit]`. |
| `shipping[length_unit]` | — | Enum | `cm`, `mm`, `m`, `in`. |
| `shipping[country_of_origin]` | — | Text | ISO 2-letter country code (`FR`, `DE`, `CN`). |
| `shipping[harmonized_system_code]` | — | Text | Harmonized System (HS) code for international shipping. |
When `catalog_api_names` or `category_api_names` contain multiple values, the cell must be wrapped in double quotes and use **commas** as the internal separator: `"france,partner_pricing"`. Without the quotes, the comma inside the cell breaks the CSV row.
Variants attached to a master inherit the master's `settings[recurrence_type]`, `settings[product_type]`, and shipping defaults. You can still set them on the variant row to override — but they must be consistent or Qwoty rejects the row.
## Common scenarios
### Create simple, single-variant products
For a list of one-off products with no variants:
```csv theme={null}
product_id,product_name,product_api_name,settings[recurrence_type],settings[product_type],catalog_api_names,inventory[sku]
,Wireless Mouse,wireless_mouse,one_off,physical,france,SKU-001
,USB-C Cable,usb_c_cable,one_off,physical,france,SKU-002
,Laptop Stand,laptop_stand,one_off,physical,france,SKU-003
```
### Create variants attached to existing master products
You've already imported a `Premium T-Shirt` master with `product_api_name = premium_tshirt`. Now add three variants:
```csv theme={null}
product_id,product_name,settings[recurrence_type],inventory[sku],identifiers[erp]
,Premium T-Shirt - Blue / S,one_off,SKU-PT-BS,ERP-PT-BS
,Premium T-Shirt - Blue / M,one_off,SKU-PT-BM,ERP-PT-BM
,Premium T-Shirt - Blue / L,one_off,SKU-PT-BL,ERP-PT-BL
```
The way variants attach to master products depends on your workspace configuration (typically through `parent_product_api_name` or `product_parent_id`). Check the example file downloaded from the import screen — it includes the relation columns specific to your workspace.
### Update existing products
Export your current products to retrieve their UUIDs, then re-import with the changes:
```csv theme={null}
product_id,product_name,inventory[sku]
6ddb0e85-1d1e-4c11-b45d-c9c7ff7c62e8,Wireless Mouse Pro,SKU-001-PRO
9b7ccfa9-1234-5678-aabb-c0ffee123456,USB-C Cable v2,SKU-002-V2
```
Only the columns you include get updated. Other fields stay untouched.
### Bulk update SKUs across catalogs
Useful when migrating from another system:
```csv theme={null}
product_id,inventory[sku],identifiers[erp]
6ddb0e85-1d1e-4c11-b45d-c9c7ff7c62e8,NEW-SKU-001,ERP-NEW-001
9b7ccfa9-1234-5678-aabb-c0ffee123456,NEW-SKU-002,ERP-NEW-002
```
## Troubleshooting
The only accepted values for `settings[recurrence_type]` are `one_off` and `recurring` (lowercase, with underscore). Common mistakes: `monthly`, `yearly`, `subscription`, `one-off` (with dash).
Qwoty matches catalog API names exactly (case-sensitive). Check the catalog API names in **Catalog & Products → Catalogs**. For multiple catalogs, use commas inside the cell with no spaces, and wrap the value in double quotes: `"france,partner_pricing"`.
The UUID in `primary_image_id` doesn't match any image in your media library. Upload your images first, export the media list to get their UUIDs, then update your CSV.
Check that `catalog_api_names` is populated. If empty, the product is attached only to the workspace's default catalog (which may be hidden from your sales reps).
Updates only modify the columns present in the CSV. If a field looks empty after import, check that you didn't leave the column header in the file with all-empty values — that overwrites the field.
Use lowercase `true` or `false`. Uppercase `TRUE` or `FALSE`, or `1` / `0`, are rejected.
## Next steps
Now that products exist, attach prices to them.
If you skipped this step, import master products first.
Universal formatting rules.
Manage where products live.
# Prepare your CSV files
Source: https://docs.qwoty.io/user-guide/data-migration/how-tos/prepare-csv-files
Complete step-by-step guide to format your data for import into Qwoty.
This guide walks you through preparing your CSV file for a successful import into Qwoty. Follow these steps in order to avoid errors.
## Step 1: Check file requirements
Before you start, ensure your file meets these requirements:
| Requirement | Details |
| ------------------------- | -------------------------------------------------------------------------------------------------- |
| **Format** | CSV |
| **Encoding** | UTF-8 (recommended) |
| **Field separator** | Comma `,` |
| **Decimal separator** | Period `.` |
| **Within-cell separator** | Comma `,` separates multiple values inside a single cell (for example, multiple catalog API names) |
| **Structure** | One object type per file (Master products, Products, or Prices) |
| **First row** | Headers — every column must have a name |
For datasets larger than 10,000 rows, split into multiple files or use the [Qwoty API](https://docs.qwoty.io).
## Step 2: Download the sample file
**This is the most important step.** The sample file shows you the exact column names and format Qwoty expects.
1. Open **Settings → Data → Import/Export Data**.
2. Select the object you want to import: **Master products**, **Products**, or **Prices**.
3. Click **Download example file**.
4. Use this file as your template — keep the headers, replace the example rows with your data.
**Pro tip:** export a few existing records first instead of starting from the
empty template. The export gives you real examples of how Qwoty formats data,
and the column names map automatically when you re-import.
## Step 3: Remove duplicate values
Qwoty enforces uniqueness on certain fields. Duplicates cause import errors.
| Object | Unique fields |
| ------------------- | -------------------------------------------------------------------------- |
| **Master products** | `product_parent_id`, `parent_product_api_name` |
| **Products** | `product_id`, `product_api_name`, `inventory[sku]` (if used as identifier) |
| **Prices** | `id` |
Before importing:
1. Sort your spreadsheet by the unique field.
2. Remove or merge duplicate rows.
3. Check for duplicates that already exist in Qwoty by exporting first.
## Step 4: Format each field type correctly
Different field types require specific formats. Here's the complete reference for Qwoty.
### Text fields
* No special formatting required.
* Leading and trailing spaces are automatically trimmed.
* If a value contains a comma, line break, or quote, wrap it in double quotes: `"Premium, Extra Large"`.
### Number fields
* Numbers only (no text).
* Use period for decimals: `1234.56`.
* No thousands separators (not `1,234.56`).
* Negative numbers allowed only where the schema permits (most price fields require positive values).
### Boolean fields
Use lowercase: `true` or `false`.
This applies to fields like `settings[is_active]`.
### Enum (select) fields
Use the **exact value** Qwoty expects, including capitalization. Common enums:
| Field | Accepted values |
| --------------------------- | ---------------------------------------------------------------------------- |
| `settings[recurrence_type]` | `one_off`, `recurring` |
| `settings[product_type]` | `physical`, `service`, `subscription` |
| `type` (price) | `one-time`, `reccuring` |
| `pricing_model` | `Flat`, `Cost based`, `Percent`, `Graduated Tiered`, `Volume Tiered`, `None` |
| `period_unit` | `day`, `week`, `month`, `year` |
Enums are case-sensitive. `flat` is different from `Flat`. `recurring` is
different from `Recurring`. Use the exact values shown above.
### Date fields
Use ISO 8601:
* `YYYY-MM-DD` — for example, `2026-04-25`
* `YYYY-MM-DDTHH:MM:SSZ` — for timestamps
### Currency fields
Use the ISO 4217 three-letter code: `EUR`, `USD`, `GBP`, `JPY` — not the symbol (`€`, `$`) or the full name.
### Tax (VAT) fields
Use the format `_`. Examples:
| Value | Meaning |
| -------- | ------------------ |
| `FR_200` | France, 20.0% VAT |
| `FR_055` | France, 5.5% VAT |
| `DE_190` | Germany, 19.0% VAT |
| `-` | No tax |
### ID fields
* **Optional**: Qwoty auto-generates UUIDs if not provided.
* **Format**: UUID, for example `c776ee49-f608-4a77-8cc8-6fe96ae1e43f`.
* **Use case**: Include the ID to **update** existing records instead of creating new ones.
### Multi-value fields
Some fields accept multiple values inside a single cell, separated by **commas**:
| Column | Example |
| -------------------- | ------------------------ |
| `catalog_api_names` | `france,partner_pricing` |
| `category_api_names` | `hardware,accessories` |
If a value within the list contains a comma, wrap the whole cell in double quotes: `"category_one,category, with comma"`.
### Indexed fields (tiers, options)
Qwoty uses bracket notation with a numeric index for repeating field groups. Indexes are **0-based** for tiers and **1-based** for options.
```text theme={null}
tiers[starting_unit][0],tiers[ending_unit][0],tiers[price][0]
options[1][name],options[1][value]
```
Add `[1]`, `[2]`, `[3]`... to declare additional rows.
## Step 5: Add relation columns
Qwoty objects reference each other through specific fields. To link a record to its parent or related object, populate the right column.
| Linking | Column to populate |
| ------------------------ | ------------------------------------------------------------------- |
| Product → Master product | `parent_product_api_name` (preferred) or `product_parent_id` (UUID) |
| Product → Catalog(s) | `catalog_api_names` (comma-separated) |
| Product → Category(ies) | `category_api_names` (comma-separated) |
| Price → Product | `product_id` (UUID) |
| Price → Pricebook | `pricebook_id` (UUID or pricebook name) |
**Import order matters!**
1. **Master products** first — they're the parents.
2. **Products** second — they reference master products.
3. **Prices** last — they reference both products and pricebooks.
Catalogs, Categories, and Pricebooks must exist before you start. Create them through the UI first.
## Step 6: Ensure custom fields exist in Qwoty
The import creates **records**, not **fields**. Any custom field you want to populate must already exist in your data model.
Before importing:
1. Open **Settings → Data → Data model**.
2. Select the object (Customer, Contract Model — note that Products and Prices are not currently extensible with custom fields).
3. Add the custom fields you need.
4. Make sure the column header in your CSV matches the field's API name exactly.
See the [Data model](/user-guide/data-model/introduction) page for the full guide on adding custom fields.
## Step 7: Final checklist
Before uploading your file, verify:
* File is in **CSV** format
* Encoding is **UTF-8**
* Field separator is **comma `,`**
* Decimal separator is **period `.`**
* Multi-value cells use commas inside, with quotes around the cell if needed
* No duplicate values in unique fields (`product_id`, `product_api_name`, `id`)
* Boolean fields use **lowercase** `true` or `false`
* Enum fields use the **exact case** shown in Step 4
* Dates use **ISO 8601** format
* All custom fields exist in **Settings → Data → Data model**
* Master products are imported **before** products
* Products are imported **before** prices
* Catalogs, Categories, and Pricebooks already exist in the workspace
## Common mistakes to avoid
| Mistake | Solution |
| ----------------------------------------------------------- | -------------------------------------------------------------------------------- |
| Using `;` as the separator | Qwoty expects comma `,` |
| Using `flat` or `recurring` (lowercase) for `pricing_model` | Use `Flat`, `Volume Tiered`, etc. — exact case |
| Importing products before their master | Always import master products first |
| Forgetting to wrap multi-catalog values in quotes | `"france,partner_pricing"` is required when the cell contains commas |
| Mapping the same column twice | Each Qwoty target accepts only one source column |
| Using `,` as the decimal separator | Numbers must use period `.` |
| Mixing `recurring` (product) with `one-time` (price) | `settings[recurrence_type] = recurring` requires `type = reccuring` on the price |
## Next steps
Your file is ready. Now:
Always start here.
Once your masters exist.
Last step — prices reference products and pricebooks.
Add custom fields before importing if needed.
# Error handling and validation
Source: https://docs.qwoty.io/user-guide/data-migration/reference/error-handling
Review and fix import errors before confirming, and download unimported rows after import.
## Pre-import validation
After uploading your file and mapping fields, Qwoty validates your data **before** importing. This lets you catch and fix errors without affecting your existing data.
## How it works
The 4-step import wizard surfaces errors at two points:
Drop your CSV file. Qwoty parses it and detects basic issues (file format, encoding, separator).
Match each header to a Qwoty field. If a required field isn't mapped, you can't proceed to the next step.
Qwoty validates every row against the rules of the target object. The summary shows:
* The number of records that will be imported
* The number of variants the file contains (for products)
* The number of rows with **validation errors**
Errors don't block valid rows. You can go back to fix your CSV or proceed.
Qwoty processes the file. You see a count of rows imported and rows that failed. If any failed, click **Download unimported rows** to get a CSV of the failures with the reason for each.
## Error display
Errors are surfaced in two ways:
* **At the Confirm step** — a summary count tells you how many rows have validation errors before you commit
* **At the Result step** — after import, the **Download unimported rows** button gives you a CSV listing every failed row, the column at fault, and the validation message
The unimported-rows CSV is the same format as your original file, plus an `error_reason` column. Fix the rows in your spreadsheet and re-upload — only the failed rows are processed on the second import.
## Common error types
### Duplicate values
**Cause:** A unique field (like `product_api_name`) already exists in Qwoty or appears twice in your file.
**Fix:**
* Edit the duplicate value in your CSV and re-upload
* Remove one of the duplicate rows
* If you intended to update the existing record, populate the `id` column (the import detects updates automatically)
See [Uniqueness constraints](/user-guide/data-migration/reference/uniqueness-constraints) for the full list of unique fields.
### Invalid format
**Cause:** A value doesn't match the expected format — invalid UUID, wrong date format, lowercase enum where Qwoty expects capitalized values, percentage out of range.
**Fix:** Edit the cell to match the expected format. See [Field mapping](/user-guide/data-migration/reference/field-mapping) for the format requirements of each field type.
Common offenders:
* `pricing_model = flat` instead of `Flat` (case-sensitive)
* `settings[recurrence_type] = monthly` instead of `recurring`
* `default_vat = 20.0` instead of `FR_200`
* `amount = 99,00` instead of `99.00` (wrong decimal separator)
* `settings[is_active] = TRUE` instead of `true` (booleans must be lowercase)
### Missing required fields
**Cause:** A column marked with `*` in the Mapping step is empty for a given row.
**Fix:** Enter a value in the required field, or remove the row from your file.
### Relation not found
**Cause:** The referenced record doesn't exist — for example, a `parent_product_api_name` that points to a master that wasn't imported, or a `pricebook_id` referencing a pricebook that doesn't exist.
**Fix:**
* Import the parent records first (see [Import order](/user-guide/data-migration/reference/import-relations#import-order-rule))
* Or correct the reference value to match an existing record
### Inconsistent values within a master
**Cause:** Two variants of the same master declare different `settings[recurrence_type]`, different option names, or different shipping defaults.
**Fix:** Standardize the values across all variants of the same master. A master has a single recurrence type and a single set of option names — variants only differ on option **values**.
### Wrong file separator
**Cause:** Your CSV uses semicolon `;` instead of comma `,` (or vice versa). Qwoty expects comma.
**Fix:** Re-export your file with comma as the separator. In Excel, save as **CSV UTF-8 (Comma delimited)**.
## Tips for fewer errors
1. **Download the example file** from the import screen first — it shows the exact column headers and expected formats
2. **Clean your data** in your spreadsheet first — remove blank rows, fix typos, normalize enum values
3. **Import files in the correct order** — Master products → Products → Prices
4. **Test with small batches** before importing thousands of rows — fix the systematic issues, then run the full import
5. **Check for duplicates** before uploading — sort your file by the unique field
6. **Limit your file size** — split very large datasets into batches of a few thousand rows each, or use the [API](https://docs.qwoty.io)
## Related
Format requirements for every field type.
Which fields are unique and how to use them.
The right import order to avoid relation errors.
Step-by-step preparation guide.
# Import relations between objects
Source: https://docs.qwoty.io/user-guide/data-migration/reference/import-relations
Import relationships between products, masters, prices, catalogs, and pricebooks via CSV.
## Overview
Qwoty supports importing relationships between objects during CSV import. This lets you link records (for example, attach products to a master, or attach prices to a pricebook) as part of your data migration.
**Supported relations for import:**
* **Product → Master product** (parent/child)
* **Product → Catalog** (one product attached to one or more catalogs)
* **Product → Category** (one product attached to one or more categories)
* **Price → Product** (each price targets one product)
* **Price → Pricebook** (each price belongs to one pricebook)
## How relations work in Qwoty
### One-to-many relations
Qwoty's catalog model uses standard one-to-many relations:
* **One Master product → Many Products (variants)** — a master groups several variants
* **One Pricebook → Many Prices** — a pricebook is the container for prices across products
* **One Catalog → Many Products** — a catalog scopes which products are sellable in a market or segment
### Many-to-many relations
Some relations are many-to-many — these use comma-separated lists inside a single CSV cell, with the cell wrapped in double quotes:
| Relation | CSV column | Example |
| -------------------- | -------------------- | -------------------------- |
| Product ↔ Catalogs | `catalog_api_names` | `"france,partner_pricing"` |
| Product ↔ Categories | `category_api_names` | `"hardware,accessories"` |
## Linking records during import
### Step 1: Identify the "one" and "many" sides
For each relation, determine which side is the parent ("one") and which is the child ("many"):
| Relation | "One" side | "Many" side |
| ----------------- | -------------- | ----------------- |
| Master / Variant | Master product | Product (variant) |
| Pricebook / Price | Pricebook | Price |
| Catalog / Product | Catalog | Product |
| Product / Price | Product | Price |
### Step 2: Ensure the "one" side records exist
Before importing the "many" side, the "one" side records must already exist in Qwoty.
* Create your **Catalogs**, **Categories**, and **Pricebooks** through the UI first
* Import **Master products** before importing their variants
* Import **Products** before importing their prices
The import will fail if a row references a record that doesn't exist.
### Step 3: Prepare your CSV file
Add a column in your "many" side CSV file that references the "one" side using a unique identifier:
| Linking | Column to populate | Accepted format |
| ----------------------- | ------------------------------------------------------------ | ------------------------------------------------ |
| Product → Master | `parent_product_api_name` (preferred) or `product_parent_id` | API name (Text) or UUID |
| Product → Catalog(s) | `catalog_api_names` | Comma-separated API names, in quotes if multiple |
| Product → Category(ies) | `category_api_names` | Comma-separated API names, in quotes if multiple |
| Price → Product | `product_id` | UUID |
| Price → Pricebook | `pricebook_id` | UUID or pricebook name |
The reference value must **exactly match** the unique field on the parent record. For API names, this means the same case, no extra spaces, no extra characters. Verify the parent's API name in **Settings → Data → Data model** or in the parent object's list view.
### Step 4: Ensure the relation field is populated correctly
Before uploading your file, double-check that:
* The parent records have the API names or UUIDs you reference
* The `*_api_names` columns use commas (not semicolons) inside the cell, with double quotes around multi-value cells
* The `parent_product_api_name` column matches your master's API name byte-for-byte
### Step 5: Upload and map the relation
1. Upload your CSV file via the import UI
2. In the **Mapping** step, find your relation column (for example, `catalog_api_names`)
3. Map it to the matching Qwoty field (for example, **Catalog API Names**)
4. Qwoty automatically links each record to the matching parent at the **Confirm** step
## Available unique fields for relations
| Object | Unique fields available for references |
| -------------------------------------------- | -------------------------------------------------- |
| **Master products** | `product_id` (UUID), `product_api_name` |
| **Products** | `product_id` (UUID), `product_api_name` |
| **Catalogs** | API name (set when the catalog is created) |
| **Categories** | API name |
| **Pricebooks** | UUID or pricebook name |
| **Customers** (when migrating customer data) | `id`, custom unique fields you've marked as unique |
We recommend using API names (`parent_product_api_name`, `catalog_api_names`, `category_api_names`) when possible — they're human-readable, easy to maintain in spreadsheets, and survive across environments.
## Import order rule
**Always import the "one" side first.**
1. **Catalogs, Categories, Pricebooks** — must exist in your workspace before any import (create them through the UI).
2. **Master products** — import these first. They're the parents that variants reference.
3. **Products** — import second, referencing their masters.
4. **Prices** — import last, referencing both products and pricebooks.
The parent record must exist before you can reference it. The import fails for rows pointing to records that don't yet exist.
## Related
Which fields are unique and what that means for relations.
Format requirements for relation columns.
Always start your migration here.
Final step — references products and pricebooks.
# Create a Contract custom field
Source: https://docs.qwoty.io/user-guide/data-model/how-tos/create-contract-custom-field
Add a custom field to a contract model — used as a contract variable and synced to your CRM.
A Contract custom field captures information specific to your legal templates — for example, a clause variant, a jurisdiction, an effective date, or a guaranteed minimum. The field appears on contract records, can be referenced as a variable inside the contract text, and optionally syncs to your CRM.
This guide walks through the full lifecycle in three steps.
You must have the **Admin** role with **Data → Read & Write** authorized. CRM mapping additionally requires **Developers** access.
## What you'll build
In this example, you'll add a **Jurisdiction** field on Contract Model — a Text field that drives a clause variant in your contract templates. Sales reps will pick it during quoting, and it will appear in the contract via a `{{contract.jurisdiction}}` variable.
| Field | Type | Purpose |
| ---------------- | ---- | ----------------------------------------------------- |
| **Jurisdiction** | Text | Country or state under which the contract is governed |
The field will be **mandatory**, **editable by sales reps**, and **not displayed to the customer** (it's an internal legal-ops marker — the customer sees the resolved clause text in the contract itself, not the variable name).
## Step 1: Create the field in the Data model
Go to **Settings → Data → Data model** and click the **Contract Model** object.
In the top right of the field list, click **+ New** to open the field creation form.
* **Name**: `Jurisdiction`
* **API Name**: Qwoty generates `jurisdiction` automatically. Edit if you need a specific identifier (for example, `governing_jurisdiction`).
* **Data Type**: pick `Text`
Click **Save** in the top right. The new field appears in the Contract Model object's field list with the **Custom** badge.
The field exists in the data model. Next, configure how it behaves on each contract model record.
## Step 2: Configure the field on the contract model
The field exists, but it doesn't yet appear on any contract. Activate it on the contract models where it applies, and configure the three properties.
Navigate to **Documents → Contract Model** and open the contract template you want to configure (for example, `Standard Sales Contract`).
Scroll to the **Custom Fields** section. The `Jurisdiction` field you just created appears, with a toggle and three checkboxes.
Click the toggle to turn the field on. The toggle turns dark — the field is now part of every contract generated from this model.
Check the boxes that match your needs:
* **Mandatory** ✓ — the contract can't be generated without a jurisdiction
* **Editable by sale** ✓ — sales reps pick the value during quoting
* **Display for customer** — leave unchecked (jurisdiction is internal — the resolved clause text is what the customer sees)
If most of your contracts share the same jurisdiction, set a default (for example, `France`). Sales reps can override it per deal.
Click **Save**.
### Use the field in the contract text
Now that the field is active, you can reference it as a variable inside the contract template's body:
```
This Agreement is governed by the laws of {{contract.jurisdiction}}.
Any dispute shall be settled before the courts of {{contract.jurisdiction}}.
```
When the contract is generated, the variable resolves to the value the sales rep entered. Combine it with conditional blocks (if your contract editor supports them) to swap entire clauses based on the value:
```
{{#if contract.jurisdiction == "France"}}
... French-specific arbitration clause ...
{{else if contract.jurisdiction == "Germany"}}
... German-specific arbitration clause ...
{{/if}}
```
Pair custom fields with content blocks ([Documents → Content blocks](/user-guide/documents/reference/content-blocks)) for reusable contract sections that depend on the field's value.
## Step 3: Sync with your CRM (optional)
If your CRM tracks contract metadata (which is often the case in regulated industries), map the Jurisdiction field to a matching CRM field.
Go to **Settings → Developers → Integration** and click your connected CRM.
Scroll to the custom fields mapping. The Contract Model field appears as `Custom: Jurisdiction`.
Open the direction dropdown and choose:
* **Bidirectional** — if your legal team also updates jurisdiction in the CRM
* **Qwoty → CRM** — typical: the value is set during quoting and pushed to the CRM record
* **CRM → Qwoty** — if jurisdiction is determined upstream by your CRM workflows
Pick the CRM field that holds the jurisdiction value. Compatible types: Text, Picklist, Dropdown.
Click **Save changes** at the bottom of the page.
Your contract jurisdiction now flows between Qwoty and your CRM, and is also resolved as a variable inside generated contracts.
## Common Contract custom fields
Beyond Jurisdiction, here are patterns that come up often:
| Field | Type | Why |
| ---------------------------- | ------------------------ | ------------------------------------------------------------------ |
| **Effective date** | Date | When the contract takes effect (often differs from signature date) |
| **Auto-renewal flag** | Text (or future boolean) | Triggers a different renewal clause |
| **Notice period (months)** | Number | Used in termination clauses |
| **Liability cap** | Number | Negotiated indemnity ceiling |
| **Governing law** | Text | Combined with Jurisdiction for cross-border contracts |
| **External legal reference** | Text | Internal counsel ticket or case number |
## Troubleshooting
Either the field is empty on the contract record, or the variable name doesn't match the API name. Open the contract and verify the field is filled. Check that the variable uses the field's API name exactly (case-sensitive).
Check that the toggle is **on** in the Custom Fields section of the contract model. Disabled toggles hide the field from forms.
The **Editable by sale** checkbox is unchecked. Activate it on the contract model.
Add a custom field on the CRM side first (Text or Picklist for Jurisdiction), then refresh the integration mapping section.
## Related
Mandatory, Editable, Display — the three controls that shape behaviour.
How variables, content blocks, and contract models combine.
Same flow, applied to quotes.
Sync directions and CRM field compatibility.
# Create a Customer custom field
Source: https://docs.qwoty.io/user-guide/data-model/how-tos/create-customer-custom-field
Add a custom field to customers — for segmentation, regulatory IDs, or CRM-synced attributes.
A Customer custom field captures attributes specific to your account base — for example, a customer type, a regulatory ID, an account manager assignment, or a sales region. The field appears on every customer record, can be filtered in customer views, and most importantly, syncs to your CRM, where customer master-data typically lives.
This guide walks through the full lifecycle in three steps.
You must have the **Admin** role with **Data → Read & Write** authorized. CRM mapping additionally requires **Developers** access.
## What you'll build
In this example, you'll add two custom fields used to qualify customers:
| Field | Type | Purpose |
| ----------------- | ---- | ------------------------------------------------ |
| **Customer type** | Text | `Prospect`, `Key Account`, `Partner`, `Reseller` |
| **VAT number** | Text | Legal tax ID for cross-border invoicing |
Both will be **editable by sales reps**, **not displayed to the customer** (these are internal qualifiers, not buyer-facing data), and **synced bidirectionally with your CRM** since customer master-data is shared across both systems.
## Step 1: Create the field in the Data model
Go to **Settings → Data → Data model** and click the **Customer** object.
In the top right of the field list, click **+ New** to open the field creation form.
* **Name**: `Customer type`
* **API Name**: Qwoty generates `customer_type`. Edit if you need a different identifier.
* **Data Type**: pick `Text`
Click **Save**, then create `VAT number` with type `Text` the same way.
Two new fields appear in the Customer object's field list with the **Custom** badge.
## Step 2: Configure the field on the customer
Like Product, Customer custom fields appear on every customer record once enabled — they're not gated by a template.
Navigate to **Customers** and open any customer record (or create a new one). The new fields appear in the customer's detail view.
Scroll to the **Custom Fields** section. Both fields appear with a toggle and three checkboxes.
Click the toggle for each field to turn it on.
For our customer-qualification use case:
* **Mandatory**: check for `Customer type` (segmentation should always be filled), uncheck for `VAT number` (only relevant for B2B / cross-border)
* **Editable by sale** ✓ — sales reps maintain customer qualifications during onboarding
* **Display for customer** — leave **unchecked**. These are internal — the customer doesn't need to see how you qualify them
Enter the values for existing customers, or import the data via CSV if you have many. Customer fields are commonly bulk-populated when migrating from a legacy CRM.
Click **Save**.
Your customers carry the new fields. They flow into reporting (filterable in customer views) and become available for CRM sync.
## Step 3: Sync with your CRM (recommended)
Customer custom fields are the **#1 use case for CRM sync**. The CRM is typically the source of truth for customer master-data, and Qwoty needs the same view to make smart quoting decisions.
Go to **Settings → Developers → Integration** and click your connected CRM.
Scroll to the customer mapping. The fields appear as `Custom: Customer type` and `Custom: VAT number`.
For customer master-data, we strongly recommend:
* **Customer type** → **Bidirectional** — both systems need the same segmentation, and either may be edited
* **VAT number** → **CRM → Qwoty** — typically maintained by sales-ops in the CRM; Qwoty just needs to read it
Avoid **Qwoty → CRM** for customer master-data unless Qwoty really is the source of truth for that field (rare).
Pick the matching field on the CRM:
* HubSpot: a Single-line text or Dropdown property on the Company object
* Salesforce: a Picklist or Text field on Account
* Pipedrive: a Single-option or Free-form field on Organization
Compatible types: Text → Text, Picklist, Dropdown.
Click **Save changes** at the bottom of the page. Sync starts at the next cycle.
Customer types and VAT numbers now sync between Qwoty and your CRM. New customers added in either system appear in the other within seconds.
## Common Customer custom fields
Beyond type and VAT, here are fields that come up often:
| Field | Type | Sync direction (typical) |
| ---------------------- | ------------------------- | ------------------------ |
| **Account manager** | Text (or future relation) | Bidirectional |
| **Industry** | Text | CRM → Qwoty |
| **Annual revenue** | Number | CRM → Qwoty |
| **Region / Territory** | Text | Bidirectional |
| **NAICS / SIC code** | Text | CRM → Qwoty |
| **Strategic flag** | Text (or future boolean) | Bidirectional |
| **Last QBR date** | Date | CRM → Qwoty |
When migrating from another CRM, first list every custom property on the customer/company object in that CRM, then create matching fields in Qwoty before importing customer data. The import will populate the fields automatically. See [Import customers](/user-guide/customers/how-tos/import-csv).
## Troubleshooting
Refresh the page. If still missing, check that the toggle is **on** in the Custom Fields section.
Check that the column header in your CSV matches the field's **API Name** exactly (case-sensitive). See [Import customers](/user-guide/customers/how-tos/import-csv) for the full column reference.
Qwoty's Text type stores the value as-is. If your CRM sends `Strategic, Renewal, Risk`, Qwoty stores that whole string. Either parse the values in your CRM workflows before sync, or open multiple Text fields in Qwoty for each tag.
Bidirectional uses last-write-wins. If two users edit the same field in both systems within a sync window, the latest write wins. To avoid conflicts, decide which system is canonical for high-stakes fields and use one-way sync (CRM → Qwoty or Qwoty → CRM) instead.
Verify that **CRM → Qwoty** sync is active, that the CRM webhook fires (visible in **Settings → Developers → Webhook**), and that the integration has read access to the Company / Account object. New records typically appear within seconds.
## Related
Manage your customer base.
Bulk-populate custom fields via CSV.
All sync directions and compatibility rules.
Connect HubSpot, Salesforce, or Pipedrive.
# Create a Product custom field
Source: https://docs.qwoty.io/user-guide/data-model/how-tos/create-product-custom-field
Add a custom field to products — for catalog metadata, certifications, or technical attributes.
A Product custom field captures attributes specific to your catalog — for example, hectares for an agricultural product, a certification level for a regulated good, or a technical capacity for industrial equipment. The field appears on every product record, can be filtered and sorted in catalog views, and optionally syncs to your CRM or ERP.
This guide walks through the full lifecycle in three steps.
You must have the **Admin** role with **Data → Read & Write** authorized. CRM mapping additionally requires **Developers** access.
## What you'll build
In this example, you'll add two custom fields to a product catalog used by an agricultural cooperative:
| Field | Type | Purpose |
| ----------------------- | ------ | ------------------------------------------------------------------------ |
| **Hectares** | Number | Surface area covered by the product (e.g., a fertilizer or seed) |
| **Certification level** | Text | Regulatory or quality certification (e.g., `Organic`, `AOC`, `ISO 9001`) |
Both will be **editable by sales reps**, and **displayed to the customer** so they can confirm the certification on their quote.
## Step 1: Create the field in the Data model
Go to **Settings → Data → Data model** and click the **Product** object.
In the top right of the field list, click **+ New** to open the field creation form.
* **Name**: `Hectares`
* **API Name**: Qwoty generates `hectares`. Edit if needed.
* **Data Type**: pick `Number`
Click **Save**, then create `Certification level` with type `Text` the same way.
Two new fields appear in the Product object's field list with the **Custom** badge.
Custom fields on Product also appear in the **Products CSV import** mapping. When migrating data from an external system, your CSV columns can populate these fields automatically. See [Import products](/user-guide/data-migration/how-tos/import-products).
## Step 2: Configure the field on the product
Custom fields on Product behave slightly differently from Quote: they're not gated by a template — they appear directly on every product record once enabled.
Navigate to **Catalog & Products → Products** and open any product (or create a new one). The new fields appear in the product's detail view.
Scroll to the **Custom Fields** section. Both fields appear with a toggle and three checkboxes.
Click the toggle for each field to turn it on. The toggle turns dark — the field is now active on this product.
For our agricultural use case, check:
* **Mandatory**: leave **unchecked** for `Hectares` (only relevant on land-related products) and **checked** for `Certification level` (always required for regulatory traceability)
* **Editable by sale** ✓ — sales reps may need to override the value for special orders
* **Display for customer** ✓ — both fields are part of the buyer's commercial context
Enter the values: `Hectares = 0.5` for a per-package fertilizer, `Certification level = Organic` for the eligible products. Repeat per record, or import the values via [CSV](/user-guide/data-migration/how-tos/import-products) if you have many products.
Click **Save**.
Your products carry the new custom fields. They appear on quotes whenever the product is added as a line item.
### What the customer sees
When a sales rep adds the product to a quote and publishes the Dealroom, the buyer sees the certification level alongside the line item. This works without further configuration — as long as **Display for customer** is checked, the field travels with the product.
## Step 3: Sync with your CRM or ERP (optional)
Product custom fields often correspond to attributes already tracked in your ERP (for accounting and inventory) or in your CRM (for sales context).
Go to **Settings → Developers → Integration** and click your connected system (CRM or ERP).
Scroll to the products mapping. The fields appear as `Custom: Hectares` and `Custom: Certification level`.
Open the direction dropdown for each field:
* **CRM → Qwoty** — typical for product master-data: the ERP or PIM is the source of truth, Qwoty receives updates
* **Qwoty → CRM** — when Qwoty is the catalog system of record
* **Bidirectional** — when both systems need to stay in sync edit-for-edit
Pick the field on the connected system. Compatible types:
* Number → Number, Integer, Decimal, Currency
* Text → Text, Picklist, Dropdown
Click **Save changes** at the bottom of the page.
Your product fields now sync between Qwoty and your CRM or ERP. Updates propagate at the next sync cycle.
## Common Product custom fields
Beyond agriculture, here are patterns that come up often:
| Field | Type | Why |
| ------------------------ | ------ | ------------------------------------------------------- |
| **Technical capacity** | Number | Power, throughput, dimensions for industrial equipment |
| **Certification number** | Text | Regulatory ID (CE marking, FDA, REACH) |
| **Manufacturer SKU** | Text | When your SKU differs from the manufacturer's |
| **Lead time (days)** | Number | Days from order to delivery — drives sales-rep promises |
| **Ship class** | Text | Hazardous, fragile, oversized — feeds shipping logic |
| **Min order quantity** | Number | Enforced at quote time |
| **Datasheet URL** | Text | Link to a full product spec PDF |
## Troubleshooting
Refresh the page after creating the field. If still missing, check that the toggle is **on** in the Custom Fields section of the product.
Custom fields on Product travel with the product to the quote, but only if **Display for customer** is checked on the product. Activate the checkbox and republish the quote.
Check that the column header in your CSV matches the field's **API Name** exactly (case-sensitive). See [Import products](/user-guide/data-migration/how-tos/import-products) for the full column reference.
Add the matching field on the CRM side first, then refresh the integration mapping section.
## Related
Pick the right type for your product attribute.
Bulk-populate custom fields via CSV.
Manage your catalog of products.
Sync directions and CRM field compatibility.
# Create a Quote custom field
Source: https://docs.qwoty.io/user-guide/data-model/how-tos/create-quote-custom-field
Add a custom field that flows from a quote template to the buyer's Dealroom and to your CRM.
A Quote custom field is information specific to your sales motion that should appear on every quote — for example, an event date, a project code, or a headcount. You define the field once in the Data model, configure it on each quote template, and optionally sync it to your CRM.
This guide walks through the full lifecycle in three steps.
You must have the **Admin** role with **Data → Read & Write** authorized. CRM mapping additionally requires **Developers** access. See [Manage roles](/user-guide/settings/users/manage-roles).
## What you'll build
In this example, you'll add three custom fields to a quote used by an event production company:
| Field | Type | Purpose |
| -------------------------- | ------ | -------------------------- |
| **Date(s) de l'événement** | Date | When the event happens |
| **Horaire de l'événement** | Text | Start time (e.g., `16:00`) |
| **Nombre de personnes** | Number | Headcount (e.g., `160`) |
All three will be **mandatory**, **editable by sales reps**, and **displayed to the customer** in the Dealroom.
## Step 1: Create the field in the Data model
The Data model is where the field's definition lives. Without a definition, the field doesn't exist anywhere in Qwoty.
Go to **Settings → Data → Data model** and click the **Quote** object (or its parent Template object — the data model groups quote-level custom fields under Template since templates drive quote generation).
In the top right of the field list, click **+ New** to open the field creation form.
* **Name**: enter the human-readable label (for example, `Date(s) de l'événement`)
* **API Name**: Qwoty generates this from the name (for example, `dates_de_l_evenement`). You can edit it if needed — keep it lowercase, no spaces, underscores instead of dashes.
* **Data Type**: pick `Date` (or `Number` for headcount, `Text` for time)
Click **Save** in the top right. The new field appears in the Quote object's field list with the **Custom** badge and **Writable** API access.
Create `Horaire de l'événement` (Text) and `Nombre de personnes` (Number) the same way.
Three custom fields now exist in the Data model. Next, configure how they behave on a quote template.
## Step 2: Configure the field on the template
The field exists, but it doesn't yet appear on any quote. You activate it per template, with three properties that control its behavior.
Navigate to **Templates → Quote templates** and open the template you want to configure (or create a new one — see [Create a quote template](/user-guide/templates/how-tos/create-quote-template)).
Scroll to the **Custom Fields** section of the template. The three fields you just created appear, each with a toggle and three checkboxes.
Click the toggle next to each field name to turn it on. The toggle turns dark — the field is now active on quotes generated from this template.
For each field, check the boxes that match your needs:
* **Mandatory** — required before the quote can be saved or submitted
* **Editable by sale** — sales reps can edit the value (otherwise it's read-only after admin entry)
* **Display for customer** — the field appears on the buyer-facing Dealroom
For our event use case, check **all three** for each field. The customer needs to see and confirm the event date, the time, and the headcount.
Below each field's checkboxes, you can enter a default value that pre-fills new quotes. Leave empty if there's no sensible default.
Click **Save** at the top right of the template editor.
Your three fields are now active on every new quote created from this template. The Custom Fields section shows them with input controls (date picker, text box, number input) ready for sales-rep entry.
### What the customer sees
Once a quote is published to the Dealroom, the customer sees the three fields at the top of the document, each marked with a violet **Display** badge:
```
Nombre de personnes [Display] Date(s) de l'événement [Display] Horaire de l'événement [Display]
160 29/04/2026 16:00
```
The Display badge confirms (in admin preview) that the field is visible to the buyer. The customer doesn't see the badge — they see a clean list of values alongside the line items.
## Step 3: Sync with your CRM (optional)
If you've connected a CRM, you can map each Qwoty custom field to a CRM field. Sync runs in the direction you choose: Bidirectional, CRM → Qwoty, or Qwoty → CRM.
Go to **Settings → Developers → Integration** and click your connected CRM (HubSpot, Salesforce, Pipedrive).
Scroll to the custom fields list. Your three Qwoty fields appear, prefixed with `Custom:` — for example, `Custom: Date(s) de l'événement`, `Custom: Horaire de l'événement`, `Custom: Nombre de personnes`.
Open the direction dropdown and choose one of:
* **Bidirectional** ↔ — changes in either system propagate to the other
* **CRM → Qwoty** — Qwoty receives, but never pushes back
* **Qwoty → CRM** — Qwoty pushes, but ignores changes from the CRM
For event-driven fields, **Qwoty → CRM** is typical: the sales rep fills the value in Qwoty, and it appears in the CRM record for reporting and follow-up.
From the **Select CRM field** dropdown, pick the field on the CRM side that should receive (or send) the value. Only compatible field types are listed:
* Date → Date or Date-time
* Number → Number, Integer, Decimal, or Currency
* Text → Text, Picklist, or Dropdown
Click **Save changes** at the bottom of the page. The new mapping is picked up at the next sync cycle (real-time for most modern CRMs).
Your custom fields now flow between Qwoty and your CRM. Test the sync by editing a value in either app and verifying it appears in the other within a few seconds.
## Troubleshooting
Make sure you saved the field in the Data model. Refresh the template page. If the field still doesn't appear, check that the field's **Type** in the Data model is `Custom` (not `System`).
The **Editable by sale** checkbox is unchecked on the template. Open the template, find the field in the Custom Fields section, check the box, and save.
The **Display for customer** checkbox is unchecked. Activate it on the template and republish the quote — the field appears on the next render of the Dealroom.
Either the CRM doesn't have a compatible field, or the integration's permissions don't grant access to it. Add or expose the field on the CRM side, then refresh the integration.
Click **Save changes** at the bottom of the mapping section — changes aren't applied until you save. Then wait one sync cycle (real-time CRMs propagate within seconds).
## Related
Detailed reference for Mandatory, Editable, Display.
All sync directions and compatibility rules.
Same flow, applied to contract models.
Manage the templates where Quote custom fields are configured.
# Data model
Source: https://docs.qwoty.io/user-guide/data-model/introduction
Learn what a data model is in Qwoty and how to design custom fields that fit your business.
## What is a data model?
A data model is the structure that defines how information is organized in your CPQ. Think of it as the **blueprint** of your sales data — you design it once, then fill it with your actual quotes, products, customers, and contracts.
Qwoty ships with a complete data model out of the box: Quote, Customer, Product, Contract Model, Catalog, Pricebook, and a few dozen other objects. But every business has unique data — an event date on a quote, a hectare count on a customer, a clause variant on a contract. The data model is where you extend Qwoty to carry **your** information.
## Key concepts
### Objects
**Objects** are the main categories of data in Qwoty. Each object represents a type of thing you track.
Qwoty comes with standard objects:
* **Customer** — companies and individuals you sell to
* **Quote** — proposals sent to customers
* **Product** — items in your catalog (with master products and variants)
* **Contract Model** — reusable templates for legal contracts
* **Catalog**, **Pricebook**, **Bundle**, **Discount** — supporting structures for your offer
* **Order**, **Sales Agreement** — what's generated once a quote is signed
* **Approval**, **Workflow** — the rules that govern your sales process
### Fields
**Fields** are the properties that describe each object. They store the actual information.
For example, the **Customer** object has fields like:
* Name
* Address
* Industry
* Linked contacts (a relation to the Contact object)
Fields have different **types**: text, number, date, boolean, and more. You can add **custom fields** to manageable objects (Customer, Quote, Product, Contract Model) to capture data specific to your business.
### Records
**Records** are the individual entries within an object — the actual data your team creates and works with day-to-day.
For example:
* "Acme Corp" is a **record** in the Customer object
* "QUO-2026-0001" is a **record** in the Quote object
**An analogy:**
| Data model concept | Real-world analogy |
| ------------------ | ------------------------------------------ |
| **Objects** | Sections in a book (the categories) |
| **Fields** | Columns in a spreadsheet (the properties) |
| **Records** | Rows in a spreadsheet (the actual entries) |
You design the data model (objects + custom fields) once, then create thousands of records within that structure.
## Why customize your data model?
Every CPQ deployment is different. Customizing your data model means you can shape Qwoty around **your** quote-to-revenue process instead of forcing yours into a rigid system.
**Common examples of custom fields:**
* An **event date** and **headcount** on quotes for an event production company
* A **hectare count** and **crop type** on customers for an agricultural cooperative
* A **legal clause variant** on contracts for a multi-jurisdiction sales operation
* A **certification level** or **regulatory ID** on products for regulated industries
Custom fields are not just labels — they flow through the quote-to-revenue cycle: they appear on quotes and Dealrooms (when configured to display), populate contract variables, and sync to your CRM.
## Tips to design your data model
### 1. Start with the standard objects
Qwoty already provides Customer, Quote, Product, Contract Model. Most businesses cover 80% of their needs by adding a few custom fields to these — without creating new objects.
### 2. Use fields for properties, not new objects
If something is just a characteristic of an existing object, make it a **custom field**.
**Use custom fields for:**
* Categories and labels (for example, `Customer type` on Customer with values "Prospect", "Key Account", "Partner")
* Status values or stages
* Attributes and properties (event date, hectares, certification number)
### 3. Mark your fields' visibility carefully
Each custom field on an object has three properties that decide where the value flows:
* **Mandatory** — the field must be filled on creation
* **Editable by sale** — sales reps can edit the value (otherwise it's read-only after import or admin entry)
* **Display for customer** — the field appears on the buyer-facing quote and Dealroom
A field marked "Display for customer" is part of the buyer experience. A field that's not displayed stays internal — useful for sales-ops or accounting metadata that shouldn't leak to customers.
### 4. Plan for CRM sync upfront
If you connect Qwoty to a CRM (HubSpot, Salesforce, Pipedrive), every custom field can be mapped to a CRM field with three sync directions: **Bidirectional**, **CRM → Qwoty**, or **Qwoty → CRM**. Decide which direction makes sense before creating the field — it shapes how your team enters data.
### 5. Keep it simple first
Start with fields. Add new ones only when you feel the limits — repeated entries, missing data, or a field crowded with too many possible values.
## Questions to guide your choice
Ask yourself:
* Is this just a property of something I already have?
* Will all my customers, quotes, or products carry this information?
* Do I want this field to appear on the quote shown to buyers?
* Should sales reps edit it, or only admins?
* Does it need to sync with my CRM in real time?
If the answer is yes to most, you've found a good candidate for a custom field.
## Accessing your data model
Click **Settings** in the left sidebar.
Under the **Data** section, click **Data model**. You'll see the full list of objects with their **Standard** or **Manageable** badge.
Click any object to see its fields. Manageable objects show a **+ New** button to add custom fields.
Don't see Data model in Settings? Access is restricted to admins with the **Data** permission enabled. Contact your workspace admin if needed — see [Manage roles](/user-guide/settings/users/manage-roles).
## Reference
Concepts and rules that shape how custom fields behave.
Standard vs Manageable objects, and which ones accept custom fields.
Text, Number, Date — what each type is for.
Mandatory, Editable by sale, Display for customer — control where values flow.
Sync directions and how custom fields connect to your CRM.
## How-tos
Step-by-step guides to add custom fields to the four manageable objects.
Add fields shown on quotes — event dates, project codes, headcounts.
Add fields used as contract variables — clauses, addenda, jurisdictions.
Add fields for catalog metadata — certifications, capacities, attributes.
Add fields to qualify customers — types, codes, regulatory IDs.
# CRM mapping
Source: https://docs.qwoty.io/user-guide/data-model/reference/crm-mapping
Sync custom fields between Qwoty and your connected CRM with Bidirectional, CRM → Qwoty, or Qwoty → CRM.
If you've connected Qwoty to a CRM (HubSpot, Salesforce, Pipedrive), every custom field on a manageable object can be mapped to a CRM field. Mapping decides which direction data flows and which CRM field receives or sends the value.
CRM mapping requires an active CRM integration. Connect your CRM first via **Settings → Developers → Integration → CRM name**, then return to map custom fields.
## Where mapping lives
Custom field mapping is configured per-CRM, in the integration settings:
Click **Settings** in the left sidebar.
Under the **Developers** section, click **Integration**, then click your connected CRM (HubSpot, Salesforce, Pipedrive, or others).
Scroll to the custom fields mapping area. Each Qwoty custom field on the synced object is listed, prefixed with `Custom:` to distinguish from system fields.
## Sync directions
For each custom field, you choose one of three sync directions:
| Direction | What it does | Use it when |
| ------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| **Bidirectional** ↔ | Changes in either system propagate to the other | The field is owned by both systems and your team edits it in either |
| **CRM → Qwoty** | Qwoty receives updates from the CRM, but never sends back | The CRM is the source of truth (e.g., customer industry from HubSpot) |
| **Qwoty → CRM** | Qwoty pushes updates to the CRM, but ignores changes coming from it | Qwoty is the source of truth (e.g., quote-driven event dates that should appear in the CRM) |
When in doubt, start with **CRM → Qwoty** (one-way pull). It's the safest setting because Qwoty never overwrites CRM data. Move to Bidirectional only when your team wants to edit the value in both apps without thinking about it.
## Selecting the CRM field
Each custom field row has two dropdowns:
* **Direction dropdown** — pick `Bidirectional`, `CRM → Qwoty`, or `Qwoty → CRM`
* **Select CRM field** — pick the matching field on the CRM side (only fields of compatible types are listed)
Compatible types follow these rules:
| Qwoty type | Compatible CRM types |
| ---------- | --------------------------------------------------------- |
| **Text** | Single-line text, multi-line text, picklist, dropdown |
| **Number** | Number, integer, decimal, currency (numeric portion only) |
| **Date** | Date, date-time |
If the dropdown is empty or doesn't show the field you expect, check that the CRM field exists, that its type is compatible, and that the integration's permissions include read/write access to the field.
## Sync frequency and timing
How often Qwoty syncs depends on the integration:
* **Real-time webhooks** for most modern CRMs (HubSpot, Salesforce) — changes propagate within seconds
* **Periodic polling** as a fallback when webhooks aren't available or fail
After saving a mapping, the next sync cycle picks up the new configuration. Existing records start receiving (or sending) values according to the new direction at the next sync.
## When the CRM field is missing values
If a CRM record doesn't have a value for the mapped field, Qwoty receives `null` and stores nothing — the custom field shows empty on the record. The reverse is also true: if Qwoty has no value and the direction includes `→ CRM`, the CRM field is set to its empty state.
## Save changes
Click **Save changes** at the bottom of the mapping section to apply your configuration. The new mappings are picked up at the next sync. You can adjust mappings any time without breaking historical data — past values stay intact.
## Common mapping patterns
| Custom field | Direction | Why |
| ------------------------------- | ----------------- | -------------------------------------------------------------------- |
| Customer's industry | **CRM → Qwoty** | Sales reps maintain the source of truth in the CRM |
| Event date (Quote custom field) | **Qwoty → CRM** | Set during quoting, exposed to CRM for reporting |
| Account manager | **Bidirectional** | Either app can update the assignment, both should reflect the change |
| Internal sales notes | (no mapping) | Stays in Qwoty only — not synced |
Not every custom field needs a mapping. Leave the **Select CRM field** dropdown empty to keep the field local to Qwoty.
## Related
Mandatory, Editable, Display — properties that work alongside CRM mapping.
Type compatibility between Qwoty and CRM fields.
Connect HubSpot, Salesforce, or Pipedrive.
The most common case for CRM-synced custom fields.
# Field properties
Source: https://docs.qwoty.io/user-guide/data-model/reference/field-properties
Mandatory, Editable by sale, and Display for customer — control where custom field values flow.
When you create a custom field in **Settings → Data → Data model**, you define its name, API name, and data type. But the field's **behavior** on each object — whether it's required, who can edit it, who sees it — is configured separately, on the object itself.
This page explains the three properties Qwoty offers and how to combine them for the most common patterns.
## The Custom Fields section on each object
Once a custom field exists in the Data model, it appears in the **Custom Fields** section of every record of the matching object (or the relevant Template, for Quote). For each field, you see:
* A **toggle** to activate or deactivate the field on this object
* Three checkboxes: **Mandatory**, **Editable by sale**, **Display for customer**
* The field's input area (text box, number, or date picker) for entering the value
If a custom field doesn't appear on the object after creation, check that its toggle is **on** in the Custom Fields section. Disabling the toggle hides the field from forms while keeping its definition intact.
## The three properties
### Mandatory
When enabled, the field **must be filled** before saving the record (or, for Template-driven fields, before the quote can be submitted).
**Use Mandatory when:**
* Missing the value would block downstream processes (for example, a `Delivery date` required by your warehouse)
* Compliance or contract generation depends on it (a `Jurisdiction` field for legal templates)
* Your reporting needs the field populated to produce useful KPIs
**Don't use Mandatory when:**
* The field only applies to some records (a `Hectares` field that's relevant for agricultural customers but not retail customers)
* The value is filled in later in the deal cycle (a `Signed date` you can only fill at signature)
### Editable by sale
When enabled, sales reps can modify the field's value. When disabled, the field is **read-only** for sales reps — typically populated by an admin, an integration, or an import.
**Use "Editable by sale" when:**
* Sales reps own the value (an `Event date` they collect from the customer)
* The field is part of the sales-rep workflow
**Disable "Editable by sale" when:**
* The value comes from your CRM and shouldn't be overwritten by mistake
* Only admins should set the value (a `Discount tier` linked to negotiated commercial terms)
* The value is calculated by an integration
"Editable by sale" controls the **sales rep** experience. Admins can always edit custom fields regardless of this setting.
### Display for customer
When enabled, the field appears on the buyer-facing quote and Dealroom. The customer sees the field's label and value alongside the line items, dates, and totals.
**Use "Display for customer" when:**
* The buyer needs the value to make a decision (an `Event date` so they can confirm scheduling)
* The value is part of the commercial terms (`Headcount`, `Delivery window`)
* The information is meant to be transparent
**Don't use "Display for customer" when:**
* The field is internal metadata (an `Account manager code`, `Sales region`)
* It's used only for reporting or accounting (a `Ledger code`)
* Sharing the value would expose internal pricing logic
## Common patterns
These combinations cover most setups:
| Pattern | Mandatory | Editable by sale | Display for customer | Example |
| ---------------------------- | --------- | ---------------- | -------------------- | ---------------------------------------------- |
| **Customer-facing required** | ✓ | ✓ | ✓ | Event date on a quote |
| **Customer-facing optional** | — | ✓ | ✓ | Special instructions |
| **Internal sales note** | — | ✓ | — | Sales rep's deal context |
| **Admin-controlled visible** | — | — | ✓ | Negotiated discount tier shown to the customer |
| **Admin-only metadata** | — | — | — | Account manager code, ledger account |
| **CRM-synced read-only** | depends | — | depends | Customer industry pulled from HubSpot |
## How a field looks to each persona
The three properties shape what each persona sees:
* **The admin** — sees and edits every custom field, regardless of properties
* **The sales rep** — sees every active field; can edit only those with **Editable by sale** ✓
* **The customer** — sees only fields with **Display for customer** ✓ on the published Dealroom
A custom field can carry information that flows through all three personas, or stay internal. The combination is yours to design.
## Display badge on the customer view
When a field is set to **Display for customer**, it shows up on the Dealroom with a violet **Display** badge (visible in admin preview mode). This badge confirms that the field will be visible to the buyer when the quote is published.
The Display badge only appears in the admin preview to remind your team which fields are exposed. The customer doesn't see the badge — they see the field's label and value styled like the rest of the quote.
## Editing properties later
You can change any of the three properties at any time. Changes take effect for **new** records and the **published** state of existing records:
* Toggling **Mandatory** on doesn't retroactively block existing records — only saves and submits going forward
* Toggling **Display for customer** on republishes the field to existing Dealrooms the next time the quote is updated
* Toggling **Editable by sale** off immediately makes the field read-only for sales reps
Disabling the field's toggle entirely (in the Custom Fields section) hides the field from all forms but **preserves stored values**. Re-enabling restores the field with the values intact.
## Related
Text, Number, Date — the three data types available.
Configure sync direction and CRM field for each custom field.
Practical example combining all three properties.
Who can edit the data model and Object fields.
# Field types
Source: https://docs.qwoty.io/user-guide/data-model/reference/field-types
The three data types available for custom fields in Qwoty.
When you add a custom field to a manageable object, you pick a **Data Type**. Qwoty currently supports three types — chosen to cover the vast majority of CPQ use cases without complicating the data model.
## Available types
| Type | Icon | Use it for | Example values |
| ---------- | ---- | --------------------------------------------------------------------- | ---------------------------------------------------- |
| **Text** | `T` | Free-form strings, codes, descriptions, dropdowns implemented as text | `Premium`, `B2B`, `EUR-FRA`, `Long descriptive note` |
| **Number** | `#` | Quantities, scores, dimensions, anything numeric | `160`, `4.5`, `2026`, `99.99` |
| **Date** | `📅` | Dates, deadlines, anniversaries | `2026-04-25`, `29/04/2026` |
## Text
Use Text fields for any string value: a customer type label, a project code, a free-form note. Text covers a wide range of needs because Qwoty doesn't enforce a value list — your team enters whatever makes sense for your process.
**Common Text use cases:**
* A `Customer type` field with values like `Prospect`, `Key Account`, `Partner`
* A `Project code` referencing your internal project tracker
* A `Notes` field for sales-rep context
* An `External reference` matching your ERP
**What Text accepts:**
* Any string up to a generous length limit
* Special characters, accents, line breaks (use double quotes when importing via CSV)
* Empty values (unless **Mandatory** is enabled)
Qwoty doesn't currently offer a native dropdown / select / multi-select type. To restrict values to a fixed set, use Text and document the allowed values in your team's playbook — or rely on your CRM's enum field and sync it via [CRM mapping](/user-guide/data-model/reference/crm-mapping).
## Number
Use Number fields for any numerical value: counts, dimensions, prices, scores. The value is stored as a number — you can compute, sort, and filter on it.
**Common Number use cases:**
* A `Headcount` field (e.g., 160 attendees on an event quote)
* A `Hectares` field on a customer in agriculture
* A `Tier level` field for customer segmentation (1, 2, 3)
* A `Custom discount %` field
**What Number accepts:**
* Integers and decimals (use `.` as the decimal separator)
* Positive and negative values (where the field permits)
* Empty values (unless **Mandatory** is enabled)
**What Number does NOT accept:**
* Currency symbols (`€`, `$`)
* Thousands separators (`1,234.56` is rejected — use `1234.56`)
* Text mixed with numbers (`160 attendees` is rejected — use `160` and put `attendees` in a separate Text field if needed)
## Date
Use Date fields for any date value: an event date, a deadline, an anniversary, an effective-from date.
**Common Date use cases:**
* An `Event date` field on quotes for events
* A `Delivery date` requested by the customer
* A `Contract effective date` on a contract model
* A `Customer onboarding date`
**What Date accepts:**
* ISO 8601 format `YYYY-MM-DD` (recommended)
* Local format `DD/MM/YYYY` (displayed in the UI by locale)
**Display behavior:**
The format displayed in the UI follows the user's locale settings. The value stored internally is always a date — sort, filter, and date-arithmetic operations work consistently regardless of how it's shown.
Date fields don't currently store time — for an event with a specific hour like `16:00`, use a separate **Text** field labeled `Time` (this matches the pattern shown in many production setups).
## Choosing the right type
A few quick rules:
* If you'll **compute or aggregate** values (sum, average, sort numerically) → **Number**
* If you'll **filter by date range** or do date math (e.g., "quotes for events next month") → **Date**
* For **everything else** — labels, codes, descriptions, free-form data → **Text**
When in doubt, start with Text. You can always migrate to a more specific type later by exporting, deleting the field, recreating it with the new type, and re-importing the data with the right format.
## Related
Mandatory, Editable, Display — what these settings do.
Which objects accept custom fields.
Step-by-step example using Text, Number, and Date.
How types map to your CRM's field types.
# Objects
Source: https://docs.qwoty.io/user-guide/data-model/reference/objects
Standard vs Manageable objects, and which ones accept custom fields.
## What is an object?
An **object** is a category of data in Qwoty — a type of thing your team works with. Each object has a fixed schema of system fields plus, for some of them, custom fields you add to match your business.
When you open **Settings → Data → Data model**, you see the full list of objects. Each one carries a badge that tells you whether you can extend it.
## Standard vs Manageable
Qwoty splits objects into two categories:
| Type | What you can do |
| -------------- | ------------------------------------------------------------------------------------------------------ |
| **Standard** | Browse fields only. The schema is locked to keep core Qwoty behavior consistent across all workspaces. |
| **Manageable** | Browse fields **and** add custom fields. The four objects most likely to carry business-specific data. |
### Manageable objects (4)
These are the objects where you can add custom fields:
| Object | Why it's manageable | Common custom fields |
| ------------------------ | ---------------------------------------------------------------------- | ------------------------------------------------------ |
| **Customer** | Every business qualifies customers differently | Customer type, account manager, segment, regulatory ID |
| **Quote** (via Template) | Quotes carry deal-specific data unique to your sales motion | Event date, project code, requested delivery date |
| **Product** | Products differ wildly by industry | Hectares, certification level, technical specs |
| **Contract Model** | Contracts carry clauses and variables specific to your legal templates | Clause variant, addendum, jurisdiction-specific text |
### Standard objects
The remaining objects use a fixed schema. You can browse their fields and integrate with them, but you can't add custom fields directly:
Address, Approval, Billing Frequency, Bundle, Business Unit, Catalog, Contact, Content Block, Customer Segment, Discount, Integration, Order, Payment Method, Payment Term, Pricebook, Sales Agreement, Tax, Template, Workflow.
Even though Quote is shown as a manageable object via its Template, the **Order** that follows a signed quote is Standard. Order data flows from the Quote at signature time — you don't customize the Order schema directly.
## How to know if an object accepts custom fields
Open **Settings → Data → Data model** and look at the badge next to each object's name:
* **Standard** badge → no custom fields can be added
* **Manageable** badge → you can add custom fields via **+ New** at the top of the field list
Inside a manageable object, system fields show a **System** badge in the **Type** column (read-only). Fields you add show a **Custom** badge with **Writable** API access.
## Why some objects are Standard
Standard objects power core Qwoty behavior — pricing, taxation, approvals, ordering. Their schemas are locked because:
* **Consistency** — every workspace handles them the same way, so integrations and exports stay predictable
* **Stability** — adding a field on a core object would risk breaking quoting math, contract generation, or order generation
* **Performance** — system objects are heavily indexed for fast queries; custom fields on them would degrade lookup speed
When you need data on a Standard object, the convention is to put it on the related Manageable object instead. For example: a **Bundle** is Standard, but the **Product** included in that Bundle is Manageable — add the custom field on Product, not Bundle.
## Related
The three data types you can pick when adding a custom field.
Mandatory, Editable, Display — control how a field behaves on each object.
Concepts behind the Qwoty data model.
Control who can edit the data model.
# Why am I or my client not receiving Qwoty email notifications?
Source: https://docs.qwoty.io/user-guide/troubleshooting/Email notifications
Troubleshoot missing quote, DealRoom, and signature emails for both sales reps and clients.
## Overview
Qwoty sends email notifications for several events in the quote lifecycle: when a quote is published to the DealRoom, when a client comments or requests a change, when a document is sent for signature, and when a signature is completed.
If you or your client aren't receiving these emails, the cause is almost always the same: the email was delivered but landed in a spam or junk folder, or it was blocked by a mail server, firewall, or email security filter before it reached the inbox. This applies whether you're the one who sent the quote or the one waiting to receive it.
Email delivery can take a few minutes. Before troubleshooting further, wait 10–15 minutes after the action was triggered (publish, send to signature, etc.).
This guide is split by audience. Find the section that matches your situation.
## Check the quote status first
Before assuming it's a delivery issue, confirm the quote actually triggered an email.
1. Open the quote at `/projects/quotes/:id`.
2. Check its status. An email is only sent once the quote is `Published` (or moved to `Pending signature`). A quote still in `Draft` or `Pending Approval` has not been sent to the client yet.
3. Check the **Activity** or history panel on the quote for a confirmation that the notification was sent.
If the quote is still in `Draft`, that's the cause — nothing has been sent. Submit and publish it first; see [Publish a quote to the DealRoom](/user-guide/dealroom/how-tos/publish-quote).
## Verify the client's email address
A mistyped or outdated address is one of the most common causes of "missing" emails.
1. Go to the customer's record at `/customers/:id`.
2. Confirm the email address on file matches what the client expects to use.
3. If it's incorrect, update it and resend the quote notification from the quote's detail page.
## Ask your client to check spam and allowlist Qwoty
If the address is correct and the quote is published, ask your client to:
* Check their spam or junk folder for an email from Qwoty.
* Mark it as "not spam" if found, so future emails land in the inbox.
* Add Qwoty's sending domains to their safe senders list (see [Allowlist Qwoty's sending domains](#allowlist-qwotys-sending-domains) below).
You can share the **DealRoom link** directly with your client as a workaround — copy it from the quote's detail page using **View in DealRoom** — instead of waiting on the email.
## Still not resolved?
If the address is correct, the quote is published, and your client has checked spam without success, ask your client's IT team to investigate further using the [allowlist section](#allowlist-qwotys-sending-domains) below, or contact Qwoty support.
## Check your spam or junk folder
Email providers and corporate filters sometimes misclassify automated emails, including legitimate ones from Qwoty.
1. Search your spam or junk folder for an email from Qwoty.
2. If you find it, mark it as "not spam" or "not junk" so future notifications land directly in your inbox.
## Wait a short delay
Notifications are usually delivered within a few minutes, but delivery can occasionally take up to a few hours depending on your mail provider's filtering. Wait before assuming the email was lost.
## Ask your IT department to allowlist Qwoty
If you're using a company email address, your IT or security team may be filtering or quarantining the message before it reaches your inbox. Forward them the [list of domains](#allowlist-qwotys-sending-domains) below so they can allow emails from Qwoty.
## Ask the sender for a direct link
If you still can't find the email after checking spam and contacting IT, ask the person who sent you the quote to share the DealRoom link directly (by chat, SMS, or any other channel) instead of relying on email delivery.
## Allowlist Qwoty's sending domains
Qwoty sends notifications from several domains depending on the type of email (quote publication, reminders, signature links). To ensure delivery, allowlist all of the following domains in your email client or ask your IT department to allow them at the mail server level:
```text theme={null}
qwoty.io
qwoty.app
notifications.qwoty.app
link.notifications.qwoty.app
mail.qwoty.app
link.mail.qwoty.app
```
Allowlisting the domain (rather than a single sender address) is more reliable, since the exact sending address can vary by notification type.
## Common questions
Check your notification preferences in [your profile](/user-guide/settings/introduction). If preferences look correct, follow the same spam-folder and allowlist steps described above — your own mail server can filter Qwoty emails the same way a client's can.
Confirm the quote hasn't expired or been canceled — links to quotes that are `Expired`, `Lost`, or `Canceled` no longer open the DealRoom. Check the quote's status at `/projects/quotes/:id`.
Yes. From the quote's detail page, use the resend action to trigger a new notification email to the client without changing the quote itself.
## Related
Step-by-step guide to publishing a quote so your client can view it.
How signature requests are sent and what triggers the notification email.
# Create Category
Source: https://docs.qwoty.io/api-reference/categories/create
POST https://qwoty.app/api/categories
Create a new product category in your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Request Body
Unique API identifier (snake\_case). Can only contain lowercase letters,
numbers and underscores. Cannot start or end with underscore.
Category name
Category description
Active status
UUID of parent category for hierarchical organization
## Examples
```bash cURL theme={null}
curl -X POST https://qwoty.app/api/categories \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"api_name": "electronics",
"name": "Electronics",
"description": "Electronic products and accessories",
"is_active": true
}'
```
```bash With Parent theme={null}
curl -X POST https://qwoty.app/api/categories \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"api_name": "smartphones",
"name": "Smartphones",
"description": "Mobile phones and accessories",
"is_active": true,
"parent_category_id": "550e8400-e29b-41d4-a716-446655440001"
}'
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/categories', {
method: 'POST',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
api_name: 'electronics',
name: 'Electronics',
description: 'Electronic products and accessories',
is_active: true,
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.post(
'https://qwoty.app/api/categories',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'api_name': 'electronics',
'name': 'Electronics',
'description': 'Electronic products and accessories',
'is_active': True
}
)
data = response.json()
```
## Response
Indicates whether the request was successful
Created category object
Unique identifier (UUID)
Workspace ID (UUID)
Category name
API identifier
Category description
Active status
ID of parent category (UUID)
Creation timestamp (ISO 8601)
Last update timestamp (ISO 8601)
```json 201 Created theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Electronics",
"api_name": "electronics",
"description": "Electronic products and accessories",
"is_active": true,
"parent_category_id": null,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z"
}
}
```
## Error Responses
```json 400 Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": [
"Field 'api_name' is required"
]
}
```
```json 400 Invalid api_name theme={null}
{
"success": false,
"error": "Validation error",
"details": [
"Field 'api_name' can only contain lowercase letters, numbers and underscores (cannot start or end with underscore)"
]
}
```
# Delete Category
Source: https://docs.qwoty.io/api-reference/categories/delete
DELETE https://qwoty.app/api/categories/{id}
Soft delete a product category
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
Category UUID
## Behavior
This endpoint performs a **soft delete** by setting the `deleted_at` timestamp on the category. The category will no longer appear in standard API queries but is not permanently removed from the database.
Deletion is **blocked** if the category is referenced by active discounts. In
that case, you will receive a `409 Conflict` error. You must first remove or
update the discount references before deleting the category.
## Examples
```bash cURL theme={null}
curl -X DELETE https://qwoty.app/api/categories/550e8400-e29b-41d4-a716-446655440001 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const categoryId = '550e8400-e29b-41d4-a716-446655440001'
const response = await fetch(`https://qwoty.app/api/categories/${categoryId}`, {
method: 'DELETE',
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
category_id = '550e8400-e29b-41d4-a716-446655440001'
response = requests.delete(
f'https://qwoty.app/api/categories/{category_id}',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates whether the request was successful
Deletion confirmation
UUID of the deleted category
Always `true` when deletion succeeds
```json 200 Success theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"deleted": true
}
}
```
## Error Responses
```json 404 Not Found theme={null}
{
"success": false,
"error": "Category not found"
}
```
```json 409 Conflict - Referenced by Discounts theme={null}
{
"success": false,
"error": "CATEGORY_REFERENCED_IN_DISCOUNT"
}
```
## Cascade Effects
When a category is deleted:
* Products linked to this category through `category_ids` retain the reference (soft delete doesn't cascade)
* Child categories (with `parent_category_id` pointing to this category) are **not** automatically deleted
* Price percentage rules scoped to this category remain intact
To permanently remove all traces of a category, you should first: 1. Remove
the category from all products (`category_ids` field) 2. Update or delete all
discounts referencing this category 3. Reassign or delete child categories 4.
Then delete the category
# Get Category
Source: https://docs.qwoty.io/api-reference/categories/get
GET https://qwoty.app/api/categories/{id}
Retrieve a single product category by its ID
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
Category UUID
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/categories/550e8400-e29b-41d4-a716-446655440001 \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const categoryId = '550e8400-e29b-41d4-a716-446655440001'
const response = await fetch(`https://qwoty.app/api/categories/${categoryId}`, {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
category_id = '550e8400-e29b-41d4-a716-446655440001'
response = requests.get(
f'https://qwoty.app/api/categories/{category_id}',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates whether the request was successful
Category object
Unique identifier (UUID)
Workspace ID (UUID)
Category name
API identifier (unique, immutable)
Category description
Active status
ID of parent category (UUID)
Creation timestamp (ISO 8601)
Last update timestamp (ISO 8601)
```json Response theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Electronics",
"api_name": "electronics",
"description": "Electronic products and accessories",
"is_active": true,
"parent_category_id": null,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z"
}
}
```
## Error Responses
```json 404 Not Found theme={null}
{
"success": false,
"error": "Category not found"
}
```
# List Categories
Source: https://docs.qwoty.io/api-reference/categories/list
GET https://qwoty.app/api/categories
Retrieve all product categories in your workspace
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Examples
```bash cURL theme={null}
curl https://qwoty.app/api/categories \
-H "Authorization: Bearer qwoty_your_token"
```
```javascript JavaScript theme={null}
const response = await fetch('https://qwoty.app/api/categories', {
headers: {
Authorization: 'Bearer qwoty_your_token',
},
})
const data = await response.json()
```
```python Python theme={null}
import requests
response = requests.get(
'https://qwoty.app/api/categories',
headers={
'Authorization': 'Bearer qwoty_your_token'
}
)
data = response.json()
```
## Response
Indicates whether the request was successful
Array of category objects
Unique identifier (UUID)
Workspace ID (UUID)
Category name
API identifier (unique, immutable)
Category description
Active status
ID of parent category for hierarchical organization (UUID)
Creation timestamp (ISO 8601)
Last update timestamp (ISO 8601)
```json Response theme={null}
{
"success": true,
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Electronics",
"api_name": "electronics",
"description": "Electronic products and accessories",
"is_active": true,
"parent_category_id": null,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:00:00Z"
},
{
"id": "550e8400-e29b-41d4-a716-446655440002",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Smartphones",
"api_name": "smartphones",
"description": "Mobile phones and accessories",
"is_active": true,
"parent_category_id": "550e8400-e29b-41d4-a716-446655440001",
"created_at": "2026-01-16T14:30:00Z",
"updated_at": "2026-01-16T14:30:00Z"
}
]
}
```
# Update Category
Source: https://docs.qwoty.io/api-reference/categories/update
PATCH https://qwoty.app/api/categories/{id}
Update an existing product category
## Authorization
Bearer token for authentication. Format: `Bearer qwoty_your_token`
## Path Parameters
Category UUID
## Request Body
All fields are optional. Only provided fields will be updated.
Category name
Category description
Active status
UUID of parent category for hierarchical organization. Set to `null` to remove
parent.
The `api_name` field is immutable and cannot be updated after creation.
## Examples
```bash cURL theme={null}
curl -X PATCH https://qwoty.app/api/categories/550e8400-e29b-41d4-a716-446655440001 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Updated Electronics",
"description": "Updated description for electronics",
"is_active": false
}'
```
```bash Update Parent theme={null}
curl -X PATCH https://qwoty.app/api/categories/550e8400-e29b-41d4-a716-446655440002 \
-H "Authorization: Bearer qwoty_your_token" \
-H "Content-Type: application/json" \
-d '{
"parent_category_id": "550e8400-e29b-41d4-a716-446655440001"
}'
```
```javascript JavaScript theme={null}
const categoryId = '550e8400-e29b-41d4-a716-446655440001'
const response = await fetch(`https://qwoty.app/api/categories/${categoryId}`, {
method: 'PATCH',
headers: {
Authorization: 'Bearer qwoty_your_token',
'Content-Type': 'application/json',
},
body: JSON.stringify({
name: 'Updated Electronics',
description: 'Updated description for electronics',
is_active: false,
}),
})
const data = await response.json()
```
```python Python theme={null}
import requests
category_id = '550e8400-e29b-41d4-a716-446655440001'
response = requests.patch(
f'https://qwoty.app/api/categories/{category_id}',
headers={
'Authorization': 'Bearer qwoty_your_token',
'Content-Type': 'application/json'
},
json={
'name': 'Updated Electronics',
'description': 'Updated description for electronics',
'is_active': False
}
)
data = response.json()
```
## Response
Indicates whether the request was successful
Updated category object
Unique identifier (UUID)
Workspace ID (UUID)
Category name
API identifier (immutable)
Category description
Active status
ID of parent category (UUID)
Creation timestamp (ISO 8601)
Last update timestamp (ISO 8601)
```json 200 Success theme={null}
{
"success": true,
"data": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"workspace_id": "660e8400-e29b-41d4-a716-446655440000",
"name": "Updated Electronics",
"api_name": "electronics",
"description": "Updated description for electronics",
"is_active": false,
"parent_category_id": null,
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-06-03T14:30:00Z"
}
}
```
## Error Responses
```json 404 Not Found theme={null}
{
"success": false,
"error": "Category not found"
}
```
```json 400 Validation Error theme={null}
{
"success": false,
"error": "Validation error",
"details": ["Field 'api_name' is required when creating a category"]
}
```