Welcome to the Coperniq API release notes. This section highlights noteworthy changes across endpoints, schemas, and docs.
Recent highlights
- New: Account file endpoints —
GET/POST /accounts/{account_id}/files,GET/DELETE /accounts/{account_id}/files/{file_id}, andPOST /accounts/{account_id}/files/upload, mirroring the existing project/opportunity file endpoints. All file endpoints (project, opportunity/request, account) now validate their path ID: missing or non-numeric returns400, and an ID that doesn’t match a real record of the expected type returns404, instead of silently returning an empty list. - New:
include_line_item_updatesonGET /projectsandGET /projects/search— opt-in flag (defaultfalse) that widensupdated_after/updated_beforeto also match projects whose project-level line items were added or edited in that window, and adds alineItemsUpdatedAtfield to each row (only present when the flag is passed). Line item deletions, reorders, and discount/tax-only edits aren’t detected, and result ordering still follows the project’s ownupdatedAt. - New: Filter
GET /opportunities/GET /projects(and their/searchvariants) by phase entry/exit date viaentered_phase_id/exited_phase_id+ date bounds — e.g. “opportunities that entered Closed - Won this month” in one query. Filtered rows include a compactphaseTimeline;phaseInstanceson the get-by-id endpoints now includesstartedAt/completedAt. - New: Utilities — read the global utility list via
GET /utilitiesandGET /utilities/{utility_id}, update custom fields viaPATCH. Filter by name/holding company/state,ownership_type, or geo lookup vialat+lng, mirroring the existing AHJ endpoints. - New: Account/project/opportunity contacts now include
isPrimary,title, anddescription, and are capped at 20 per response. Projects and opportunities now return their owncontactstoo (independent of the parent account) viainclude_contacts. - Fixed: contact responses now split related records into
accounts,projects,opportunities, andvendors(opportunities and vendors were previously dropped);PATCH /contacts/{contact_id}no longer returns stale/empty relations. - Fixed:
POST/PATCHresponses for accounts, projects, and opportunities no longer leak internal-only fields (lineItems,sections,matchedBy,phaseInstanceId, and others);workflowIdno longer returnsnullafter creating/updating with an explicit workflow. - Updated:
tradesis now documented as an enum listing every accepted trade type, on both projects and opportunities.
Looking for a specific date? See the entries below.
Archive work orders via the API
Work order update endpoints now accept isActive in the request body. Set isActive: false to archive a work order, or isActive: true to unarchive it — handled independently of any other fields in the same request.
Work order response payloads now also return isActive. The existing isArchived field is deprecated but still returned for backward compatibility — no breaking change.
updatedAt and updated_after/updated_before now reflect custom property changes
updated_after/updated_before on GET /projects (v1 and v2), GET /projects/search, and the other v1 list endpoints (requests/deals, clients/accounts, vendors) now also match records whose custom property values changed within the window. This is always on; there is no new query param.
The updatedAt field on a returned record now reflects the most recent change to that record, including changes to its custom property values.
Account files & project line item date filtering
New: Account file endpoints
Manage files on an account, mirroring the existing project/opportunity file endpoints.
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.
Utilities
New: Utility endpoints
Read and update the global utility list, mirroring the existing AHJ endpoints — utilities are seeded globally with no per-company scoping except for the custom properties.
Account, Project & Opportunity contacts
New: isPrimary and full contact detail
contacts[] on accounts, projects, and opportunities now returns id, name, title, description, emails, phones, and isPrimary for every contact (previously just id/emails/phones on accounts, and nothing at all on projects/opportunities).
isPrimaryistruefor exactly one contact — the one at the lowest position (the first element you send on create/update). At most one contact is ever primary.- Projects and opportunities now return
contactstoo — their own contacts, independent of and not inherited from the parent account’s. Set them on create/update via thecontactsrequest field, same as accounts. Passinclude_contacts=trueonGET /projects,GET /projects/search,GET /projects/{project_id}, and the equivalent/opportunitiesendpoints to include it — same as accounts. POST/PATCHresponses always includecontactsfor accounts, projects, and opportunities — not gated behindinclude_contactslikeGETis.
Timesheets
New: Timesheet (time entry) endpoints
Create, read, update, and delete timesheets — time entries logged by a worker against a project, account, or opportunity (and optionally a work order).
Bill payments
New: Bill payment endpoints
Record and retrieve payments against a bill, mirroring the existing invoice payment endpoints.
AHJs and an isCustom flag on properties
New: AHJ (Authorities Having Jurisdiction) endpoints
Look up jurisdictions and manage their custom property values through the public API.
Line item sections for projects, invoices, and bills
Line items can now be grouped into named sections — the same grouping you see on quotes and in the Coperniq UI — across project line items, invoices, and bills.
New catalog categories and DELETE behavior change
Envelope product categories
17 new ProductCategory values are now available when creating or updating a catalog item with type=PRODUCT:
ROOF_COVERING, ROOF_UNDERLAYMENT, ROOF_MEMBRANE, ROOF_FLASHING, ROOF_VENT, WALL_CLADDING, WALL_TRIM, WEATHER_BARRIER, WINDOW, EXTERIOR_DOOR, SKYLIGHT, OPENING_FLASHING, GUTTER, DOWNSPOUT, INSULATION, SEALANT, SHEATHING.
Labels — list and create
Manage labels that can be applied to work orders and assets.
Assets — list, create, read, update
Manage physical assets tied to a site and account. Reads use project-service GraphQL; creates and updates call fleet-service.
Quotes — full CRUD
You can now create, read, update, delete, send, and download PDF quotes through the public API.
Project & opportunity title fallback
When a project or opportunity has no title of its own (empty string or whitespace-only), the API now returns the parent account’s title in the title field instead of the blank value.
This applies to all read endpoints that return project or opportunity records:
GET /projects,GET /projects/search,GET /projects/{projectId}GET /opportunities,GET /opportunities/search,GET /opportunities/{opportunityId}
Bills
New: Bills endpoints
You can now create, read, update, and delete bills on projects via the public API.
Work order notes and work items
New: Work order notes endpoints
You can now create and list notes directly on work orders.
Create a note
Sites endpoints
Full CRUD support for site records via the public API.
Sites are address/location records that belong to an account. A single account may have multiple sites.
Properties endpoint naming updates
GET /properties — new account and opportunity keys
The /properties response now includes account and opportunity keys in addition to the existing client and request keys:
The record_type query parameter now accepts account and opportunity as values:
Form template update endpoint
Update a form template
A new endpoint lets you update an existing form template:
PATCH /form-templates/{formTemplateId}— Update a form template’s name, property layout, assignee, collaborators, labels, and due date configuration.
All fields are optional — only provided fields are updated. formLayouts is optional — when provided, it fully replaces all layout groups and their fields; omit it to leave the existing layout untouched. Include existing groups with their id to preserve them, or omit the id for new groups. Groups not included are removed. Array order determines position on the form.
