POST /projects/research endpoint to create a project and kick off a research pipeline in a single call. The list endpoint is optimized for speed and returns quickly even when you have many projects.
Create a project
string
required
Display name for the project. Used to generate the URL slug and the normalized
clean_title.string
Free-text description of what you’re building. Defaults to an empty string.
Idempotent by idea. Submitting the same idea twice from the same account does not create a duplicate. The server collapses both calls to the same project — the second request returns the existing project’s
id with a refreshed updated_at, surfacing it at the top of your recent list. Two distinct ideas that happen to share a short_title remain separate projects.201 response
Immediately after the project row is persisted, the server kicks off a background canonical-knowledge lookup that pre-populates the project with suggested competitors and features drawn from similar prior research. Suggestions are delivered as a This step is best-effort. If the canonical layer is empty, the embedding fails, or no similar prior research exists, no event is emitted (or the event arrives with empty
project.seeded event on the project channel — not in this response. Subscribe to the channel using the project UUID as entity_id to receive them, or read them off the timeline returned by GET /projects/{id}/home.The event payload looks like:competitors and features arrays). Project creation never blocks on or fails because of seeding.string
required
UUID for the new project.
string
required
URL-safe identifier derived from
title.string
required
Always
draft on creation. Transitions: draft → researching → ready → building, plus blocked and done as manual targets.number
required
Starts at
0. Updated after each completed research run.string
Normalized version of the title produced by the LLM normalization pass.
string
Single sentence summary generated during normalization.
string
Either
complete or failed.Create a project and start research atomically
Use this endpoint instead of callingPOST /projects followed by POST /research. It creates the project row and enqueues the first research job in a single transaction.
Supply an
Idempotency-Key header to safely retry the request on network failure. The server replays the original response without starting a duplicate pipeline.string
required
The product idea to research. Maximum 5,000 characters.
string
Optional extended context. Defaults to an empty string.
string
default:"market"
Research mode. Either
market (full market analysis) or feature (feature-focused analysis that auto-chains into feature deep research on completion).- 202 — new pipeline
- 200 — cache hit
GET /research/{job_id}/events.List projects
Returns a paginated list of your projects, ordered by most recently updated. This is the fastest way to get an overview of your projects.number
default:"5"
Maximum items to return. Upper bound: 100.
number
default:"0"
Number of items to skip for pagination.
200 response
object[]
required
boolean
required
true if additional pages exist. Use offset to paginate.Get a project
Returns the project detail row along with counts and inline summaries of recent research and build graphs.200 response
404 if the project does not exist or belongs to another user.
Get the full project (mobile-optimized)
Returns the project, its latest completed research, any in-progress research job, the latest build graph with full node tree, and similar projects — all in one call.200 response
Get the project home (single round-trip aggregator)
Returns everything the mobile project tab needs in a single call: the project, its short digest, recent research, build graphs, briefs, voice clips, agents, Forge runs, and the most recent timeline events. Pair this with a single WebSocket subscription on theproject channel using the returned ws.last_event_seq as cursor to keep the screen live.
200 response
object
required
Compact project block.
short_title and description come from the summary projection; status is the current state (draft, researching, ready, building, blocked, done).string
One-line natural language summary of recent project activity. Empty string when nothing notable has happened.
object[]
Up to the 10 most recent research runs. Each entry carries
id, status, version, completed_at, the overall quality.overall_score, and the first three competitor names from incumbents.object[]
Up to the 5 most recent build graphs with
node_count and expanded_count. Subscribe to the build channel for live updates.object[]
Up to the 10 most recent briefs with
audience (template name) and a share_url when a share token is set.object[]
Voice clips threaded to this project. Each entry has
slug, target_type, target_id, and created_at.object[]
Up to 20 active spawned agents attached to this project. Each entry includes the natural-language
goal, schedule_cron (cron, ISO interval, or on:event sentinel), and last/next run timestamps.object[]
Up to 10 most recent Forge runs scoped to this project, with
pr_url populated when the run created a GitHub pull request.object[]
Up to 50 most recent fanned-in events from research, build, brief, Forge, chat, and agent runs. Each entry has
ts, kind, a short summary (truncated to 200 chars), and an optional ref (entity UUID).object
required
Subscription coordinates for the
project channel. Pass last_event_seq as cursor in your subscribe frame to resume from the exact point the home payload was generated.404 if the project does not exist or belongs to another user.
Get the public preview
Returns a safe public subset of the project — no user information. Accessible without authentication. Use this for social sharing, App Clip previews, and OG tag generation.200 response
Update a project
Accepts any subset of fields. Passnull to clear a field.
string
Updated project title.
string
Updated description.
string
Manually override the project state. Valid values:
draft, researching, ready, building, blocked, done.string
GitHub repository in
owner/repo format. Used by Forge for code generation. Pass null to clear.200.
Delete a project
200 response
Get research data for a project
Returns the full payload of the latest completed research run attached to this project.200 response
Get the build graph for a project
Returns the latest build graph with the full node tree assembled underroots.
This endpoint returns nodes under
roots. The direct GET /build-graphs/{id} endpoint uses nodes instead.200 response
List feature research jobs for a project
Returns the latest 10 feature deep research jobs associated with this project.200 response
Get per-tab research data
Extracts a single column from the research table for a given display tab. Useful for populating individual UI tabs without fetching the entire research payload.string
required
The tab to fetch. One of:
incumbents, emerging, features, signals, white_spaces, interpretation, quality, sources, recommendations, existential_risks, build_readiness.id and the requested column. Returns 400 for an unrecognized tab name.
Semantic search
Search across your projects (and any public projects you have access to) using natural language. A query like “mobile payments for gig workers” surfaces semantically related projects, not just exact keyword matches.string
required
Search query. Between 1 and 500 characters.
number
default:"10"
Number of results to return. Upper bound: 50.
200 response
number
required
Cosine similarity score between 0 and 1. Higher is more relevant.
boolean
required
true if the result belongs to the authenticated user.503 if the semantic search service is temporarily unavailable. Retry after a moment.
Publish a project
Flips a project to public-by-opt-in and returns its shareable URL. Projects are private by default; publishing makes the project readable atGET /public/p/{slug} without authentication. The slug is derived from the kebab-cased title plus a 6-character salted hash so unpublished or unknown slugs cannot be brute-forced.
Re-publishing an already-published project (or publishing an unpublished one that previously had a slug) returns the same URL — slugs are deterministic and durable.
Requires a real (non-anonymous) account. Anonymous principals receive 401. Persists a project.published event on the project channel.
string
required
UUID of the project to publish.
object
Per-section visibility toggles. Omitted fields fall back to defaults.
200 response
boolean
required
Always
true on success.string
required
The deterministic public slug. Stable across re-publishes.
string
required
Absolute URL of the public project page.
400— unknown key inoptions, wrong value type, or invalidshow_handle_or_anonchoice.401— anonymous principal (publishing requires a real account).404— project does not exist or belongs to another user.
Unpublish a project
Flipsis_public back to false. The slug and published_at timestamp are preserved as an audit trail, so re-publishing later returns the same URL.
Persists a project.unpublished event on the project channel.
200 response
404 if the project does not exist or belongs to another user.
Update public visibility options
Updatespublic_options without changing is_public or published_at. Use this to tweak what’s visible on an already-public page.
string
required
UUID of the project.
object
required
New
public_options payload. Same fields as the publish endpoint. Unknown keys return 400.200 response
400— unknown key inoptionsor invalid value type.404— project does not exist or belongs to another user.