Every endpoint a customer contact's token can use: everything the portal does.
Every endpoint of the portal half of the API, 92 in all,
grouped by area. How to sign in, the envelope every answer uses, paging, errors and
permissions are on REST API and webhooks; this page is the list.
| Method | Address | What it does |
GET | /api/v1/portal/contracts | List the company's contracts, the latest start date first. Contracts hidden from the customer or in the bin are never listed. |
GET | /api/v1/portal/contracts/{contract} | One contract, with its wording, its signatures and who is still to sign. |
POST | /api/v1/portal/contracts/{contract}/sign | Sign the contract on the customer's side. Takes signature (a PNG data URI of the drawn signature), signed_by (the signer's full name) and accept (1, the signer agreeing to the signing statement). |
GET | /api/v1/portal/estimate-requests | List the company's estimate requests, newest first. |
POST | /api/v1/portal/estimate-requests | Ask to be quoted. Takes subject, description, budget, wanted_by, the workspace's extra questions as answers[<id>], and up to five attachments (multipart). The company and contact are the caller's own. |
GET | /api/v1/portal/estimate-requests/create | What the request form asks: the workspace's own extra questions (each sent as answers[<id>]) and the upload limits for attachments. |
GET | /api/v1/portal/estimate-requests/{estimateRequest} | One estimate request, with the files shared with the customer and the estimate it led to once that has been sent. |
GET | /api/v1/portal/estimates | List the company's estimates, newest first. Drafts are never listed. Filter with status (sent, accepted, declined). |
GET | /api/v1/portal/estimates/{estimate} | One estimate, with its lines, totals, signatures and the discussion (never internal comments). Opening it the first time stamps it as viewed, as opening the page does. |
POST | /api/v1/portal/estimates/{estimate}/accept | Accept the estimate. Takes lines (the ids of the optional lines kept) with lines_submitted=1 when the estimate offers optional lines; an estimate that asks to be signed is accepted by signing it instead. |
POST | /api/v1/portal/estimates/{estimate}/decline | Decline the estimate. Takes an optional decline_reason. |
POST | /api/v1/portal/estimates/{estimate}/sign | Sign the estimate, which accepts it. Takes signature (a PNG data URI of the drawn signature), signed_by (the signer's full name), accept (1, the signer agreeing to the signing statement) and, as for accept, lines with lines_submitted. |
GET | /api/v1/portal/pdf/{kind}/{id} | Download the PDF of an invoice, estimate, proposal or contract the company has been sent ({kind} is invoice, estimate, proposal or contract). Streams application/pdf as an attachment. Estimates and proposals answer 403 while "Let clients download the PDF" is off. |
GET | /api/v1/portal/proposals | List the company's proposals, newest first. Drafts are never listed. Filter with status (sent, open, revised, accepted, declined). |
GET | /api/v1/portal/proposals/{proposal} | One proposal, with its content, lines, totals, signatures and the discussion (never internal comments). Opening it the first time stamps it as viewed and moves a "sent" proposal to "open", as the page does. |
POST | /api/v1/portal/proposals/{proposal}/accept | Accept the proposal. Takes lines (the ids of the optional lines kept) with lines_submitted=1 when it offers optional lines; a proposal that asks to be signed is accepted by signing it instead. |
POST | /api/v1/portal/proposals/{proposal}/decline | Decline the proposal. Takes an optional decline_reason. |
POST | /api/v1/portal/proposals/{proposal}/sign | Sign the proposal, which accepts it. Takes signature (a PNG data URI of the drawn signature), signed_by (the signer's full name), accept (1, the signer agreeing to the signing statement) and, as for accept, lines with lines_submitted. |
POST | /api/v1/portal/sales-documents/thread-order | Remember which way round this contact reads a discussion. Takes order: "oldest" (the default) or "newest". |
POST | /api/v1/portal/sales-documents/{kind}/{document}/comments | Post a comment to the discussion on an estimate or a proposal ({kind} is "estimate" or "proposal"). Takes body and up to five attachments (multipart). Answers 404 while the discussion is switched off. |
| Method | Address | What it does |
GET | /api/v1/portal/projects | List the company's projects: live work first, then by deadline. Each carries only what its switches allow (deadline with view_overview, progress with view_progress) and the tabs it has. |
GET | /api/v1/portal/projects/request | Whether this contact may ask for a new project (404 when the workspace does not let customers request projects). |
POST | /api/v1/portal/projects/request | Ask for a new project. Takes name, description and deadline; it is created "not started", for the company, for staff to take up. 403 when the workspace does not let customers request projects. |
GET | /api/v1/portal/projects/{project} | One project: its headline, the tabs it shows this customer, its switches, and its overview when the overview tab is one of them. |
POST | /api/v1/portal/projects/{project}/feedback | Rate the project. Takes rating (1-5) and an optional comment; leaving feedback again changes the first, until staff have replied to it. 403 while feedback is closed for this project. |
POST | /api/v1/portal/projects/{project}/files | Upload files to the project. Takes files[] (one to ten, multipart); they are visible to the company's contacts at once and the project's team is told. |
GET | /api/v1/portal/projects/{project}/files/{file} | Download a project file under its own name. |
DELETE | /api/v1/portal/projects/{project}/files/{file} | Delete a file the contact uploaded themselves. |
POST | /api/v1/portal/projects/{project}/files/{file}/comments | Comment on a project file. Takes body. |
POST | /api/v1/portal/projects/{project}/tasks | Add a task to the project. Takes title, description, start_date, due_date and milestone_id (one of the project's). It starts in the first open status, visible to the customer; the project's team is told. |
GET | /api/v1/portal/projects/{project}/tasks/create | What the new-task form offers: the project's milestones a task can be put under (empty unless the customer may see milestones). 404 unless the customer may add tasks to this project. |
GET | /api/v1/portal/projects/{project}/tasks/{task} | One task: its details, and - as the project's switches allow - its assignees, checklist, time logged and comment thread (never internal comments; files only those shared with the customer). |
PUT / PATCH | /api/v1/portal/projects/{project}/tasks/{task} | Change a task the contact added themselves. Takes title, description, start_date, due_date and milestone_id; any left out keeps its current value. 403 for anybody else's task or while edit_own_tasks is off. |
POST | /api/v1/portal/projects/{project}/tasks/{task}/comments | Comment on a task. Takes body and, while the project allows attaching files, up to five attachments (multipart). |
DELETE | /api/v1/portal/projects/{project}/tasks/{task}/comments/{comment} | Delete a comment the contact wrote on a task - only their own, and only while they may still comment. |
GET | /api/v1/portal/projects/{project}/{tab} | One tab of a project: overview, tasks, milestones, gantt, files, activity or feedback. 404 for a tab this project does not show the customer. |
| Method | Address | What it does |
GET | /api/v1/portal | The contact's overview: each panel's key, label, size and data, in the order the business (and, where allowed, the contact) arranged them, and which shortcuts the contact is offered. |
GET | /api/v1/portal/account | The signed-in contact's own details, the tabs of My account the business shows them, and the parts of the portal they may open. |
PUT / PATCH | /api/v1/portal/account | Change the contact's own name, phone number and job title. Takes first_name, last_name, phone and job_title; any left out keeps its current value. The email address cannot be changed from the portal. |
GET | /api/v1/portal/account/company | The company's billing and shipping addresses, and whether this contact may change them (primary contacts only). Answers 404 while the business hides the Company tab. |
PUT / PATCH | /api/v1/portal/account/company | Change the company's billing and shipping addresses - primary contacts only. Takes address, city, state, zip, country and the same five with a shipping_ prefix; nothing else about the company can be changed here. |
GET | /api/v1/portal/account/contacts | Everybody at the contact's company, primary contacts first, each with the parts of the portal they may open. Primary contacts only. |
POST | /api/v1/portal/account/contacts | Add a contact to the company and email them a link to choose a password. Takes first_name and email (required), last_name, phone, job_title, and permissions - the area keys they may open (see GET account/contacts/create). Primary contacts only. |
GET | /api/v1/portal/account/contacts/create | The parts of the portal a contact can be given (less modules switched off), for the add and edit forms - each a key for "permissions" and its label. Primary contacts only. |
GET | /api/v1/portal/account/contacts/{colleague} | One contact at the company, with the parts of the portal they may open. Primary contacts only. |
PUT / PATCH | /api/v1/portal/account/contacts/{colleague} | Change a contact's name, phone, job title and - unless they are a primary contact, who always sees everything - the parts of the portal they may open. Takes first_name (required), last_name, phone, job_title and permissions. Any field left out keeps its current value; permissions, when sent, replaces the whole list. Primary contacts only. |
POST | /api/v1/portal/account/contacts/{colleague}/deactivate | Stop a contact signing in. Not yourself, and not the company's last active primary contact. Primary contacts only. |
POST | /api/v1/portal/account/contacts/{colleague}/invite | Email a contact who has not chosen a password yet a new link to choose one; the new link replaces the old. Primary contacts only. |
POST | /api/v1/portal/account/contacts/{colleague}/reactivate | Let a switched-off contact sign in again. Primary contacts only. |
PUT / PATCH | /api/v1/portal/account/password | Change the contact's password. Takes current_password, password and password_confirmation; the contact is emailed that it changed. Answers 404 while the business hides the Password tab. |
GET | /api/v1/portal/customise/dashboard | The overview panels this contact may arrange, in their order, each with its key, label, icon and whether they have hidden it; and whether they have an arrangement of their own. |
PUT / PATCH | /api/v1/portal/customise/dashboard | Save the contact's arrangement of the overview. Takes widgets: an object of panel key => {"visible": true|false}, in the order to show them. Keys the contact was not offered are ignored. |
POST | /api/v1/portal/customise/dashboard/reset | Put the overview back to the business's standard layout. |
GET | /api/v1/portal/customise/menu | The menu entries this contact may arrange, in their order, each with its key, label, icon, whether they have hidden it and whether it is locked (always shown); and whether they have an arrangement of their own. |
PUT / PATCH | /api/v1/portal/customise/menu | Save the contact's arrangement of the menu. Takes items: an object of entry key => {"visible": true|false}, in the order to show them. A locked entry cannot be hidden. |
POST | /api/v1/portal/customise/menu/reset | Put the menu back to the business's standard layout. |
GET | /api/v1/portal/details | The contact's own details, while privacy is switched on. The portal's old "My details" page now goes to My account, so there is no page to read them from; the same 404 applies while privacy is off. |
PUT / PATCH | /api/v1/portal/details | Correct the contact's own name and phone number. Takes first_name, last_name and phone; any left out keeps its current value. |
GET | /api/v1/portal/messages | The contact's conversations, the latest activity first, each with the other people in it and how many messages are new to the contact. |
POST | /api/v1/portal/messages | Start a conversation. Takes subject, body, recipients (user ids from GET messages/create - one not on that list refuses the whole message) and up to five attachments[] as multipart files. |
GET | /api/v1/portal/messages/create | Who the contact may write to - staff and colleagues, each {id, name, job_title} - whether they may start a conversation at all, and whether they have asked to be emailed when a message arrives. |
POST | /api/v1/portal/messages/preferences | Choose whether the contact is emailed when a message arrives. Takes emails (boolean); left out, the choice stays as it is. |
GET | /api/v1/portal/messages/{conversation} | One conversation with every message in it, oldest first, and whether the contact may reply. Reading it marks it read for the contact. |
POST | /api/v1/portal/messages/{conversation}/reply | Reply to a conversation. Takes body and up to five attachments[] as multipart files. Refused with 403 when replies to it are switched off. |
GET | /api/v1/portal/notifications | The contact's notices, newest first. Takes unread=1 for only those not yet read. |
GET | /api/v1/portal/notifications/feed | The latest ten notices and the number unread - what the bell polls. Takes after (a notice id) for only those newer than it. |
POST | /api/v1/portal/notifications/read-all | Mark every notice read. |
POST | /api/v1/portal/notifications/{id}/read | Mark one notice read, and say how many are still unread. |
GET | /api/v1/portal/pages/{slug} | One published customer page by its slug: title and content (HTML, sanitised as the portal shows it). |
GET | /api/v1/portal/privacy | The contact's privacy page: which parts are offered, the consents asked for and their answers, the history of their answers, their latest data export and removal request, how long an export link lasts, and the privacy policy and terms links. |
POST | /api/v1/portal/privacy/consents | Record the contact's answers to every consent currently asked for. Takes purposes: the ids of those they agree to; every other active one is recorded as withdrawn. Left out altogether, every answer stands. |
POST | /api/v1/portal/privacy/export | Build a copy of everything held about the contact, now, replacing any copy still waiting. Download it from the id returned, at GET privacy/export/{privacyRequest}, before it expires. |
GET | /api/v1/portal/privacy/export/{privacyRequest} | Download the contact's data export as a zip file. It is deleted from the server once fetched, so this works once; afterwards, and once the link has expired, it answers 404. |
POST | /api/v1/portal/privacy/removal | Ask the business to remove the contact's details. Takes confirm (must be accepted) and an optional message. Refused with 422 while an earlier request is still waiting. |
| Method | Address | What it does |
POST | /api/v1/portal/ai/actions/{action}/cancel | Cancel an action the assistant proposed. Nothing happens. |
POST | /api/v1/portal/ai/actions/{action}/confirm | Carry out an action the assistant proposed, as the contact, through the portal's own rules. Takes inputs (the values for the boxes the card asks for, such as a password) and typed (what its confirm_text asks to be typed first). An action already dealt with, or one that fails, is 422 and stays as it was. |
GET | /api/v1/portal/ai/assistant | What the assistant's opening screen offers: whether it is available, the month's allowance and the quick starters this contact may use. 403 while the business has not switched the portal's assistant on. |
POST | /api/v1/portal/ai/assistant | Ask the assistant something, starting a conversation when none is named. Takes question (at most 4000 characters) and conversation (the id of one of the contact's conversations to carry on). Answers with the conversation id, its title, the answer as text and as HTML, and any actions it proposes - nothing it proposes happens until it is confirmed. A failed answer is 422 with the conversation id in data, because the question was recorded anyway. |
GET | /api/v1/portal/ai/assistant/history | The contact's thirty most recent conversations, newest first. |
GET | /api/v1/portal/ai/assistant/{conversation} | One of the contact's conversations with every message in it, and the actions the assistant proposed along the way with their status. |
DELETE | /api/v1/portal/ai/assistant/{conversation} | Delete one of the contact's conversations. |
POST | /api/v1/portal/auth/token | Exchange a customer contact's email and password for a portal token. |