OQL Β· Entities
Deals
OQL field reference for deals. Sales opportunities linked to contacts, tracked through pipeline stages.
Last updated Jul 16, 2026
Deals are sales opportunities linked to contacts and tracked through pipeline stages. The financial fields are aggregatable, which makes deals the natural entity for revenue reporting.
Fields
Legend: F filterable, S sortable, A aggregatable, G groupable.
| Field | Type | F | S | A | G | Description |
|---|---|---|---|---|---|---|
id | ID | Y | β | β | β | Deal ID |
name | string | Y | Y | β | β | Deal name |
text | string | β | β | β | β | Deal description / notes (free text; writable via create/update) |
contact_id | ID | Y | β | β | Y | Associated contact ID |
owner_id | ID | Y | β | β | Y | Deal owner user ID |
status | string | Y | Y | β | Y | One of pending, won, lost |
amount | number | Y | Y | Y | β | Deal value |
total_amount | number | Y | Y | Y | β | Total amount including recurring months |
cost | number | Y | Y | Y | β | Deal cost (if cost tracking is enabled) |
total_cost | number | Y | Y | Y | β | Total cost including recurring months |
margin | number | Y | Y | Y | β | Profit margin (amount - cost) |
commission | number | Y | Y | Y | β | Commission amount |
commission_percentage | number | Y | β | Y | β | Commission percentage |
commission_type | string | Y | β | β | Y | How commission is expressed: none, percentage, or absolute |
commission_base | string | Y | β | β | Y | What percentage commission is calculated from: amount or margin |
pipeline_id | ID | Y | β | β | Y | Pipeline ID |
stage | number | Y | Y | β | Y | Pipeline stage number. Live for pending deals only. |
last_stage | number (virtual) | Y | Y | β | β | Final stage for closed deals (won, lost, and every delivery-pipeline deal) |
expected_close_date | date | Y | Y | β | Y | Expected close date (when status is pending) |
close_date | date | Y | Y | β | Y | Actual close date (when status is won or lost) |
months | number | Y | β | β | β | Number of recurring months |
reason_lost | string (output only) | β | β | β | β | Reason lost display name |
reason_lost_id | ID | Y | β | β | Y | Reason lost ID |
has_deal_items | boolean | Y | β | β | β | Whether the deal has line items |
archived | boolean | Y | β | β | Y | Whether the deal is archived |
created_at | time | Y | Y | β | Y | Record creation timestamp |
modified_at | time | Y | Y | β | Y | Last modification timestamp |
Notes
stagevslast_stagemap to the same underlying value but are scoped by deal status. Pending deals exposestage; closed deals (won, lost, and every deal in a delivery pipeline) exposelast_stage. The non-applicable one isnullin projections, and filtering bystageimplicitly restricts the result set to pending deals.- Stage values are integers but not sequential. A typical pipeline
uses values like
10,20,40. Stage numbers and their labels are account-configurable. - Sales vs delivery pipelines. A pipeline is either a sales
pipeline (deals run pending β won/lost) or a delivery pipeline
(post-sales project tracking, where every deal is won and the stages
track delivery progress). Deals in a delivery pipeline are always
won, so their position lives inlast_stage, notstage. Usecontext()to see each pipelineβs type. - Financial fields (
amount,total_amount,cost,total_cost,margin,commission,commission_percentage) are all aggregatable and valid as the argument tosum,avg,min,max,median, andpercentile. close_dateis writable via the MCPcreate/updatetools, but only on won or lost deals (or together withstatus: "won"/"lost"in the same call). Pending deals have no close date; setting it on one is rejected with a hint to useexpected_close_dateinstead.- Lookups:
contact(viacontact_id). Pull the contactβs fields inline ascontact.<field>β for examplecontact.last_name,contact.company, orcontact.country_codeβ inselect,where, andorder_by. See Concepts βΊ Lookups. - Custom fields are queryable by name as
custom_fields.<name>inselect,where, andorder_by; selectcustom_fields.*(or*) to return all of them. Filterability and sortability depend on the fieldβs type. Numeric custom fields can also be aggregated ({ "sum": ["custom_fields.<name>"] }), andmin/maxwork on date custom fields.group_byanddistincton custom fields are not supported: group by a top-level field and filter on the custom field inwhereinstead. Usedescribeto discover an accountβs custom-field names and types.
Example queries
Top 25 open deals by amount, each with its contactβs company (a lookup):
{
"from": "deals",
"select": ["name", "amount", "contact.company"],
"where": { "status": "pending" },
"order_by": [{ "amount": "desc" }],
"limit": 25
}
Deals I own that closed last quarter:
{
"from": "deals",
"where": {
"owner_id": "ME()",
"status": "won",
"close_date": "LAST_QUARTER()"
}
}
Won revenue this quarter, by owner:
{
"from": "deals",
"select": ["owner_id", "count()", { "sum": ["amount"] }],
"where": { "status": "won", "close_date": "THIS_QUARTER()" },
"group_by": ["owner_id"]
}
Monthly closed-won trend over the last year:
{
"from": "deals",
"select": ["close_date", "count()", { "sum": ["amount"] }],
"where": {
"status": "won",
"close_date": { ">=": { "DAYS_AGO": [365] } }
},
"group_by": [{ "MONTH": ["close_date"] }]
}
Stuck deals (in pipeline more than 90 days):
{
"from": "deals",
"where": {
"status": "pending",
"created_at": { "<": { "DAYS_AGO": [90] } }
},
"order_by": [{ "amount": "desc" }]
}