Skip to content

Canviq MCP and AI Agent Integration Guide

Complete reference for connecting AI agents and automated pipelines to Canviq via the Model Context Protocol (MCP) server.


Overview

The Canviq MCP server exposes every capability available in the dashboard as an MCP tool. Agents can create surveys, query PMF scores, stream responses in real time, and trigger automation workflows. All operations use the same authentication layer as the REST API.

Server URL: https://canviq.app/api/mcp
Transport: Streamable HTTP (SSE for streaming tools)
Protocol: MCP 2025-03


Authentication

Creating an API key

  1. Sign in to your Canviq dashboard.
  2. Go to Settings > API Keys.
  3. Click Create key.
  4. Select the key type:
  5. Live (pk_live_...): Accesses production data
  6. Test (pk_test_...): Accesses sandbox data only
  7. Select a policy (see Permission scopes).
  8. Copy the key. It is shown exactly once and cannot be retrieved again.

Store the key in a secrets manager (AWS Secrets Manager, Vault, 1Password, etc.). Never commit it to source control.

Using the key

Pass the key in the Authorization header on every request:

Authorization: Bearer pk_live_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345

For MCP clients that use the api-key configuration field, set:

{
  "mcpServers": {
    "canviq": {
      "url": "https://canviq.app/api/mcp",
      "apiKey": "pk_live_..."
    }
  }
}

Permission scopes

Scope What it allows
surveys:read List and view surveys
surveys:write Create, update, and delete surveys
surveys:publish Publish and unpublish surveys
responses:read Query and stream survey responses
responses:export Export response data
analytics:read Access PMF scores, trends, and sentiment
automation:read View workflow definitions
automation:write Create and modify workflows
automation:execute Trigger workflows manually
admin:agents Manage other agent identities
admin:audit Read the audit log

Read-only access to PMF data (surveys:read, responses:read, analytics:read) is available on the Free tier. Write access (surveys:write, responses:export, automation:write) requires the Growth plan.

Key format

Keys follow the pattern pk_{env}_{32-char-base64url-secret}. The first 14 characters are used for O(1) database lookup. The full key is verified against an Argon2id hash stored at rest. The plaintext is never persisted.


Rate limits

Tier Requests/minute Concurrent streams Export size
Free / Startup 30 1 1,000 rows
Growth 300 5 10,000 rows
Scale 1,000 20 100,000 rows

When you exceed the rate limit, the server returns 429 Too Many Requests with a Retry-After header indicating how many seconds to wait. Use exponential backoff for retries:

1st retry: wait Retry-After seconds
2nd retry: wait 2× Retry-After seconds
3rd retry: wait 4× Retry-After seconds

Input modes

Structured mode

Pass a JSON object matching the tool's input schema. Validated before execution.

{
  "title": "Feature Satisfaction Survey",
  "questions": [
    {
      "type": "rating",
      "text": "How satisfied are you with the new dashboard?",
      "scale": { "min": 1, "max": 5 }
    },
    {
      "type": "free_text",
      "text": "What could we improve?",
      "optional": true
    }
  ],
  "targeting": {
    "attributes": { "plan": "pro" },
    "behavior": { "last_active_days": 7 }
  }
}

Natural language mode

Describe what you want in plain text. The server parses your description into a structured schema using Claude Sonnet.

"Create an NPS survey for users who completed onboarding this week.
Ask them to rate likelihood to recommend, then ask what one thing
we could do to improve their experience. Target iOS users only."

When input is ambiguous, the server returns a clarification_needed response rather than guessing:

{
  "status": "clarification_needed",
  "parsed": { "...": "what was understood" },
  "ambiguities": [
    {
      "field": "targeting.signup_date",
      "question": "Should 'this week' include today or start from Monday?",
      "options": ["last_7_days", "current_week_monday_start"]
    }
  ]
}

Respond with a clarifying answer or supply the full structured schema to proceed.


Tool reference

Survey management

survey_create

Creates a new survey.

Required scope: surveys:write

Input:

Parameter Type Required Description
title string Yes Survey display name
questions array Yes Question definitions (see below)
targeting object No Audience filter rules
trigger object No Event-based trigger configuration
theme object No Visual customization overrides

Question types:

Type Description
sean_ellis The core PMF question with three named options
nps 0–10 numeric scale
rating 1–5 or 1–10 emoji or numeric scale
multiple_choice Single or multi-select list of options
free_text Open-ended text response

Returns: SurveyPayload (the created survey including its generated ID).


survey_get

Fetches a single survey by ID.

Required scope: surveys:read

Input: { "survey_id": "string" }

Returns: SurveyPayload (the requested survey).


survey_update

Updates a survey's title, questions, or targeting. Changes take effect immediately for surveys in draft status. For live surveys, changes require republishing.

Required scope: surveys:write

Input: { "survey_id": "string", ...fields to update }


survey_publish

Makes a survey live. Users with matching trigger conditions can now see it.

Required scope: surveys:publish

Input: { "survey_id": "string" }


survey_unpublish

Pauses a live survey. Users will not see it until republished. Existing responses are preserved.

Required scope: surveys:publish

Input: { "survey_id": "string" }


survey_delete

Permanently deletes a survey and all associated responses. Irreversible.

Required scope: surveys:write

Input: { "survey_id": "string" }


surveys_list

Returns all surveys for the authenticated organization.

Required scope: surveys:read

Input: { "status": "draft | live | paused | archived" } (optional filter)

