OQL · Entities
Contacts
OQL field reference for contacts. The people in your CRM and their associated company, communication details, and metadata.
Maintained by the OnePageCRM engineering team · Last updated Aug 27, 2026
Contacts are the people in your CRM and their associated company, communication details, and metadata.
Default sort: weight descending. When you omit order_by,
contacts return in Action Stream priority
order (the same
order the OnePageCRM app uses).
Fields
Legend: F filterable, S sortable, A aggregatable, G groupable.
| Field | Type | F | S | A | G | Description |
|---|---|---|---|---|---|---|
id | ID | Y | Y | — | — | Contact ID |
first_name | string | Y | Y | — | — | First name |
last_name | string | Y | Y | — | — | Last name |
company_name | string | Y | Y | — | — | Company name (display text) |
company_id | ID | Y | — | — | Y | Linked company record ID |
job_title | string | Y | Y | — | — | Job title |
status | string (output only) | — | — | — | — | Status display name. Not queryable — use status_id. |
status_id | string | Y | Y | — | Y | Status system_id (e.g. lead, prospect, customer). Defaults to lead on create. On update, may propagate to every contact in the same company — see note below |
owner_id | ID | Y | — | — | Y | Owner user ID |
lead_source | string (output only) | — | — | — | — | Lead source display name |
lead_source_id | string | Y | Y | — | Y | Lead source system_id |
tags | string[] | Y | — | — | Y | Tag names. On update, may propagate to every contact in the same company — see note below |
starred | boolean (virtual) | Y | — | — | — | Starred by the current user |
pending_deal | boolean | Y | — | — | Y | Whether the contact has at least one pending deal (read-only) |
background | string | — | — | — | — | Background text (select only) |
address | string | Y | — | — | — | Primary street address |
city | string | Y | Y | — | Y | Primary address city |
state | string | Y | Y | — | Y | Primary address state or region |
zip_code | string | Y | — | — | — | Primary address postal code |
country_code | string | Y | Y | — | Y | ISO 3166-1 alpha-2 country code |
address_type | string | Y | — | — | Y | One of work, home, billing, delivery, other |
created_at | time | Y | Y | — | Y | Record creation timestamp |
modified_at | time | Y | Y | — | Y | Last modification timestamp |
last_activity_date | time | Y | Y | — | Y | Most recent activity (note, call, meeting, deal) |
weight | number (virtual) | — | Y | — | — | Action Stream sort weight. Default sort field. |
emails | array | Y | — | — | — | {address, type} objects |
phones | array | Y | — | — | — | {number, type} objects |
urls | array | Y | — | — | — | {url, type} objects |
Notes
- Writing
status_idortagscan change other contacts. If the contact belongs to a company withsync_statusorsync_tagsset, that write applies to every contact in the company, not just the one you addressed. Tags are replaced wholesale rather than merged, so the other contacts’ tags are overwritten. Check the flags oncompaniesbefore writing either field. - Virtual fields (
status,lead_source) are display labels resolved from their underlying_idfield at query time. They are not queryable at all: you cannot select, filter, sort, or group by them. Use the_idfield instead. Selecting one is rejected outright rather than silently ignored. To report the human-readable label, select the_idand map it through the matchingcontext()list —statusesforstatus_id,lead_sourcesforlead_source_id. tagsuses bare-string equality for single-tag membership ({"tags": "VIP"}) andinfor multi-tag overlap ({"tags": {"in": ["VIP", "Hot"]}}). Grouping bytagsgives a per-tag breakdown: each tag is its own bucket, so a contact tagged bothVIPandHotis counted under each.count()per tag is exact; summing an unrelated field double-counts multi-tag contacts.starredis per-user. The value reflects whether the calling user has starred the contact, not whether anyone has.pending_dealis true when the contact has at least one deal stillpending. Won and lost deals do not count, sofalsedoes not mean the contact has never had a deal — it means none are open right now. It is a flag, not a figure: for values, querydealswithcontact_id.emails,phones,urlsare arrays of objects. Filter using dotted subfields:emails.address,emails.type(work,home,other)phones.number,phones.type(work,mobile,home,direct,fax,other)urls.url,urls.type(website,blog,twitter,linkedin,facebook,instagram,xing,other)
- Lookups:
company(viacompany_id). Pull the linked company record’s fields inline ascompany.<field>— for examplecompany.name,company.city, orcompany.country_code— inselect,where, andorder_by. Always name a subfield:companyon its own is a lookup, not a field, and selecting it bare is rejected. See Concepts › Lookups. company_namevs thecompanylookup.company_nameis text stored on the contact and is set even when no company record is linked;company.<field>reads the linked record. A contact with acompany_namebut nocompany_idreturnsnullfor everycompany.<field>.- 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_byworks on custom fields whose type supports it (checkdescribe);distinctdoes not — group by the field instead. Usedescribeto discover an account’s custom-field names and types.
Example queries
Open leads owned by me, top of stream:
{
"from": "contacts",
"where": { "owner_id": "ME()", "status_id": "lead" },
"limit": 25
}
Contacts I’ve starred:
{
"from": "contacts",
"where": { "starred": true }
}
Contacts whose linked company is in Ireland, with the company’s city:
{
"from": "contacts",
"select": ["first_name", "last_name", "company_name", "company.city"],
"where": { "company.country_code": "IE" },
"order_by": [{ "company.name": "asc" }]
}
Contacts at an Irish phone number, with no activity in the last 30 days:
{
"from": "contacts",
"where": {
"phones.number": { "like": "+353%" },
"last_activity_date": { "<": { "DAYS_AGO": [30] } }
}
}
Contacts by country:
{
"from": "contacts",
"select": ["country_code", "count()"],
"where": { "country_code": { "is not": null } },
"group_by": ["country_code"]
}