Quotes
Manage quotes (CPQ). A quote is attached to a deal and/or company and contains line items built from catalog products or entered as custom line items.
Financial fields
totalAmountis the total contract value (TCV) over the contract term (termMonths) in the quote'scurrency.mrr/arrare the recurring revenue portions of the quote.totalAmountInWorkspaceCurrency,mrrInWorkspaceCurrency, andarrInWorkspaceCurrencyare derived server-side from the quote's currency using exchange rates — do not set them in requests.
Returns quotes the user has access to. Filter by workspace using the where parameter: {"workspaceId": "<WORKSPACE_UUID>"}, or narrow further, e.g. {"workspaceId": "<WORKSPACE_UUID>", "status": "accepted"}.
Getting the quotes for a deal
Filter by dealId to fetch the quote(s) attached to a specific deal — for example after receiving a deal webhook:
GET /api/quotes?where={"workspaceId": "<WORKSPACE_UUID>", "dealId": "<DEAL_UUID>"}The same lookup also works from the deal side via relation expansion: GET /api/deals/{dealId}?fields=id,quotes.id embeds the deal's quote IDs (or quotes.* for full quote objects).
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Comma-separated list of fields to return. Defaults to all fields.
Supports relation expansion with dot-notation: a path like company.name embeds the named field of the related record, and quotes.id embeds the id of each related quote. Use .* to embed full related objects, e.g. quotes.*. Related records are returned nested on the response — as an object for to-one relations (e.g. company) or an array for to-many relations (e.g. quotes). Archived related records are excluded.
Example: GET /api/deals/{dealId}?fields=id,name,quotes.id returns the deal with a quotes array of {"id": "<QUOTE_UUID>"} entries.
id,name,domainJSON-encoded filter object. All top-level conditions are combined with AND logic. Use $or for OR logic.
The available operators depend on the data type of the field you are filtering on.
String fields
Fields like name, domain, description, linkedin, source, externalId, location.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"name": "Linear"} |
$eq | Explicit exact match | {"name": {"$eq": "Linear"}} |
$not | Not equal | {"source": {"$not": "import"}} |
$in | Matches any value in array | {"domain": {"$in": ["linear.app", "granola.so"]}} |
$notIn | Matches none of the values | {"source": {"$notIn": ["import", "api"]}} |
$contains | Case-insensitive word-boundary substring match | {"name": {"$contains": "YC"}} |
$notContains | Does not contain | {"name": {"$notContains": "Test"}} |
$containsAny | Contains any of the given strings | {"name": {"$containsAny": ["YC", "Techstars"]}} |
$startsWith | Starts with prefix | {"domain": {"$startsWith": "app."}} |
$endsWith | Ends with suffix | {"email": {"$endsWith": "@zero.inc"}} |
$exists | Field is present and truthy | {"linkedin": {"$exists": true}} |
$notExists | Field is absent, null, or empty | {"linkedin": {"$notExists": true}} |
Number fields
Fields like value, confidence.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"value": 5000} |
$eq | Explicit exact match | {"value": {"$eq": 5000}} |
$not | Not equal | {"value": {"$not": 0}} |
$gt | Greater than | {"value": {"$gt": 10000}} |
$gte | Greater than or equal | {"value": {"$gte": 5000}} |
$lt | Less than | {"value": {"$lt": 10000}} |
$lte | Less than or equal | {"value": {"$lte": 50000}} |
$in | Matches any value in array | {"confidence": {"$in": [0.25, 0.5, 0.75]}} |
$notIn | Matches none of the values | {"confidence": {"$notIn": [0, 1]}} |
$exists | Field is present and truthy | {"value": {"$exists": true}} |
$notExists | Field is absent, null, or zero | {"value": {"$notExists": true}} |
Multiple operators can be combined on one field:
{"value": {"$gte": 5000, "$lt": 10000}}
Date fields
Fields like closeDate, startDate, endDate, createdAt, updatedAt.
Values can be ISO 8601 strings ("2026-01-01", "2026-01-01T00:00:00Z") or relative time macros.
Relative time macros: +Nd / -Nd (days), +Nw / -Nw (weeks), +Nm / -Nm (months), +Ny / -Ny (years), +Nh / -Nh (hours), +Ns / -Ns (seconds), now().
| Operator | Description | Example |
|---|---|---|
$gte | On or after | {"closeDate": {"$gte": "2026-01-01"}} |
$lte | On or before | {"closeDate": {"$lte": "2026-03-31"}} |
$gt | After | {"createdAt": {"$gt": "2026-01-01T00:00:00Z"}} |
$lt | Before | {"createdAt": {"$lt": "now()"}} |
$date | Exact date match (compares date portion only) | {"closeDate": {"$date": "2026-01-15"}} |
$exists | Field is present and truthy | {"closeDate": {"$exists": true}} |
$notExists | Field is absent or null | {"closeDate": {"$notExists": true}} |
Date range example:
{"closeDate": {"$gte": "2026-01-01", "$lte": "2026-03-31"}}
Relative date example (closing in next 30 days):
{"closeDate": {"$gte": "now()", "$lte": "+30d"}}
Array fields
Fields like listIds, ownerIds, contactIds.
| Operator | Description | Example |
|---|---|---|
$includes | Array contains the given value (use this — bare exact match is not supported) | {"listIds": {"$includes": "<LIST_UUID>"}} |
$notIncludes | Array does not contain the given value | {"ownerIds": {"$notIncludes": "<USER_UUID>"}} |
$overlaps | Array contains at least one of the given values | {"ownerIds": {"$overlaps": ["<UUID_1>", "<UUID_2>"]}} |
$notOverlaps | Array contains none of the given values | {"listIds": {"$notOverlaps": ["<UUID_1>", "<UUID_2>"]}} |
$all | Array contains all of the given values | {"listIds": {"$all": ["<UUID_1>", "<UUID_2>"]}} |
$length | Filter by array length (supports nested operators) | {"contactIds": {"$length": {"$gte": 2}}} |
$exists | Field is present and non-empty | {"ownerIds": {"$exists": true}} |
$notExists | Field is absent or empty | {"ownerIds": {"$notExists": true}} |
UUID / ID fields
Fields like id, workspaceId, companyId, stage.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"stage": "<PIPELINE_STAGE_UUID>"} |
$in | Matches any value in array | {"stage": {"$in": ["<UUID_1>", "<UUID_2>"]}} |
$notIn | Matches none of the values | {"stage": {"$notIn": ["<UUID_1>"]}} |
$not | Not equal | {"stage": {"$not": "<UUID>"}} |
Boolean fields
Fields like archived.
| Operator | Description | Example |
|---|---|---|
| (exact) | Exact match | {"archived": false} |
$not | Not equal | {"archived": {"$not": true}} |
Logical operators
These work across all data types.
$or — matches if any sub-condition is true:
{
"workspaceId": "<WORKSPACE_UUID>",
"$or": [
{"ownerIds": {"$overlaps": ["<USER_UUID_1>", "<USER_UUID_2>"]}},
{"closeDate": {"$gte": "2026-01-01"}}
]
}
$and — matches if all sub-conditions are true (useful when you need multiple conditions on the same field):
{
"$and": [
{"name": {"$contains": "Enterprise"}},
{"name": {"$notContains": "Test"}}
]
}
Dot-notation (relation filtering)
Use dot-syntax to filter records based on properties of related objects:
{"company.name": "Linear"}
{"company.domain": {"$in": ["linear.app", "granola.so"]}}
{"company.location.city": "San Francisco"}
{"companyProfile.categories": {"$overlaps": ["Sales", "Marketing"]}}
All operators available for the target field's data type can be used with dot-notation.
Custom property filtering
Custom properties are stored in the custom object on records, keyed by UUID. Use GET /api/columns to discover the available custom property IDs, types, and options for a workspace.
Filter on custom properties using custom.<COLUMN_ID> with operators appropriate for the column's type.
Note: For
selectandmultiselectcolumns, option values are UUIDs (thekeyfield from the column'soptionsarray). Use the UUID, not the human-readable name.
// Text custom property
{"custom.54e1ca7d-69c3-4b77-8266-8085b5834116": {"$contains": "enterprise"}}
// Select custom property (use the option key UUID)
{"custom.a1b2c3d4-e5f6-7890-abcd-ef1234567890": "3e839b5c-b311-4887-b2da-727d2d75cdd6"}
// Multi-select custom property (use option key UUIDs)
{"custom.b2c3d4e5-f6a7-8901-bcde-f12345678901": {"$overlaps": ["a1b1c1d1-e1f1-1111-aaaa-111111111111", "b2b2c2d2-e2f2-2222-bbbb-222222222222"]}}
// Currency/number custom property
{"custom.c3d4e5f6-a7b8-9012-cdef-123456789012": {"$gte": 100000}}
// Date custom property
{"custom.d4e5f6a7-b8c9-0123-defa-234567890123": {"$gte": "2026-01-01"}}
// Boolean custom property
{"custom.e5f6a7b8-c9d0-1234-efab-345678901234": true}
// Check if custom property has a value
{"custom.f6a7b8c9-d0e1-2345-fabc-456789012345": {"$exists": true}}
Complex example
All top-level keys are ANDed together:
{
"name": {"$contains": "Zero"},
"location.city": "Helsinki",
"location.country": {"$in": ["United Kingdom", "Germany", "Sweden"]},
"value": {"$gte": 10000},
"closeDate": {"$gte": "2026-01-01", "$lte": "2026-03-31"},
"ownerIds": {"$includes": "<USER_UUID>"},
"stage": {"$in": ["<UUID_1>", "<UUID_2>"]},
"lastActivity": {"$exists": true},
"companyProfile.categories": {"$overlaps": ["Sales", "Marketing"]},
"custom.54e1ca7d-69c3-4b77-8266-8085b5834116": {"$contains": "enterprise"}
}
{"stage":"<PIPELINE_STAGE_UUID>"}Maximum number of records to return
100Pagination offset
0JSON string for sort order
{"name":"asc"}Successful response
Authentication failed or token is invalid/expired
GET /api/quotes HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": [
{
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"companyId": "123e4567-e89b-12d3-a456-426614174000",
"dealId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"status": "draft",
"lineItems": [
{
"productId": "123e4567-e89b-12d3-a456-426614174000",
"priceId": "123e4567-e89b-12d3-a456-426614174000",
"sku": "text",
"name": "text",
"description": "text",
"quantity": 1,
"amount": 1,
"currency": "text",
"interval": "day",
"discountType": "absolute",
"discount": 1,
"totalAmount": 1
}
],
"totalAmount": 1,
"totalAmountInWorkspaceCurrency": 1,
"currency": "text",
"mrr": 1,
"arr": 1,
"mrrInWorkspaceCurrency": 1,
"arrInWorkspaceCurrency": 1,
"termMonths": 1,
"sentAt": "2026-01-01T00:00:00.000Z",
"expiresAt": "2026-01-01",
"acceptedAt": "2026-01-01T00:00:00.000Z",
"rejectedAt": "2026-01-01T00:00:00.000Z",
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000"
}
],
"total": 1
}Create a new quote. Attach it to a record via dealId and/or companyId.
Line items can reference a catalog product price (productId + priceId) or be fully custom (name, amount, currency entered directly). status defaults to draft.
The workspace-currency fields (totalAmountInWorkspaceCurrency, mrrInWorkspaceCurrency, arrInWorkspaceCurrency) are derived server-side — do not send them.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Deal to attach the quote to. Provide dealId and/or companyId.
Company to attach the quote to. Provide dealId and/or companyId.
draftPossible values: Total contract value (TCV) over termMonths in currency.
Quote created successfully
Authentication failed or token is invalid/expired
POST /api/quotes HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 348
{
"workspaceId": "workspace-uuid",
"dealId": "deal-uuid",
"name": "Enterprise annual quote",
"status": "draft",
"currency": "USD",
"termMonths": 12,
"totalAmount": 5000,
"arr": 5000,
"lineItems": [
{
"productId": "product-uuid",
"priceId": "price-uuid",
"name": "Enterprise Plan · Annual",
"quantity": 1,
"amount": 5000,
"currency": "USD",
"interval": "year",
"totalAmount": 5000
}
]
}{
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"companyId": "123e4567-e89b-12d3-a456-426614174000",
"dealId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"status": "draft",
"lineItems": [
{
"productId": "123e4567-e89b-12d3-a456-426614174000",
"priceId": "123e4567-e89b-12d3-a456-426614174000",
"sku": "text",
"name": "text",
"description": "text",
"quantity": 1,
"amount": 1,
"currency": "text",
"interval": "day",
"discountType": "absolute",
"discount": 1,
"totalAmount": 1
}
],
"totalAmount": 1,
"totalAmountInWorkspaceCurrency": 1,
"currency": "text",
"mrr": 1,
"arr": 1,
"mrrInWorkspaceCurrency": 1,
"arrInWorkspaceCurrency": 1,
"termMonths": 1,
"sentAt": "2026-01-01T00:00:00.000Z",
"expiresAt": "2026-01-01",
"acceptedAt": "2026-01-01T00:00:00.000Z",
"rejectedAt": "2026-01-01T00:00:00.000Z",
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000"
},
"sideEffects": []
}Returns a single quote by ID.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Successful response. If the quote does not exist, the API returns HTTP 200 with an empty body ({}).
Authentication failed or token is invalid/expired
GET /api/quotes/{quoteId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"workspaceId": "123e4567-e89b-12d3-a456-426614174000",
"companyId": "123e4567-e89b-12d3-a456-426614174000",
"dealId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"status": "draft",
"lineItems": [
{
"productId": "123e4567-e89b-12d3-a456-426614174000",
"priceId": "123e4567-e89b-12d3-a456-426614174000",
"sku": "text",
"name": "text",
"description": "text",
"quantity": 1,
"amount": 1,
"currency": "text",
"interval": "day",
"discountType": "absolute",
"discount": 1,
"totalAmount": 1
}
],
"totalAmount": 1,
"totalAmountInWorkspaceCurrency": 1,
"currency": "text",
"mrr": 1,
"arr": 1,
"mrrInWorkspaceCurrency": 1,
"arrInWorkspaceCurrency": 1,
"termMonths": 1,
"sentAt": "2026-01-01T00:00:00.000Z",
"expiresAt": "2026-01-01",
"acceptedAt": "2026-01-01T00:00:00.000Z",
"rejectedAt": "2026-01-01T00:00:00.000Z",
"archived": true,
"createdAt": "2026-01-01T00:00:00.000Z",
"updatedAt": "2026-01-01T00:00:00.000Z",
"createdById": "123e4567-e89b-12d3-a456-426614174000"
}
}Delete a quote. Use archive=true for soft delete.
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
If true, soft deletes (archives) the quote instead of permanently deleting it.
falseQuote deleted successfully
Hard delete (archive omitted or false): returns 1 on success.
Soft delete (archive=true): returns a single-element array containing the archived quote object.
1Authentication failed or token is invalid/expired
The requested resource was not found
DELETE /api/quotes/{quoteId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Accept: */*
{
"data": 1,
"sideEffects": []
}Update an existing quote.
lineItems is replaced as a whole — include every line item that should remain, not just the changed ones.
Changing totalAmount, mrr, arr, or currency recomputes the derived workspace-currency fields server-side. To record status transitions, set status together with the matching timestamp (sentAt, acceptedAt, or rejectedAt).
All API requests require a Bearer token in the Authorization header. Create an API key from Workspace Settings → API keys
Only the changed fields are returned in data. Use GET on the record to retrieve the full updated object.
Only the changed fields, not the full object.
Authentication failed or token is invalid/expired
PATCH /api/quotes/{quoteId} HTTP/1.1
Host: api.zero.inc
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
Accept: */*
Content-Length: 602
{
"dealId": "123e4567-e89b-12d3-a456-426614174000",
"companyId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"status": "draft",
"lineItems": [
{
"productId": "123e4567-e89b-12d3-a456-426614174000",
"priceId": "123e4567-e89b-12d3-a456-426614174000",
"name": "text",
"description": "text",
"quantity": 1,
"amount": 1,
"currency": "text",
"interval": "day",
"discountType": "absolute",
"discount": 1,
"totalAmount": 1
}
],
"totalAmount": 1,
"currency": "text",
"mrr": 1,
"arr": 1,
"termMonths": 1,
"sentAt": "2026-01-01T00:00:00.000Z",
"expiresAt": "2026-01-01",
"acceptedAt": "2026-01-01T00:00:00.000Z",
"rejectedAt": "2026-01-01T00:00:00.000Z"
}{
"data": {},
"sideEffects": []
}Last updated

