Welcome to the Coperniq API release notes. This section highlights noteworthy changes across endpoints, schemas, and docs.
Recent highlights
- New: Pinned notes with
isPinnedon note create andPATCH /notes/{note_id}. Details - Updated:
POST /usersinvites with a seat (seatType, now required) and labor cost, andGET /usersreturns both. Details - Updated: Work orders can be labelled with
labelsonPATCH. Details - Updated: Notes lists return
replyToIdon replies, andPATCH /notes/{note_id}returns the edited note. Details - New: Notes on files with
GETandPOST /files/{file_id}/notes, including @mentions. Details - Docs: API keys for Google sign-in users.
POST /api-keysneeds a password; set one with Forgot password on the login page. SSO sign-in still works. Details - Updated:
GET /userscan include deactivated users withinclude_inactive=true, and returnsisActive. Details - Updated: Work orders now return
createdBy. Details - New:
GET /formslists your company’s forms across all records, withupdated_afterand other filters. Details - Docs: Work-order
collaboratorsonPATCHreplaces the full list. Details - New: Sub-accounts with
parentAccountIdon accounts. Details - Updated: Project and opportunity lists return the project manager, owner and sales rep, plus phases with
include_phases=true. Details - New:
internalMemoon invoices and bills. Details - Updated: Archived work orders can be listed with
include_archived=trueand fetched by ID. Details - Updated: Work order visits now return their
description. Details - Updated: Viewer seats can no longer create API keys. Details
- Updated: Calls have custom reasons and dispositions, AI summaries and transcripts, and a company-wide
GET /calls. Details - New: API key management for superadmins. Details
Looking for a specific date? See the entries below.
Pin notes
New: pin and unpin notes
In Coperniq, a pinned note stays at the top of a record’s notes. You can now pin and unpin notes through the API too.
Pin or unpin an existing note with isPinned on PATCH /notes/{note_id}:
Seats and labor cost on user invites
Updated: POST /users takes a seat and a labor cost
Invite User now matches Invite Members in Coperniq. Along with name, email, role and phone, you can set the member’s seat and labor cost.
Label work orders
Updated: PATCH a work order with labels
You can now set a work order’s labels through the API, the same way you can in Coperniq. This works on the project, opportunity, request, account and client work-order PATCH endpoints, for example PATCH /projects/{project_id}/work-orders/{work_order_id}.
Notes on files
New: GET and POST /files/{file_id}/notes
You can now read and add notes (comments) on files, the same way you can on projects and work orders.
GET /files/{file_id}/noteslists a file’s notes, newest first, withpageandpage_size(max 100).POST /files/{file_id}/notesadds a note. Sendbody, and optionallyreplyToIdto reply to a note on the same file, orfileIdsto attach files.
API keys for Google sign-in users
Docs: creating an API key if you sign in with Google
POST /api-keys needs your Coperniq email and password. If you sign in to Coperniq with Google, your account has no password yet, so the request returns 401.
To set one:
- On the Coperniq login page, click Forgot password.
- Enter the email you sign in with and open the reset link we send you.
- Choose a password.
Deactivated users on GET /users
Updated: GET /users can include deactivated users
Records can still point to people who have since been deactivated, as owner, salesRep, projectManager, createdBy, assignee and so on. Add include_inactive=true to GET /users to return deactivated members alongside active ones, so you can resolve those ids to a name and email.
List forms across your company
New: GET /forms
Lists your company’s forms across every project and opportunity, including forms on account work orders, paginated. Use it to sync forms incrementally instead of listing them one record at a time.
Query parameters
Sub-accounts with parentAccountId
New: parentAccountId on accounts
Accounts can now be read and written as sub-accounts of another account.
- Read: returned on
GET /accounts,GET /accounts/{account_id},GET /accounts/search, and thePOST/PATCHresponses. - Write: accepted on
POST /accountsandPATCH /accounts/{account_id}. SendnullonPATCHto unlink an account from its parent. - Rules: the parent must be an account in your company and must itself be top-level (1 level of nesting). An account that already has sub-accounts cannot become one, and an account cannot be its own parent. Breaking a rule returns
400withfield: "parentAccountId".
Project and opportunity lists return more of what the single-record GET returns
Updated: GET /projects, GET /projects/search, GET /opportunities, GET /opportunities/search
List and search responses now include the project manager and owner, sales rep and project manager ids, so a bulk sync no longer needs a follow-up GET per record.
- New fields on every row:
ownerId,salesRepId,projectManagerId, andprojectManager(nullwhen unset).
Viewer seats can no longer create API keys
Updated: POST /api-keys
Users with a Viewer (view-only) seat in the selected company now get a 403 from POST /api-keys and its legacy alias GET /token, and no key is created. Office and Field seats are unaffected.
- The seat checked is the one in the company the key is for: the
company_idyou pass, or your default company. A user who is a Viewer in one company and an Office user in another can still create keys for the second company. - Keys that Viewer users created before this change keep working. A superadmin can find them with
GET /api-keysand delete them withDELETE /api-keys/{key_id}.
API key management for superadmins, and calls updates
New: List and delete API keys, and archive API users
Superadmins can now manage their company’s API keys through the API. These endpoints authenticate with Basic Auth (your Coperniq email and password), the same as POST /api-keys, not with an API key. Each one takes an optional company_id for logins that belong to more than one company.
Opportunity status on read endpoints, now WON/LOST
Fixed: status is now returned when reading opportunities
GET /opportunities/{opportunity_id}, GET /opportunities, and GET /opportunities/search now include the opportunity’s status. Create and update already accepted it, but these read endpoints never returned it. The legacy /requests aliases return it too.
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.
