Portal API
Portal routes are called by the portal worker via service token. The portal worker authenticates the client and forwards context via headers:
X-Portal-Company-Id— the authenticated client’s company ID (required)X-Portal-User-Role—viewer,collaborator, oradminX-Portal-User-Id— the portal user’s IDX-Portal-Group-Company-Ids— comma-separated company IDs for group-aware queries (parent + children)
All routes enforce company scoping on every query. Endpoints that write data require collaborator or admin role.
Dashboard
Section titled “Dashboard”Get Dashboard
Section titled “Get Dashboard”GET /api/portal/dashboardReturns an overview for the client including active projects with task progress, financial summary, recent activity, upcoming milestones, and latest announcements.
Response: { projects, taskCounts, financial, recentActivity, milestones, announcements }
financialincludestotal_invoiced,total_outstanding,total_overdue- Group-aware: uses all companies in the group
Projects
Section titled “Projects”List Projects
Section titled “List Projects”GET /api/portal/projectsQuery parameters:
| Param | Type | Description |
|---|---|---|
status | string | Filter by project status |
Response: { projects, taskCounts }
Get Project
Section titled “Get Project”GET /api/portal/projects/:idResponse: { project, goals, members, aiSummary, decisions }
Includes the project’s goals, team members, latest AI summary, and recent decisions.
Get Project Tasks
Section titled “Get Project Tasks”GET /api/portal/projects/:id/tasksReturns non-private tasks for the project with status, priority, type, and assignee details.
Response: { tasks }
Get Project Roadmap
Section titled “Get Project Roadmap”GET /api/portal/projects/:id/roadmapReturns the roadmap linked to the project, if any, with its items.
Response: { roadmap, items }
Get Project Decisions
Section titled “Get Project Decisions”GET /api/portal/projects/:id/decisionsReturns all decisions for the project with the decider’s name.
Response: { decisions }
Budgets
Section titled “Budgets”List Budgets
Section titled “List Budgets”GET /api/portal/budgetsReturns budgets linked to the client’s projects or deals. Group-aware.
Response: { budgets }
Get Budget
Section titled “Get Budget”GET /api/portal/budgets/:idResponse: { budget, lineItems }
Approve Budget
Section titled “Approve Budget”POST /api/portal/budgets/:id/approveRole required: collaborator or admin
Approves a budget that has pending_approval status. Logs approval in history.
Response: { ok: true }
Invoices
Section titled “Invoices”List Invoices
Section titled “List Invoices”GET /api/portal/invoicesReturns all invoices for the client’s companies. Group-aware.
Response: { invoices }
Get Invoice
Section titled “Get Invoice”GET /api/portal/invoices/:idResponse: { invoice, lineItems, payments }
Contracts
Section titled “Contracts”List Contracts
Section titled “List Contracts”GET /api/portal/contractsReturns contracts where any signer’s email belongs to the client’s company contacts. Group-aware.
Response: { contracts }
List Pages
Section titled “List Pages”GET /api/portal/pagesReturns published pages linked to the client’s company or projects. Group-aware.
Response: { pages }
Get Page
Section titled “Get Page”GET /api/portal/pages/:idReturns a single published page with its rich content.
Response: { page }
List Notes
Section titled “List Notes”GET /api/portal/notesReturns client-visible notes across the client’s projects, deals, budgets, and invoices. Group-aware. Limited to 100 results.
Response: { notes }
Create Note
Section titled “Create Note”POST /api/portal/notesRole required: collaborator or admin
Request body:
| Field | Type | Description |
|---|---|---|
resourceType | string | project or invoice |
resourceId | string | Resource ID |
content | string | Note content |
parentId | string | Optional parent note ID (for replies) |
portalUserId | string | Portal user ID |
Response: { id } (201)
History
Section titled “History”Get History
Section titled “Get History”GET /api/portal/historyReturns recent history entries for the client’s projects, invoices, and budgets. Limited to 50 results.
Response: { history }
Get Team
Section titled “Get Team”GET /api/portal/teamReturns team members and leads across the client’s active projects. Group-aware.
Response: { team, leads }
Announcements
Section titled “Announcements”List Announcements
Section titled “List Announcements”GET /api/portal/announcementsReturns the latest 20 company-category announcements.
Response: { announcements }
Client Requests
Section titled “Client Requests”Create Request
Section titled “Create Request”POST /api/portal/requestsRole required: collaborator or admin
Creates a task from a client request. Auto-assigns SLA if applicable. Notifies the delivery manager.
Request body:
| Field | Type | Description |
|---|---|---|
title | string | Request title (required) |
description | string | Request description |
priority | string | Priority value (e.g. high, medium) |
projectId | string | Optional project to attach the request to |
portalUserId | string | Portal user ID |
Response: { taskId, ok: true } (201)
Notifications
Section titled “Notifications”List Notifications
Section titled “List Notifications”GET /api/portal/notificationsQuery parameters:
| Param | Type | Description |
|---|---|---|
filter | string | all (default), unread, or archived |
limit | number | Max results (default: 50, max: 100) |
offset | number | Pagination offset (default: 0) |
Response: { notifications, limit, offset }
Unread Count
Section titled “Unread Count”GET /api/portal/notifications/unread-countResponse: { unread_count }
Mark as Read
Section titled “Mark as Read”POST /api/portal/notifications/:id/readResponse: { ok: true }
Mark All as Read
Section titled “Mark All as Read”POST /api/portal/notifications/read-allResponse: { ok: true }
Get Notification Preferences
Section titled “Get Notification Preferences”GET /api/portal/notifications/preferencesReturns notification types that support portal delivery, merged with the user’s saved preferences.
Response: { preferences }
Update Notification Preferences
Section titled “Update Notification Preferences”PUT /api/portal/notifications/preferencesRequest body:
| Field | Type | Description |
|---|---|---|
preferences | array | Array of { notification_type, channel_in_app, channel_email } |
Response: { ok: true }
Connections
Section titled “Connections”List Connections
Section titled “List Connections”GET /api/portal/connectionsReturns platform connections for the client’s company. Only available when dashboard_enabled is true on the client profile.
Response: { connections, dashboard_enabled }
Create Connection
Section titled “Create Connection”POST /api/portal/connectionsConnects a new platform (e.g. Shopify, Google Ads). Tests the connection, encrypts credentials, and stores. Notifies the assigned DM.
Request body:
| Field | Type | Description |
|---|---|---|
platform | string | Platform key |
credentials | object | Platform-specific credentials |
display_name | string | Optional display name |
Response: { connection } (201)
Delete Connection
Section titled “Delete Connection”DELETE /api/portal/connections/:idDisconnects a client-managed connection. Cannot disconnect agency-managed connections.
Response: { ok: true }
Sync Connections
Section titled “Sync Connections”POST /api/portal/connections/syncTriggers an async sync of all connected platforms for the client.
Response: { message: "Sync started" } (202)
Results
Section titled “Results”Get Results
Section titled “Get Results”GET /api/portal/resultsReturns aggregated metrics for the client with period-over-period comparison and goal tracking. Only available when dashboard_enabled is true.
Query parameters:
| Param | Type | Description |
|---|---|---|
period_type | string | week, month (default), quarter, or year |
period_end | string | End date (YYYY-MM-DD, default: today) |
Response: { period, metrics, insight, connected_platforms, dashboard_enabled }
Each metric includes value, prior_value, delta_pct, target, and tracking_status.
Get Campaign Results
Section titled “Get Campaign Results”GET /api/portal/results/campaignsReturns campaign-level performance data aggregated over the selected period.
Query parameters:
| Param | Type | Description |
|---|---|---|
period_type | string | week, month (default), quarter, or year |
period_end | string | End date (YYYY-MM-DD) |
platform | string | Filter by ad platform |
sort_by | string | Sort field (default: spend). Options: spend, impressions, clicks, conversions, roas, cpa, etc. |
sort_dir | string | asc or desc (default) |
Response: { period, campaigns, totals }
Ask AI
Section titled “Ask AI”POST /api/portal/results/askAsk a natural-language question about the client’s performance data.
Request body:
| Field | Type | Description |
|---|---|---|
question | string | The question to ask |
Response: { answer }
Questionnaire
Section titled “Questionnaire”Get Questionnaire
Section titled “Get Questionnaire”GET /api/portal/questionnaireReturns the client’s success profile questionnaire responses.
Response: { profile }
Submit Questionnaire
Section titled “Submit Questionnaire”POST /api/portal/questionnaireRole required: collaborator or admin
Submits or updates the client success profile. Can also create client goals. Notifies the assigned DM on first completion.
Request body:
| Field | Type | Description |
|---|---|---|
financial_year_start | string | Financial year start |
financial_year_end | string | Financial year end |
key_trading_periods | string | Key trading periods |
channel_online_pct | number | Online channel percentage |
channel_instore_pct | number | In-store channel percentage |
channel_wholesale_pct | number | Wholesale channel percentage |
celebration_vision | string | Celebration vision |
growth_priorities_text | string | Growth priorities |
company_direction_text | string | Company direction |
goals | array | Array of goal objects with metric, target, unit, period, metric_key, metric_label |
Response: { ok: true }
List QBRs
Section titled “List QBRs”GET /api/portal/qbrsReturns presented QBRs for the client. Internal sections are filtered based on portal_visibility settings.
Response: { qbrs }
Onboarding
Section titled “Onboarding”Get Onboarding Progress
Section titled “Get Onboarding Progress”GET /api/portal/onboardingReturns the client’s onboarding template, stages, milestones, and progress.
Response: { template, stages, total_milestones, completed_milestones, completion_percentage, current_stage_type, started_at, completed_at }