Returns: SurveyPayload[] (all surveys for the organization, filtered by status if provided).


Response access

responses_query

Queries survey responses with optional filters.

Required scope: responses:read

Input:

Parameter Type Required Description
survey_id string Yes Survey to query
from ISO 8601 date No Start of date range
to ISO 8601 date No End of date range
answer_filter object No Filter by a specific question's response value
limit integer No Max responses per page (default 100, max 1000)
cursor string No Pagination cursor from previous response

Returns: { responses: ResponseRecord[], next_cursor: string | null } (the matching responses for the page, plus a cursor for the next page or null if this is the last page).


responses_stream_subscribe

Opens a real-time stream of new responses as they arrive.

Required scope: responses:read

Input: { "survey_id": "string", "filters": object } (filters optional)

Behavior: Returns a stream ID. The MCP server sends response.created events over the SSE connection. Call responses_stream_unsubscribe with the stream ID to stop.


responses_export

Exports all responses for a survey as a flat record set.

Required scope: responses:export

Input: { "survey_id": "string", "format": "json | csv" }

Returns: string (a temporary download URL for the exported file, valid for 1 hour).


Analytics

analytics_summary

Returns the current PMF score and response counts.

Required scope: analytics:read

Input: { "survey_id": "string" }

Returns: PmfSummary (the current PMF score and response counts, see schema below).

{
  "pmf_score": 43.2,
  "response_count": 186,
  "very_disappointed_count": 80,
  "somewhat_disappointed_count": 68,
  "not_disappointed_count": 38,
  "last_updated": "2026-06-01T00:00:00Z"
}

analytics_trend

Returns PMF score over time, grouped by week.

Required scope: analytics:read

Input:

Parameter Type Description
survey_id string Survey ID
from ISO 8601 date Start of range (default: 90 days ago)
to ISO 8601 date End of range (default: today)

Returns: { weeks: [{ week_start: string, pmf_score: number, response_count: number }] } (PMF score grouped by week for the requested date range).


analytics_cohort

Returns the segment breakdown for Very vs. Somewhat Disappointed cohorts, including open-text themes.

Required scope: analytics:read

Input: { "survey_id": "string" }


analytics_sentiment

Returns sentiment analysis from Claude Haiku applied to open-text responses.

Required scope: analytics:read

Input: { "survey_id": "string", "question_id": "string" }


Distribution

targeting_set

Configures audience filter rules for a survey.

Required scope: surveys:write

Input:

{
  "survey_id": "string",
  "rules": {
    "attributes": { "plan": "pro", "country": "US" },
    "behavior": { "last_active_days": 14 },
    "locale": ["en", "fr"]
  }
}

trigger_configure

Sets the event-based trigger rule for a survey.

Required scope: surveys:write

Input:

{
  "survey_id": "string",
  "event": "project_created",
  "min_count": 3,
  "cooldown_days": 90
}

fatigue_configure

Sets per-user survey frequency limits.

Required scope: surveys:write

Input:

{
  "survey_id": "string",
  "max_per_user": 1,
  "cooldown_days": 90
}

Automation

workflow_create

Creates an automation workflow triggered by a survey response event.

Required scope: automation:write

Input:

{
  "name": "Low NPS Follow-Up",
  "trigger": {
    "event": "response.created",
    "survey_id": "nps_survey_123",
    "conditions": [{ "question": "nps_score", "operator": "lt", "value": 7 }]
  },
  "actions": [
    {
      "type": "create_ticket",
      "config": { "system": "zendesk", "priority": "high" }
    }
  ]
}

Built-in action types:

Action Description
send_email Send a templated email to the respondent or a team address
create_ticket Create a support ticket in an integrated system
update_crm Update a user record in your CRM
tag_user Apply a tag to the user for future targeting
trigger_survey Launch a follow-up survey after a delay
call_webhook POST to a custom URL with the response payload
update_attribute Modify a user attribute for future targeting

Error codes

All errors return JSON with a code and message field.

HTTP status Code Meaning
400 VALIDATION_ERROR Input did not pass schema validation. details field lists each invalid field.
401 UNAUTHORIZED Missing or invalid API key.
403 FORBIDDEN Key does not have the required scope for this operation.
404 NOT_FOUND Resource does not exist or your key cannot access it.
409 CONFLICT Operation conflicts with current state (e.g. publishing an already-live survey).
422 CLARIFICATION_NEEDED Natural language input was ambiguous. See ambiguities field.
429 RATE_LIMITED Rate limit exceeded. See Retry-After header.
500 INTERNAL_ERROR Server error. Retry after a short delay.

Natural language examples

The following queries work in natural language mode:

"What is my current PMF score?"
→ calls analytics_summary for the most recent active survey

"Show me the trend for the last 3 months"
→ calls analytics_trend with from = 3 months ago

"Create a quick NPS survey for users on the Pro plan"
→ calls survey_create with type=nps and targeting.attributes.plan=pro

"How many responses have we gotten this week?"
→ calls responses_query with from = start of current week, count only

"Pause the onboarding survey"
→ calls survey_unpublish for a survey matching "onboarding" in title

"Export all responses from the Q2 PMF survey as CSV"
→ calls responses_export with format=csv

Example: Claude Desktop configuration

{
  "mcpServers": {
    "canviq": {
      "url": "https://canviq.app/api/mcp",
      "apiKey": "pk_live_your_key_here"
    }
  }
}

Once connected, you can ask Claude:

"Query my Canviq PMF score and summarize the top themes from the Very Disappointed cohort."