Overview
The PortfolioStack REST API provides read access to the project catalog and portfolio workspace. Use it to query projects, retrieve portfolio data, and integrate PortfolioStack into your own tools, dashboards, and reporting systems.
| Base URL | https://rest.portfoliostack.com |
| Version | v1 |
| Protocol | HTTPS only |
| Response format | JSON |
Getting Started
API access requires a PortfolioStack account on a plan with API access enabled. Generate an API key from your account settings to authenticate requests.
- Sign in to PortfolioStack
- Go to Account → Integrations
- Under API Keys, click Generate Key
- Give the key a label and click Create
- Copy the
psk_…key — it is shown once and cannot be retrieved again
If the API Keys section is not visible in Account → Integrations, API access is not enabled on your current plan.
Authentication
All requests must include your API key as a Bearer token in the Authorization header.
Authorization: Bearer psk_your_key_hereAPI keys are long-lived. Store them securely and treat them like passwords. If a key is compromised, revoke it from Account → Integrations and generate a new one. You can maintain multiple active keys — for example, one per integration or environment.
Authentication errors
| Status | Cause |
|---|---|
401 Unauthorized | Missing, malformed, or revoked API key |
403 Forbidden | API access not enabled on your plan |
Rate Limits
The API allows 100 requests per minute per IP address. Exceeding this limit returns a 429 Too Many Requests response.
{ "error": "Too many requests, please try again later." }Projects
Projects are structured work plans from the PortfolioStack catalog. Each project includes a task hierarchy, industry classification, roles, compliance requirements, and linked resources.
List projects
GET https://rest.portfoliostack.com/v1/projectsReturns a paginated list of projects from the catalog. Supports keyword search, NAICS code filtering, and role filtering.
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
q | string | — | Keyword search across project names, industries, and tags |
naics | string | — | One or more NAICS codes, comma-separated (e.g. 4841,2361) |
role | string | — | Role name or alias, comma-separated. Common names and abbreviations are resolved automatically (e.g. PM, project manager, Safety Manager) |
page | integer | 1 | Page number |
limit | integer | 50 | Results per page (max 100) |
Response
{
"data": [
{
"id": "proj_abc123",
"name": "HAZMAT Incident Response Program",
"industry": "Truck Transportation",
"naics_codes": ["4841"],
"description": "...",
"task_count": 22,
"phase_count": 4,
"phases": ["Planning", "Procurement", "Execution", "Closeout"],
"roles": ["Safety Manager", "Project Manager", "EHS Specialist"],
"task_names": ["Initial Hazard Assessment", "PPE Procurement", "..."],
"search_tags": ["hazmat", "dot", "emergency response"],
"generated_at": "2025-01-15T00:00:00.000Z"
}
],
"meta": {
"total": 2100,
"page": 1,
"limit": 50,
"pages": 42,
"suggestion": null
}
}When a keyword search returns no primary results, meta.suggestion may contain a spelling correction and results will reflect the corrected query. When primary results are empty but related projects exist, they appear in meta.secondary.
Get project
GET https://rest.portfoliostack.com/v1/projects/:idReturns full project detail including all tasks. The id parameter accepts a project ID, instance ID, or MongoDB ObjectId.
Query parameters
| Parameter | Type | Description |
|---|---|---|
expand | string | Comma-separated resource types to include on each task: skills, tools, apps |
Response
{
"id": "proj_abc123",
"name": "HAZMAT Incident Response Program",
"industry": "Truck Transportation",
"naics_codes": ["4841"],
"description": "...",
"rationale": "...",
"audience": "...",
"target_companies": "...",
"value_potential": "...",
"sources": [{ "label": "...", "url": "..." }],
"search_tags": ["hazmat", "dot"],
"generated_at": "2025-01-15T00:00:00.000Z",
"task_count": 22,
"tasks": [
{
"wbs_code": "1.1",
"name": "Initial Hazard Assessment",
"type": "task",
"category": "Planning",
"owner_role": "Safety Manager",
"duration_days": 5,
"dependency_hints": [],
"obligation_ids": [],
"evidence_ids": [],
"notes": "..."
}
]
}Portfolios
Portfolios are user-created collections of projects. Portfolio endpoints return data belonging to the authenticated user — including metrics, phases, and task-level grid data matching what is visible in the portfolio workspace.
List portfolios
GET https://rest.portfoliostack.com/v1/portfoliosReturns all active portfolios for the authenticated user, with project summaries and counts. No query parameters.
Response
{
"data": [
{
"id": "64a1b2c3d4e5f6a7b8c9d0e1",
"name": "FY2026 Infrastructure",
"created_at": "2026-01-10T00:00:00.000Z",
"updated_at": "2026-07-15T00:00:00.000Z",
"project_count": 2,
"task_count": 87,
"phase_count": 12,
"projects": [
{
"id": "proj_abc123",
"name": "HAZMAT Incident Response Program",
"task_count": 30,
"added_at": "2026-01-12T00:00:00.000Z"
}
]
}
]
}Get portfolio
GET https://rest.portfoliostack.com/v1/portfolios/:idReturns full portfolio detail including metrics, phases, and the complete task grid for all projects in the portfolio.
Query parameters
| Parameter | Type | Description |
|---|---|---|
expand | string | Comma-separated resource types to include on each task: skills, tools, apps |
Response
{
"id": "64a1b2c3d4e5f6a7b8c9d0e1",
"name": "FY2026 Infrastructure",
"created_at": "2026-01-10T00:00:00.000Z",
"updated_at": "2026-07-15T00:00:00.000Z",
"metrics": {
"project_count": 2,
"task_count": 87,
"total_estimated_cost": 412500,
"completion_pct": 22,
"status_breakdown": {
"not_started": 40,
"in_progress": 28,
"blocked": 0,
"complete": 19
},
"by_project": [
{
"project_id": "proj_abc123",
"name": "HAZMAT Incident Response Program",
"task_count": 30,
"complete_count": 10,
"completion_pct": 33,
"estimated_cost": 187500
}
],
"by_role": [
{ "role": "PM", "task_count": 22 },
{ "role": "Safety Manager", "task_count": 18 }
]
},
"projects": [
{
"id": "64b2c3d4e5f6a7b8c9d0e1f2",
"project_id": "proj_abc123",
"name": "HAZMAT Incident Response Program",
"industry": "Truck Transportation",
"naics_codes": ["4841"],
"added_at": "2026-01-12T00:00:00.000Z",
"phase_count": 4,
"task_count": 30,
"phases": [
{
"wbs_code": "1.0",
"name": "Planning Phase",
"description": "Establish project scope and regulatory requirements."
}
],
"tasks": [
{
"wbs_code": "1.1.1",
"name": "Initial Hazard Assessment",
"phase": "Planning Phase",
"category": "Planning",
"owner_role": "Safety Manager",
"duration_days": 5,
"start_date": "2026-03-01T00:00:00.000Z",
"end_date": "2026-03-05T00:00:00.000Z",
"estimated_cost": 6250,
"status": "Complete",
"description": "...",
"user_notes": null,
"dependency_hints": [],
"obligation_ids": ["OBL-001"],
"evidence_ids": ["EV-001"]
}
]
}
]
}phase vs category: phase is the name of the parent WBS summary task (e.g. "Planning Phase") derived by walking the task list. category is the task category field stored on each leaf task (e.g. "Planning").
Expand parameter
The expand query parameter enriches task-level data with linked resources from the PortfolioStack catalog. Supported on GET /v1/projects/:id and GET /v1/portfolios/:id.
| Value | Description |
|---|---|
skills | Suggested learning pathways and certifications linked to each task |
tools | Recommended software tools linked to each task |
apps | Task-linked app templates available in PortfolioStack |
Values are comma-separated. Tasks without a linked intent return empty arrays for the requested expand types.
GET /v1/projects/:id?expand=skills,tools,appsEach task gains
{
"skills": [
{
"id": "...",
"name": "Hazardous Materials Handling",
"links": [{ "name": "HAZWOPER 40-Hour", "url": "...", "platform": "OSHA" }]
}
],
"tools": [{ "id": "...", "name": "Incident Command Software", "links": [...] }],
"apps": [{ "id": "...", "name": "Incident Report Form", "links": [] }]
}Error codes
All error responses return JSON with an error field describing the issue.
| Status | Meaning |
|---|---|
401 | Missing, malformed, or revoked API key |
403 | API access not enabled on your plan |
404 | Resource not found |
429 | Rate limit exceeded — 100 requests per minute |
500 | Unexpected server error |
{ "error": "Invalid or revoked API key." }