Read UserSay from your agent
Read UserSay feedback and insights from your own AI agents.
UserSay exposes the same read-only data surface over HTTP and MCP. It is built for analysis: connect Claude, Cursor, or your own agent; let it inspect what users said and what Sarah distilled across sessions.
Tool surface
There are three tools:
| Tool | Use it for |
|---|---|
get_context | List projects, then orient on one project: product context, watchlist, enabled Moments, data counts, available datasets. |
query_data | Search or list data from one dataset: feedback or insights. |
get_item | Open one result in full: a transcript or a signal with all its quotes. |
query_data has two datasets:
| Dataset | Contains |
|---|---|
feedback | Raw sessions and transcript search hits. |
insights | Signals distilled across sessions, with verbatim quotes. |
The public surface is intentionally read-only. Project setup, Moment editing, and destructive operations stay in the dashboard.
Authentication
Generate an API key under Settings → Developers → API & MCP access. Keys
start with us_live_.
Send the key in any of these places:
Authorization: Bearer <YOUR_USERSAY_KEY>
X-UserSay-Key: <YOUR_USERSAY_KEY>
?key=<YOUR_USERSAY_KEY>Keys are user-scoped: they can read projects the key owner owns or has been invited to as a shared member. Rate limit is 120 calls/minute per key.
HTTP API
Base URL:
https://www.usersay.ai/api/v1Call a tool by POSTing JSON to /api/v1/<tool>.
curl -X POST https://www.usersay.ai/api/v1/query_data \
-H "X-UserSay-Key: <YOUR_USERSAY_KEY>" \
-H "Content-Type: application/json" \
-d '{
"projectSlug": "my-product",
"dataset": "insights",
"limit": 20
}'Errors return:
{ "error": "Invalid insight status", "validValues": ["active", "archived", "all"] }MCP server
Hosted MCP URL:
https://www.usersay.ai/mcp?key=<YOUR_USERSAY_KEY>Cursor-style config:
{
"mcpServers": {
"usersay": {
"url": "https://www.usersay.ai/mcp",
"headers": {
"Authorization": "Bearer <YOUR_USERSAY_KEY>"
}
}
}
}The MCP server supports initialize, tools/list, and tools/call. Tool calls
return both structuredContent and a text JSON fallback for older clients.
get_context
Use this first. Omit projectSlug to list accessible projects:
{}Pass projectSlug to get project context:
{ "projectSlug": "my-product" }Response includes:
{
projects: Array<{
slug: string;
name: string;
role: "owner" | "shared";
productName: string | null;
productDescription: string | null;
installed: boolean;
}>;
currentProject?: {
slug: string;
name: string;
productContext: string | null;
founderLanguage: string;
watchlist: {
understanding: string[];
conversion: string[];
retention: string[];
custom: string[];
};
enabledMoments: Array<{
id: string;
name: string;
category: string;
depth: "quick" | "deep";
description: string;
}>;
counts: {
sessions: number;
terminalSessions: number;
messages: number;
signals: number;
};
datasets: Array<{ id: "feedback" | "insights" }>;
};
}query_data
Input:
{
projectSlug: string;
dataset: "feedback" | "insights";
q?: string;
since?: string; // YYYY-MM-DD
until?: string; // YYYY-MM-DD
kind?: string;
status?: string;
limit?: number; // max 100
}The meaning of kind and status depends on the dataset:
| Dataset | kind | status |
|---|---|---|
feedback | Moment id, such as cancel or a custom Moment id | completed, abandoned, all |
insights | Signal kind: problem, wish, persona, use_case, pricing, competitor | active, archived, all |
Date filters are dataset-specific:
feedbackuses terminal session completion dates in your configured timezone.insightsuses when the latest supporting quote was said by the user.
Examples:
{
"projectSlug": "my-product",
"dataset": "feedback",
"q": "pricing refund",
"since": "2026-07-01",
"status": "all",
"limit": 20
}{
"projectSlug": "my-product",
"dataset": "insights",
"kind": "problem",
"status": "active",
"limit": 30
}{
"projectSlug": "my-product",
"dataset": "insights",
"kind": "problem",
"limit": 10
}Response:
{
project: { slug: string; name: string };
dataset: "feedback" | "insights";
summary: string;
totals: Record<string, number>;
items: Array<{
id: string;
type: "session" | "signal";
title: string;
summary: string | null;
quote: string | null;
occurredAt: string | null;
status: string | null;
kind: string | null;
confidence?: string | null;
dashboardUrl?: string;
metadata?: Record<string, unknown>;
}>;
}get_item
Input:
{
"projectSlug": "my-product",
"id": "<id from query_data>"
}get_item can return:
- A full session transcript with insights and end-user metadata.
- A signal with all its quotes and source session ids.
Recommended agent flow
Use this in your agent prompt:
You have access to UserSay's MCP server.
Workflow:
1. Call get_context first. If there are multiple projects, ask which product to analyze.
2. Use query_data to inspect one dataset:
- feedback: raw sessions and transcript search
- insights: distilled signals
3. Use get_item only for the few results you need to verify with full evidence.
4. Prefer quotes and source session ids over vague summaries.
5. Do not ask UserSay to mutate project settings; this MCP surface is read-only.Migration from the old tool list
The previous public surface exposed granular tools such as list_projects,
query_sessions, get_signal, and create_moment. They were replaced by the
three-tool read-only surface to reduce MCP model confusion and to make
signals first-class analysis data.
Old → new:
| Old tool | New path |
|---|---|
list_projects, get_project_context | get_context |
query_sessions, search_sessions | query_data with dataset: "feedback" |
get_session | get_item |
list_signals, search_signals | query_data with dataset: "insights" |
get_signal | get_item |