Client portals API

How to work a firm's client portals from a script, an automation or an assistant: list portals, send a client a request, add folders, move the work to a stage, read what happened, and hear when a client finishes a request or adds a file.

A client portal is a private space for one client: a Messages channel they see, a Firm only channel they don't, a file library, requests (files, answers, approvals, tasks) and stages. This guide is for the firm's side. A client's own view stays in the app: nothing here can be reached with a client's token.

Before you start

Scope Lets a token
portals:read list portals, read one, its files, requests and timeline
portals:write set up a portal, change it, its stages and branding, add folders, send and change requests

Portals

GET   /v1/orgs/{orgId}/portals
POST  /v1/orgs/{orgId}/portals          { "name": "Smith & Co", "kind": "business", "lead_user_id": "…", "staff_ids": ["…"] }
GET   /v1/portals/{portalId}
PATCH /v1/portals/{portalId}            { "welcome": "…", "show_progress": true, "require_2fa": true }
PATCH /v1/orgs/{orgId}/portal-branding  { "display_name": "Acme Accounting", "accent_light": "#1f6f43" }

POST takes an Idempotency-Key, so a retried request sets up one portal. category_id makes an existing category into a portal. PATCH can turn two-factor sign-in on, never off.

Stages and folders

PUT  /v1/portals/{portalId}/stages      { "names": ["Intake", "Documents", "Review", "Filed"] }
POST /v1/portals/{portalId}/stage       { "position": 1 }
GET  /v1/portals/{portalId}/files
POST /v1/portals/{portalId}/folders     { "name": "2024 Tax", "visibility": "shared" }

A new folder is Firm only unless you ask for "visibility": "shared". Moving to a stage may start requests from the portal's template, as it does in the app.

Requests

GET   /v1/portals/{portalId}/requests
GET   /v1/portals/{portalId}/requests/{requestId}
POST  /v1/portals/{portalId}/requests   { "title": "Tax documents", "due_on": "2027-01-15",
                                          "items": [{ "kind": "upload", "label": "W-2s" },
                                                    { "kind": "question", "label": "Any new dependents?", "config": { "question": "yesno" } }] }
PATCH /v1/portals/{portalId}/requests/{requestId}

Item kinds are upload, question, checkbox, approve and task (a sign item links an envelope sent in the app). A new request tells the client the way the app does: an email naming the firm, how many items and when (titles only if the firm chose that), and a line in their portal. internal_note is for your staff and never reaches the client. POST takes an Idempotency-Key.

The timeline

GET /v1/portals/{portalId}/timeline?limit=100

What happened, newest first: requests sent and completed, files added, stages, approvals. Each line has id, kind, at, text and firm_only, which marks a line the client never sees.

Client channels in the app

In Missived a portal is a client: Create → Client makes one in a single step (name, kind, lead, the team, a template and the client's contacts, with "Send the invites now" ticked by default). The client gets a row under Clients in the sidebar and opens on tabs: Conversation (the portal's Messages channel, which the client sees), Internal notes (its Firm only channel, which they never see), Files, Requests, Billing and a firm-only Timeline. The channels keep their ids, so messages_channel_id and internal_channel_id here are the Conversation and Internal notes, and everything on this page works unchanged. Your client still sees "Messages" in their own portal.

Someone on a client's team who has no portal licence sees the client's name in the sidebar and a locked page with "Ask an Admin for a licence", nothing else.

Logging email with a client

When your server has a logging domain (CLIENT_LOG_EMAIL_DOMAIN, log.missived.com on the hosted service), everyone with a portal licence gets a private address in Settings → Calendar & connections → Client email logging, like [email protected]. Bcc it when you write to a client, or forward a client's email to it, and the email is filed on that client's Timeline under Email: only your team sees it, never the client. Attachments go to the client's Files in a Firm only Email attachments folder.

Logged and sent email isn't in /v1, MCP or events.

Emailing a client from Missived

A client's Timeline → Email has Write an email, and each email there has Reply. You pick who it goes to from the client's own contacts and addresses (nobody else), write a subject and a message, and can attach files from the client's library: up to 10 MB is attached, anything bigger goes as a link (only for files the client can already open in their portal). It waits 10 seconds for Undo, then goes; the card shows Sent, Delivered, Delayed, Bounced or "Not sent" and why, with Try again.

Email sent from Missived goes through our email provider (Resend), like every other email we send; it's listed on our privacy promise.

What stays in the app

Inviting or removing a client, reminding them now, cancelling a request, deleting a folder, closing or deleting a portal, who may invite and what reminder emails say all happen in Missived, where a person confirms them.

Events

portal.request_completed (every required item of a request is done), portal.file_uploaded (a file or a new version went into the library, by the client or your staff) and portal.stage_changed go to event subscriptions, and from there to Zapier, Make and n8n.

A portal is private, so its events go only to a subscription that lists the portal's Messages channel (messages_channel_id on the portal) in channel_ids; a subscription for every channel never receives them, and only someone who can open the portal can list its channel. An event carries ids, the stage position, a file's size, version and SHA-256, and who did it. Names — the portal's, the request's title, the file's name, the stage's name — come along only when the org sends content in events. Answers, notes and email addresses are never sent.

For assistants

The MCP server has tools on these routes: list_portals, get_portal, list_portal_files, list_portal_requests and get_portal_timeline to read, and create_portal, update_portal, update_portal_branding, set_portal_stages, move_portal_stage, create_portal_folder, create_portal_request and update_portal_request to work. Confirm a request's items and due date with the person before sending it: the client is told.