Opportunity & Project Phase Tracking
Improved: Phase instance timestamps on GET /opportunities/{opportunity_id}
Each entry in phaseInstances now includes startedAt and completedAt, matching the projects endpoint. Use these to compute time-in-stage or close dates without a separate lookup.
- Only populated on
GET /opportunities/{opportunity_id}; the list and search endpoints do not returnphaseInstancesand are unaffected. completedAtisnullfor the phase the opportunity currently occupies and any that have not been started.
New: Filter GET /opportunities/GET /projects (and their /search variants) by phase entry/exit date
Find records by when they entered or exited a phase — e.g. “opportunities that entered Closed - Won this month” — in a single query, without paging through the whole list.
- Look up a phase’s template ID via the
phaseTemplateIdon anyphaseInstancesentry fromGET /opportunities/{opportunity_id},GET /projects/{project_id}, orGET /workflows. - A
*_phase_idis required whenever its matching date bound(s) are supplied; omitting it returns a400 VALIDATION_ERROR. Invalid date formats also return400. - When
entered_phase_id/exited_phase_idis used, each row in the response also includesphaseTimeline— the record’s full phase-transition history in compact form (phaseTemplateId,startedAt,completedAt), so you can see when it entered/exited without a second call per record. Not present on unfiltered list/search calls, and not the same asphaseInstances(no names/positions/SLA — use the get-by-id endpoint for that).
Fixed: phaseInstances on GET /opportunities/{opportunity_id} was unordered and missing SLA fields
phaseInstances now returns in position order and includes phaseTemplate.redSla/yellowSla, matching the projects endpoint and the documented schema.
Fixed: primaryPhone list filter required an exact match
GET /opportunities/GET /projects (and their /search variants) primaryPhone filter did an exact match against the raw stored phone value, so any human-entered format (dashes, spaces, parens) returned nothing. Now normalizes to digits and substring-matches against both the record’s own phone and its linked contact’s phone.
