Resource Reference
Learn which Public API endpoints are available, what they return, and how pagination works.
Resource Reference
This page documents the human-readable shape of the v1 Deck Public API. The machine-readable contract at /openapi is the complete source for endpoint methods, request bodies, filters, and response schemas.
Endpoint summary
| Method | Path | Description | Pagination |
|---|---|---|---|
GET | /openapi | OpenAPI 3.1 specification | none |
GET | /me | Verify the key and inspect resolved identity | none |
GET | /context, /org-members, /dashboard, /search | Read shared organization data or search product records | varies |
GET | /themes | List themes | cursor |
GET | /themes/{themeId} | Get a theme by ID | none |
GET | /insights | List insights | cursor |
GET | /insights/{insightId} | Get an insight by ID | none |
GET, POST, PATCH, DELETE | /themes, /insights, and their nested resources | Maintain themes, insights, their links, and insight quotes | varies |
GET, POST, PATCH, DELETE | /categories, /segments, /contacts, /feedback | Work with feedback classification and customer data | varies |
GET, POST | /interviews, /surveys, /nps, /csv-uploads, /integrations | Read source data and start supported imports, syncs, or processing | varies |
GET, POST | /jobs and /jobs/{jobId} | Create, list, and poll product async jobs | varies |
GET | /projects | List Projects | cursor |
POST | /projects | Create a Project | none |
GET | /projects/{projectId} | Get a Project by ID | none |
PATCH | /projects/{projectId} | Update Project metadata | none |
DELETE | /projects/{projectId} | Soft-delete a Project by archiving it | none |
GET | /initiatives | List initiatives | cursor |
GET | /initiatives/{initiativeId} | Get an initiative by ID | none |
POST | /build/projects | Execute one Project build mutation | none |
POST | /build/initiatives | Execute one initiative build mutation | none |
POST | /build/opportunities | Execute one opportunity build mutation | none |
GET, POST, PATCH, DELETE | /initiatives, /opportunities, and their nested resources | Read and manage Build resources, evidence, sections, segments, and links | varies |
GET | /support/cs/tickets | Read customer-service tickets | varies |
GET | /subthemes | List subthemes | offset |
GET | /subthemes/{subthemeId} | Get a subtheme by ID | none |
GET | /competitors | List competitors and research status | none |
GET | /competitors/overview | Landscape overview | none |
GET | /competitors/{competitorId} | Get one competitor | none |
GET | /competitors/subthemes/{subthemeId} | Competitor subtheme detail | none |
GET | /competitors/opportunity-teasers | Opportunity teasers | none |
GET | /competitors/opportunity-teasers/{teaserId} | Teaser insights | cursor |
POST | /competitors/research | Start or add-and-rerun research | none |
GET | /competitors/research/{jobId} | Poll a research job | none |
POST | /competitors/promote | Promote a finding to Opportunity Backlog | none |
GET | /competitors/checkins | List check-in runs | cursor |
GET | /competitors/checkins/latest | Latest check-in run | none |
POST | /competitors/checkins | Enable or disable check-ins (org admin) | none |
The table groups endpoint families so it remains readable as Deck evolves. Use /openapi when you need an exact route or schema; it is generated from the same shipped inventory used to verify Public API parity.
/me
Use this endpoint to confirm that your key works and see the identity Deck resolved from it.
Response fields
| Field | Meaning |
|---|---|
org_id | The authenticated organization |
user_id | The key owner |
role | The key owner’s current role |
scope | The authorized scope for this endpoint (read for /me) |
/themes
Lists themes for the authenticated organization.
Query parameters
| Parameter | Type | Notes |
|---|---|---|
limit | integer | default 50, max 100 |
cursor | string | opaque pagination cursor |
Theme fields
| Field | Meaning |
|---|---|
id | Theme ID |
name | Theme name |
color | Theme color |
notes | Optional notes |
insight_count | Number of linked insights |
created_at | Creation timestamp |
updated_at | Last update timestamp |
/insights
Lists insights for the authenticated organization.
Query parameters
| Parameter | Type | Notes |
|---|---|---|
limit | integer | default 50, max 100 |
cursor | string | opaque pagination cursor |
theme_id | string | filter to a specific theme |
sentiment | string | POSITIVE, NEGATIVE, NEUTRAL |
category | string | category filter |
Supported categories
PAIN_POINTSDELIGHTSGENERAL_FEEDBACKFEATURE_REQUESTSUSABILITY_ISSUESBUGS_AND_ERRORS
Insight list fields
| Field | Meaning |
|---|---|
id | Insight ID |
name | Insight title |
sentiment | Sentiment classification |
category | Insight category |
quote | Representative customer quote |
themes | Linked theme references |
created_at | Creation timestamp |
updated_at | Last update timestamp |
/insights/{insightId}
Returns a single insight with richer source metadata.
Additional fields include:
feedback_idcontact_iduser_emailuser_nameinterview_date
/projects
Lists Projects for the authenticated organization.
POST /projects creates a Deck-side Project and seeds the canonical Overview, Evidence, and Success Metrics sections. Required body: title. Optional body fields: description, status, initiative_id, and owner_id.
Query parameters
| Parameter | Type | Notes |
|---|---|---|
limit | integer | default 50, max 100 |
cursor | string | opaque pagination cursor |
initiative_id | string | filter to Projects linked to an initiative |
status | string | Project lifecycle status |
external_provider | string | linear or jira |
Supported statuses
not_startedin_progresscompletedarchived
Project fields
| Field | Meaning |
|---|---|
id | Project ID |
title | Project title |
slug | URL slug |
description | Optional short summary |
status | Project lifecycle status |
initiative | Linked initiative summary when present |
external_links | Jira or Linear link metadata when present |
created_at | Creation timestamp |
updated_at | Last update timestamp |
/projects/{projectId}
Returns a single Project plus structured document sections and evidence references.
PATCH /projects/{projectId} updates Project metadata: title, description, status, initiative_id, owner_id, start_date, and target_date.
DELETE /projects/{projectId} soft-deletes the Project by archiving it. It does not delete or mutate Jira or Linear artifacts.
Additional fields:
sectionsevidence_referencesexternal_linkslatest_evidence_story
Project sections include Overview, Evidence, and Success Metrics content when present.
/initiatives
Lists initiatives for the authenticated organization.
Query parameters
| Parameter | Type | Notes |
|---|---|---|
limit | integer | default 50, max 100 |
cursor | string | opaque pagination cursor |
status | string | initiative lifecycle status |
Supported statuses
draftexploringbuildingready_to_actcompletedarchived
Initiative fields
| Field | Meaning |
|---|---|
id | Initiative ID |
title | Initiative title |
slug | URL slug |
description | Optional short summary |
goal | Optional goal statement |
status | Initiative status |
leveraged_insights_count | Number of linked insights |
created_at | Creation timestamp |
updated_at | Last update timestamp |
/initiatives/{initiativeId}
Returns a single initiative plus structured content sections.
Additional fields:
sectionslast_generated_at
Each section includes:
keycontentupdated_at
Build write surfaces
Build writes are action-based and strictly bounded.
Required on every mutation:
Idempotency-Keyheader (1-128 chars)- one action payload per request
- org policy that allows write scope for the key owner
Explicit non-goals:
- backlog generation endpoints
- standalone story generation endpoints outside approved Build actions
- arbitrary delete endpoints
- third-party Jira or Linear artifact creation
/build/projects
Executes one Project mutation action.
Supported actions
| Action | Required fields | Optional fields |
|---|---|---|
create | title | status, initiative_id, owner_id |
update_title | project_id, title | none |
update_status | project_id, status | none |
archive | project_id | none |
set_initiative | project_id, initiative_id | initiative_id may be null to make the Project standalone |
upsert_section | project_id, section_key, content | none |
link_external_artifact | project_id, provider, external_url, display_title, one of external_id or external_key | metadata |
unlink_external_artifact | project_id, provider | none |
assign_insights | project_id, insight_ids | reason |
unassign_insights | project_id, insight_ids | none |
generate_evidence_story | project_id | none |
link_external_artifact stores Deck-side Jira or Linear metadata for artifacts that already exist. It does not create or modify provider artifacts.
/build/initiatives
Executes one initiative mutation action.
Supported actions
| Action | Required fields | Optional fields |
|---|---|---|
create | title | goal, owner_id |
update_title | initiative_id, title | none |
update_goal | initiative_id, goal | none |
update_status | initiative_id, status | none |
upsert_section | initiative_id, section_type, content | metadata |
set_segments | initiative_id, segment_ids | none |
/build/opportunities
Executes one opportunity mutation action.
Supported actions
| Action | Required fields | Optional fields |
|---|---|---|
archive | opportunity_id | none |
set_segments | opportunity_id, segment_ids | none |
link_initiative | opportunity_id, initiative_id | none |
create_initiative_from_opportunity | opportunity_id, title | goal, owner_id |
Idempotency behavior
- Same key + same normalized payload returns the original response with
idempotency.replayed=true - Same key + different payload returns
409 IDEMPOTENCY_CONFLICT - Reusing the same key while the original matching request is still running also returns
409 IDEMPOTENCY_CONFLICT
/subthemes
Lists subthemes synthesized from theme-level or segment-level analysis.
Subthemes are only available to organizations eligible for the subthemes feature.
Query parameters
| Parameter | Type | Notes |
|---|---|---|
limit | integer | default 50, max 100 |
offset | integer | zero-based offset |
theme_id | string | filter to a specific theme |
segment_id | string | filter to a specific segment when segments are enabled |
q | string | case-insensitive search against pattern name or description |
Subtheme fields
| Field | Meaning |
|---|---|
id | Subtheme ID |
name | Subtheme name |
description | Subtheme description |
insight_count | Number of contributing insights |
sentiment_breakdown | Positive, negative, and neutral counts |
theme_id | Parent theme ID |
theme_name | Parent theme name |
theme_color | Parent theme color |
segment_id | Segment context when present |
synthesized_at | Timestamp of the synthesis used |
/subthemes/{subthemeId}
Returns full synthesized subtheme detail.
Additional fields:
insight_idsblocksthemerelated_subthemes
Use this endpoint when you need more than the summary list and want the full subtheme context.
/competitors
Reads and writes the in-app Competitors graph. The workspace must have Competitors enabled. Otherwise Deck returns 403 FEATURE_NOT_AVAILABLE.
This graph is separate from your first-party themes and insights. List responses include graph status (empty, importing, running, ready, or failed), optional last_job, and competitor { id, name, domain } rows.
The Public API does not enable or disable Competitors, and it does not delete a tracked competitor. Those actions live in Settings → Competitors.
Writes require write scope and an Idempotency-Key header (1–128 characters). Members can start research, add a competitor, and promote a finding. Only organization admins can change the check-in schedule (POST /competitors/checkins returns 403 ROLE_NOT_ALLOWED for members).
POST /competitors/research
Merges the posted competitors with the set you already track and appends new reviews onto the published graph — the same behavior as adding a competitor in the app. An empty competitors array is 400. Research that is already running is 409 CONFLICT. A successful start returns 202 with { job_id, status }.
{
"competitors": [{ "name": "Rival", "website": "rival.com" }],
"reviews_per_source": 30
}reviews_per_source is optional (10–100). The default is 30.
Poll GET /competitors/research/{jobId} until the job is ready or failed. If the workspace does not have enough credits, the job response is still HTTP 200 with error_code: CREDIT_LIMIT_EXCEEDED. The last published graph stays visible while research runs.
POST /competitors/promote
Promotes a competitor subtheme or opportunity teaser into Opportunity Backlog.
{
"source_type": "subtheme",
"source_id": "…",
"title": "Fix onboarding",
"include_first_party_feedback": true
}source_type is subtheme or opportunity_teaser. Promoting the same source again returns the existing backlog item (existing: true) instead of creating a duplicate.
GET /competitors/checkins
Lists check-in runs newest first. The response is { data, pagination }. Pass the last run id as cursor. limit is 1–100 and defaults to 50.
Each run includes status, cadence, timestamps, and a snapshot. Empty runs store { "changed": false } only. When the snapshot has diffs, rank closer to 0 means the theme moved up; an impactScore increase of 1 means the opportunity is more valuable.
GET /competitors/checkins/latest
Returns the most recent check-in run, or null if none exist.
POST /competitors/checkins
Organization admins enable or disable periodic check-ins. This does not start a scrape immediately.
{
"enabled": true,
"cadence": "monthly"
}cadence is weekly, monthly, or quarterly (7 / 30 / 90 elapsed days). If you enable check-ins without a cadence, Deck uses monthly. Check-ins require Competitors to be on.
Use /openapi for the exact field types. Competitor opportunity-teaser detail pages 10 insights at a time with a cursor. Check-in lists paginate by run id.
Pagination
Cursor pagination
Used by:
- themes
- insights
- projects
- initiatives
- competitor opportunity-teaser insights
- competitor check-in runs
Response shape:
{
"pagination": {
"next_cursor": "opaque-string-or-null",
"has_more": true
}
}Offset pagination
Used by:
- subthemes
Response shape:
{
"pagination": {
"total": 120,
"has_more": true,
"offset": 0,
"limit": 50
}
}OpenAPI spec
The latest API contract is available at:
/api/v1/openapiUse it when you want the exact field types or need to generate a client.