Welcome to the Coperniq API release notes. This section highlights noteworthy changes across endpoints, schemas, and docs.
Recent highlights
- New: Catalog item units, measurements, and categories
- Catalog items gain
unit/unitId,measurements/measurementIds,coverage, andwasteFactorMode/wasteFactorValue— the same quantity-calculation fields available in the Coperniq UI. - New
GET/GET {id}endpoints for/catalog-unitsand/measurements, plus a newGET /catalog-categoriesfor discovering validcategory/tradeGroupvalues. measurementIdsis replace-all onPATCH, not a merge — pass[]to clear all links.
- Catalog items gain
- New: Choose the company for a new API key
POST /api-keys(and legacy aliasGET /token) accept an optionalcompany_idquery param, for logins belonging to more than one company.- Passing
company_idcreates the key against that company; you must belong to it, or the request is rejected. - Omitting it now falls back deterministically to your first non-collaborator company, then your first company — fixing an inconsistency that could otherwise surface as a
Failed to fetch tokenerror.
- New: Update and delete a payment
PATCH/DELETE /payments/{payment_id}— the first payment endpoints scoped by the payment’s own id, not an invoice or bill.PATCHacceptsamount,paymentMethod,paymentDate,notes,paymentReference(same fields as create); at least one is required.- External (Stripe) payments restrict
PATCHtonotesonly, and rejectDELETEentirely — use a refund instead. - Updating or deleting a payment recalculates the parent invoice’s/bill’s
amountPaidand status automatically.
- New: Folders
GET/POST /projects/{project_id}/foldersGET/POST /opportunities/{opportunity_id}/foldersGET/PATCH/DELETE /folders/{folder_id}nameis required on create,parentIdandphaseInstanceIdare mutually exclusive, folders nest at most 2 levels deep.- Both list endpoints also return workflow phase rows (
type: "phase") alongside root-level real folders (type: "folder"), matching the project’s/opportunity’s Docs tab GET /phases/{phase_instance_id}/contentsandGET /folders/{folder_id}/contentslist a phase’s or folder’s direct children (type: "folder" | "file" | "form"— forms are ordinary files under the hood).- Also new:
GET/PATCH/DELETE /files/{file_id}—PATCHrenames, archives/unarchives, and/or moves a file (viafolderId/phaseInstanceId) in one request;GET/DELETEreplace the entity-scoped get/delete endpoints, which still work but are no longer documented.
- New: Create Form PDF
POST /forms/{form_id}/pdfto export a form to PDF.
Looking for a specific date? See the entries below.
Catalog item units, measurements, and categories
New: Units, measurements, and quantity-calculation fields on catalog items
Catalog items now expose the same unit and measurement-driven quantity calculation available in the Coperniq UI.
GET/POST/PATCH /catalog-items responses and request bodies gain:
Choose the company for a new API key
POST /api-keys (and its legacy alias GET /token) now accepts an optional company_id query parameter, for accounts where the same login belongs to more than one company. Your company ID can be found in the URL after logging in to the Coperniq UI: https://app.coperniq.io/{company_id}/inbox.
File properties and search field fixes
Notes
- A recognised public field name that doesn’t apply to the record type you’re searching (for
example
valueon an account, ortradeson a vendor) now returns400naming the field, instead of an empty200. If you were relying on an empty result for one of these, you’ll now get an explicit error — which is the point: an empty list was indistinguishable from “no matches”. - A field name the API does not recognise as a standard field is still treated as a possible custom property, unchanged. An empty result there still means “no records matched” — we can’t tell a typo from a real custom property name without knowing your company’s columns, so only fields we can prove don’t apply are rejected.
tradesholds a list of values, so a scalar comparison matches if any entry equals your value. Range and substring operators are rejected with a400rather than silently matching nothing.
Folders & Folder Contents
Added CRUD for project folders.
GET /projects/{project_id}/folders— list root-level folders (not nested in another folder), plus workflow phase rows. Passinclude_archived=trueto include archived folders (defaultfalse).GET /opportunities/{opportunity_id}/folders— same functionality as list project folders.POST /projects/{project_id}/folders- create folder.POST /opportunities/{opportunity_id}/folders- create folder.GET /folders/{folder_id}— get by id.PATCH /folders/{folder_id}— rename, archive/unarchive (isActive), or move (setparentId/phaseInstanceId) — moving isn’t a separate operation.DELETE /folders/{folder_id}— delete (also removes any subfolders and their files).GET /folders/{folder_id}/contents— list a folder’s direct children (subfolders, files, forms).GET /phases/{phase_instance_id}/contents— list a phase’s direct children, same shape as folder contents.
Create Form PDF
You can now use the API to create the form PDF similar to what an automation email would send.
POST /forms/{form_id}/pdfto export a form to PDF.- Optional body fields:
imagesOnly,imagesInRow(0-6),includeImageMetadata. - Returns
{ pdfBase64, contentType, name }.
Technical Details page, and documenting how to archive records
Added a Technical Details page (between Quick Start and Webhooks in the sidebar). Rate limits moved there from the Authentication page — same content, new home. Also added a new Archiving records section: most records can be archived instead of permanently deleted — use that object’s PATCH endpoint and set isActive to false (some resources still use isArchived; check the endpoint reference).
Create a form on a work order
Added POST /work-orders/{work_order_id}/forms to create a form instance directly on a work order, closing the gap where forms could only be created on projects and opportunities.
templateId is required. The form is created with its template’s name — a work order already sits in a phase, so phaseInstanceId is not accepted on this endpoint; the form inherits its placement from the work order’s parent element instance. The response includes workOrderId alongside projectId for correlation, and (when present) phaseInstanceId/phaseTemplateId.
Create forms on opportunities
Added POST /opportunities/{opportunity_id}/forms to create a form instance directly on an opportunity, mirroring the existing POST /projects/{project_id}/forms. templateId is required, name is optional (defaults to the template’s name), and opportunities cannot be assigned to a phase, so phaseInstanceId is not accepted or returned on this endpoint.
The response from both create-form endpoints now also returns templateId, name, createdAt, and updatedAt directly on the top-level object.
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.
