Portfolios
Requires a Pro plan or higher.
All portfolio endpoints are scoped to a workspace: /api/workspaces/:wsId/portfolios.
Portfolios are public pages that display a curated set of links with live analytics. The public page is accessible at /p/{slug} without authentication.
POST /workspaces/:wsId/portfolios
Create a portfolio.
Role: editor+
curl -X POST https://nobsredir.com/api/workspaces/ws_abc123/portfolios \ -H "X-API-Key: nobs_your_key" \ -H "Content-Type: application/json" \ -d '{ "name": "Q1 Campaign Links", "slug": "q1-campaign", "description": "All links for the Q1 2026 marketing campaign", "config": { "show_clicks": true, "style": { "bg_color": "#0a0a0a", "text_color": "#e5e5e5", "accent_color": "#3b82f6" } } }'Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Portfolio name, max 100 characters. |
slug | string | no | URL slug for the public page. Lowercase letters, numbers, hyphens, underscores. Max 50 characters. Auto-generated if omitted. |
description | string | no | Description shown on the public page. |
config | object | no | Display and styling options. |
config.show_clicks | boolean | no | Show click counts on public page (default: true). |
config.show_trend | boolean | no | Show click trend on public page. |
config.style | object | no | Custom colors: bg_color, text_color, accent_color (hex values). |
Response 201:
{ "id": "pf_abc123", "name": "Q1 Campaign Links", "slug": "q1-campaign", "description": "All links for the Q1 2026 marketing campaign", "config": {"show_clicks": true, "style": {"bg_color": "#0a0a0a", "text_color": "#e5e5e5", "accent_color": "#3b82f6"}}, "created_at": "2026-03-09T12:00:00.000Z"}Errors:
400- Name is required400- Invalid slug format400- Slug exceeds 50 characters402- Plan limit reached (Pro: 3, Team: 10, Agency: 25)402- Portfolios require a Pro plan or higher409- Slug already exists
GET /workspaces/:wsId/portfolios
List all portfolios in the workspace.
Role: viewer+
curl https://nobsredir.com/api/workspaces/ws_abc123/portfolios \ -H "X-API-Key: nobs_your_key"Response 200:
{ "portfolios": [ { "id": "pf_abc123", "workspace_id": "ws_abc123", "name": "Q1 Campaign Links", "slug": "q1-campaign", "description": "All links for the Q1 2026 marketing campaign", "config": {"show_clicks": true}, "created_by": "usr_abc123", "created_at": "2026-03-09T12:00:00.000Z", "updated_at": "2026-03-09T12:00:00.000Z" } ]}GET /workspaces/:wsId/portfolios/:portfolioId
Get a portfolio with its links.
Role: viewer+
curl https://nobsredir.com/api/workspaces/ws_abc123/portfolios/pf_abc123 \ -H "X-API-Key: nobs_your_key"Response 200:
{ "id": "pf_abc123", "workspace_id": "ws_abc123", "name": "Q1 Campaign Links", "slug": "q1-campaign", "description": "All links for the Q1 2026 marketing campaign", "config": {"show_clicks": true}, "created_by": "usr_abc123", "created_at": "2026-03-09T12:00:00.000Z", "updated_at": "2026-03-09T12:00:00.000Z", "links": [ { "id": "lnk_xyz789", "domain": "go.yourco.com", "slug": "demo", "target": "https://example.com/page", "title": "Demo page", "click_count": 142, "sort_order": 0 } ]}Errors: 404 - Portfolio not found.
PATCH /workspaces/:wsId/portfolios/:portfolioId
Update a portfolio’s name, description, or config.
Role: editor+
curl -X PATCH https://nobsredir.com/api/workspaces/ws_abc123/portfolios/pf_abc123 \ -H "X-API-Key: nobs_your_key" \ -H "Content-Type: application/json" \ -d '{"name": "Q1 Campaign Links (Updated)", "config": {"show_clicks": false}}'Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | no | New name, max 100 characters. |
description | string or null | no | New description. Set to null to clear. |
config | object | no | New display/styling config. |
Response 200:
{"ok": true}Errors: 400 - No updates provided. 404 - Portfolio not found.
DELETE /workspaces/:wsId/portfolios/:portfolioId
Delete a portfolio and its public page. Does not delete the links themselves.
Role: admin+
curl -X DELETE https://nobsredir.com/api/workspaces/ws_abc123/portfolios/pf_abc123 \ -H "X-API-Key: nobs_your_key"Response 200:
{"ok": true}Errors: 404 - Portfolio not found.
POST /workspaces/:wsId/portfolios/:portfolioId/links
Add links to a portfolio. Links must belong to the same workspace.
Role: editor+
curl -X POST https://nobsredir.com/api/workspaces/ws_abc123/portfolios/pf_abc123/links \ -H "X-API-Key: nobs_your_key" \ -H "Content-Type: application/json" \ -d '{"link_ids": ["lnk_xyz789", "lnk_abc456"]}'Body:
| Field | Type | Required | Description |
|---|---|---|---|
link_ids | string[] | yes | Array of link IDs to add. Max 50 per request. |
Links that don’t belong to the workspace are silently skipped. Duplicate links (already in the portfolio) are silently skipped.
Response 200:
{"added": 2}Errors: 400 - Empty or missing link_ids array. 400 - More than 50 links. 404 - Portfolio not found.
DELETE /workspaces/:wsId/portfolios/:portfolioId/links/:linkId
Remove a link from a portfolio. Does not delete the link itself.
Role: editor+
curl -X DELETE https://nobsredir.com/api/workspaces/ws_abc123/portfolios/pf_abc123/links/lnk_xyz789 \ -H "X-API-Key: nobs_your_key"Response 200:
{"ok": true}Errors: 404 - Portfolio not found.
Public page
The public portfolio page is available at:
https://yourdomain.com/p/{slug}No authentication is required. The page displays the portfolio’s links with optional click counts and custom styling. It is cached for 5 minutes.