Documentation

Learn how Pollenate works — whether you're setting up your first feedback widget, building a survey, or connecting it to your app. No coding experience required to get started.

💡How Pollenate Works

Pollenate helps you hear from the people who use your product, visit your website, or walk through your door. It collects their feedback — ratings, comments, survey responses — and organizes it all in one dashboard so you can spot patterns and take action.

🏢

Organizations & Brands

Your organization is your team's account — it holds everything. Inside it, you can create brands to separate feedback by product, department, or location. For example, a hospital group might have one brand per clinic.

📬

Inboxes

Think of an inbox like a mailbox for feedback. You might have one inbox for your website, another for your mobile app, and a third for post-visit surveys. Each inbox has its own unique key so feedback gets routed to the right place.

🔌

Widgets

A widget is a small feedback prompt that appears on your website — like a "How did we do?" button or a row of star ratings. You add it by pasting a short snippet of code. No app installation or software download needed.

📋

Feedback Pages

Feedback Pages are standalone survey forms with their own web address. Great when you want to ask multiple questions, share a link via email or text message, or print a QR code on a flyer for in-person events.

🔑

API Keys

An API key is like a password that lets your website talk to Pollenate securely. You create one in Settings → API Keys and give it to your widget so it has permission to send feedback to your account.

📊

Dashboard & Reports

Everything your users submit shows up in your dashboard in real time. You can filter by date, score, or inbox, search by keyword, and watch trends over time — no spreadsheets required.

The big picture

Create an inboxAdd a widget or surveyPeople give feedbackReview in your dashboardTake action

🚀Quick Start

Get up and running with Pollenate in under 5 minutes. Collect your first piece of feedback today.

🗣️ In plain English: You paste a small snippet of code into your website. It adds a feedback button that visitors can click to rate their experience or leave a comment. Their responses appear in your Pollenate dashboard instantly. If you're not comfortable editing code, ask your web developer to add this — it only takes a minute.

1. Add the widget script to your HTML
<script 
  src="https://pollenate.dev/widget.js"
  data-inbox-key="YOUR_INBOX_KEY"
  data-api-key="YOUR_API_KEY"
  data-type="emoji"
  async
></script>

That's it! The widget will automatically appear on your page. You'll need both an inbox key (from the Inboxes page) and an API key with the "collect" scope (from Settings → API Keys).

🔐Authentication

🗣️ In plain English: Authentication is how Pollenate knows the feedback is coming from your website and not someone else's. It uses special keys — kind of like a password — that you copy from your Pollenate settings and paste into your widget code. There are two kinds: one for your website's feedback widget, and one for managing your account through code.

Pollenate uses two authentication methods depending on your use case:

API Keys (for widgets & integrations)

Use API keys for server-to-server integrations and widget authentication. Pass your key in the X-Pollenate-Key header.

curl -X POST https://api.pollenate.dev/collect \
  -H "X-Pollenate-Key: pk_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{"type": "emoji", "score": 5, "comment": "Great!"}'

JWT Bearer Token (for dashboard API)

Use JWT tokens for authenticated user sessions. Tokens are obtained via OTP email verification.

curl https://api.pollenate.dev/organizations \
  -H "Authorization: Bearer eyJhbGc..."

📦Embed Widget

🗣️ In plain English: A widget is a small interactive element that appears on your website — like a row of smiley faces or a star rating — asking visitors for their opinion. You add it by pasting one line of code into your website. It works on any website: WordPress, Squarespace, Wix, custom-built sites, and more. Your web developer can set it up in under a minute.

The Pollenate widget can be embedded in any website with a single script tag.

Script Attributes

AttributeRequiredDescription
data-inbox-keyYesYour inbox's public key (from the Inboxes page)
data-api-keyYesYour API key with "collect" scope (from Settings → API Keys)
data-typeNoWidget type: emoji, stars, thumbs, nps, csat, text
data-positionNoPosition: bottom-right, bottom-left, top-right, top-left
data-themeNoTheme: light, dark, or custom theme ID
data-promptNoCustom prompt text
data-inlineNoSet to "true" for inline widget (no floating button, form shows directly on page)
data-containerNoCSS selector for a container element to mount the widget into (e.g. "#feedback"). Implies inline mode.
data-contextNoJSON object with custom context metadata (e.g. '{"page":"pricing","userId":"123"}')
data-debugNoSet to "true" for console logging (default: false)
data-track-impressionsNoSet to "false" to disable page view / impression tracking (default: true)
data-strip-query-paramsNoSet to "true" to strip query parameters from tracked URLs for privacy (default: false). Only the domain and path are recorded.

🎨Widget Types

Pollenate offers six different ways to ask for feedback. Choose the one that fits what you're trying to learn. Not sure which to pick? Emoji and Stars work great for most situations.

😞 😐 🙂 😄 🤩

Emoji

5-point emoji scale for quick sentiment. Best for: getting a fast read on how people feel.

type="emoji"
⭐⭐⭐⭐⭐

Stars

Classic 5-star rating. Best for: product reviews, service quality, anything people are used to rating out of 5.

type="stars"
👍 👎

Thumbs

Simple up/down voting. Best for: "Was this helpful?" questions on docs, articles, or support pages.

type="thumbs"
0─────10

NPS

Net Promoter Score (0-10). Best for: measuring loyalty — "How likely are you to recommend us?"

type="nps"
1─────5

CSAT

Customer Satisfaction (1-5). Best for: measuring satisfaction after a specific interaction, like a support call or purchase.

type="csat"
💬

Text

Open-ended text feedback. Best for: collecting suggestions, bug reports, or detailed comments in the user's own words.

type="text"

🎭Customization

🗣️ In plain English: You can change the colors, fonts, and shape of the widget to match your website's look. If you don't want to touch code, you can create custom themes in the Pollenate dashboard under Brand Settings instead.

Customize the widget appearance using CSS variables or create custom themes in the dashboard.

CSS Variables
:root {
  --pollenate-primary: #f59e0b;
  --pollenate-primary-hover: #d97706;
  --pollenate-bg: #ffffff;
  --pollenate-text: #1e293b;
  --pollenate-border: #e2e8f0;
  --pollenate-radius: 12px;
}

⚛️React Package

For React apps, install @pollenate/react for a fully typed, native React component — no script tags or DOM manipulation needed.

Installation

Terminal
npm install @pollenate/react
# or
pnpm add @pollenate/react
# or
yarn add @pollenate/react

Quick Start

React Component
import { PollenateWidget } from '@pollenate/react';

export function App() {
  return (
    <PollenateWidget
      inboxKey="YOUR_INBOX_KEY"
      apiKey="YOUR_API_KEY"
      type="stars"
      theme="auto"
      onSubmit={(event) => {
        console.log('Feedback submitted:', event.detail);
      }}
    />
  );
}

Inline Widget

Add inline to render the widget directly in your page flow instead of as a floating button:

Inline Example
<PollenateWidget
  inboxKey="my-inbox"
  apiKey="pk_live_xxx"
  type="thumbs"
  inline
  prompt="Was this page helpful?"
/>

Custom Themed

Override colors, border radius, fonts, and dark-mode colors:

Themed Example
<PollenateWidget
  inboxKey="my-inbox"
  apiKey="pk_live_xxx"
  type="nps"
  theme="auto"
  primaryColor="#6366f1"
  borderRadius={12}
  buttonText="Submit Score"
  darkPrimaryColor="#818cf8"
  darkBgColor="#1e1b4b"
  hideBranding
/>

Props Reference

PropTypeDefaultDescription
inboxKeystringYour inbox key (required)
apiKeystringYour API key with collect scope (required)
typestringemojiemoji, stars, thumbs, nps, csat, or text
themestringlightlight, dark, or auto
inlinebooleanfalseRender inline instead of floating
positionstringbottom-rightFloating position (bottom-right, bottom-left, top-right, top-left)
promptstringCustom prompt text above the rating
contextobjectMetadata sent with each submission
primaryColorstringPrimary accent color (hex)
bgColorstringWidget background color (hex)
onSubmitfunctionCallback when feedback is submitted
onImpressionfunctionCallback on widget impression

📦 Full props list: See all available props including dark-mode overrides, animation, and branding options in the npm package README.

🛠️Custom Widgets

Want complete control over your feedback UI? Build your own custom widget using our REST API. This allows you to match your brand perfectly and integrate feedback into any user flow. If you're using React, consider the @pollenate/react package first — it handles all the API calls and UI for you.

💡 When to use custom widgets: Use the API directly when you need a fully custom UI, want to embed feedback in specific flows (like checkout), or integrate with frameworks we don't support yet.

Basic Example

Here's a complete example of a custom feedback form using vanilla HTML/JavaScript:

HTML + JavaScript
<form id="custom-feedback">
  <p>How would you rate your experience?</p>
  <div class="rating-buttons">
    <button type="button" data-score="1">😞</button>
    <button type="button" data-score="2">😐</button>
    <button type="button" data-score="3">🙂</button>
    <button type="button" data-score="4">😄</button>
    <button type="button" data-score="5">🤩</button>
  </div>
  <textarea name="comment" placeholder="Any additional feedback?"></textarea>
  <button type="submit">Submit</button>
</form>

<script>
let selectedScore = null;

document.querySelectorAll('[data-score]').forEach(btn => {
  btn.onclick = () => selectedScore = parseInt(btn.dataset.score);
});

document.getElementById('custom-feedback').onsubmit = async (e) => {
  e.preventDefault();
  
  const response = await fetch('https://api.pollenate.dev/collect', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Pollenate-Key': 'YOUR_API_KEY'
    },
    body: JSON.stringify({
      inboxKey: 'YOUR_INBOX_KEY',
      type: 'emoji',
      score: selectedScore,
      comment: e.target.comment.value,
      context: {
        page: window.location.pathname,
        userAgent: navigator.userAgent
      }
    })
  });
  
  if (response.ok) {
    alert('Thank you for your feedback!');
  }
};
</script>

React Example (DIY)

This shows how to build a completely custom feedback UI from scratch using the API directly. For a drop-in solution, see the React Package section above.

Custom React Component
import { useState } from 'react';

export function CustomFeedback() {
  const [score, setScore] = useState(null);
  const [comment, setComment] = useState('');
  const [submitted, setSubmitted] = useState(false);

  const handleSubmit = async () => {
    const response = await fetch('https://api.pollenate.dev/collect', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Pollenate-Key': process.env.POLLENATE_API_KEY
      },
      body: JSON.stringify({
        inboxKey: 'YOUR_INBOX_KEY',
        type: 'emoji',
        score,
        comment,
        context: { page: window.location.pathname }
      })
    });
    
    if (response.ok) setSubmitted(true);
  };

  if (submitted) return <p>Thanks for your feedback! 🎉</p>;

  return (
    <div className="feedback-widget">
      <p>How was your experience?</p>
      <div className="emojis">
        {['😞', '😐', '🙂', '😄', '🤩'].map((emoji, i) => (
          <button
            key={i}
            onClick={() => setScore(i + 1)}
            className={score === i + 1 ? 'selected' : ''}
          >
            {emoji}
          </button>
        ))}
      </div>
      <textarea
        value={comment}
        onChange={(e) => setComment(e.target.value)}
        placeholder="Tell us more..."
      />
      <button onClick={handleSubmit} disabled={!score}>
        Submit Feedback
      </button>
    </div>
  );
}

API Request Format

FieldTypeRequiredDescription
inboxKeystringYesYour inbox's public key
typestringYesemoji, stars, thumbs, nps, csat, or text
scorenumberNo*0-10 for NPS, 1-5 for others, 0/1 for thumbs
commentstringNo*Text feedback (max 2000 chars)
contextobjectNoCustom metadata (page URL, user ID, etc.)

* At least one of score or comment is required

Score Ranges by Type

emoji

Score 1-5 (😞 to 🤩)

stars

Score 1-5 (⭐ to ⭐⭐⭐⭐⭐)

thumbs

Score 0 (👎) or 1 (👍)

nps

Score 0-10

csat

Score 1-5

text

No score, comment only

Using Context for Insights

The context field lets you attach metadata to each feedback submission. This enables powerful filtering and analysis in your dashboard.

// Example context object
{
  "context": {
    "page": "/checkout",
    "userId": "user_abc123",
    "plan": "pro",
    "feature": "dark-mode",
    "experiment": "new-pricing-v2",
    "sessionId": "sess_xyz789"
  }
}

📋Feedback Pages

🗣️ In plain English: Feedback Pages are like online surveys. You build a form with questions, and Pollenate gives you a link you can share anywhere — by email, text message, on social media, or even printed as a QR code on a poster or business card. No website or coding required. Respondents just open the link and fill it out.

Feedback Pages are standalone, branded feedback forms that you create through the Pollenate dashboard. Unlike widgets (which embed into existing pages), Feedback Pages are hosted at their own URL and can collect multi-question surveys from users.

💡 When to use Feedback Pages vs. Widgets: Use widgets when you want to collect quick, single-question feedback inline on your existing website. Use Feedback Pages when you need a multi-question form, want to share via QR code or printed materials, or need a standalone survey that doesn't require embedding on an existing site.

Key Features

🛠️ Visual Form Builder

Drag-and-drop editor with 7+ question types. Add, edit, delete, and reorder questions with a live preview.

🎨 Custom Branding

Match your brand with custom accent colors, background colors, and brand association. Configurable thank-you messages.

📱 Multiple Sharing Options

Share via direct link, QR code (PNG/SVG), email signature button, embeddable iframe, or print-ready flyer.

📊 Built-in Analytics

View response counts, page views, conversion rates, daily trends, and per-question breakdowns with score distributions.

🔨Form Builder

The form builder lets you create multi-question feedback forms using a visual editor. Each question has a type, prompt, optional description, and can be marked as required.

Supported Question Types

TypeAPI ValueScore RangeDescription
⭐ Star Ratingstars1–5Classic 5-star rating
😄 Emojiemoji1–55-point emoji sentiment scale
👍 Thumbsthumbs0–1Simple thumbs up/down
📊 NPSnps0–10Net Promoter Score
😊 CSATcsat1–5Customer Satisfaction score
💬 TexttextOpen-ended text response (single-line or multi-line, with optional validation)
🔘 Single Choicesingle_choiceSelect one option from a list
☑️ Multiple Choicemulti_choiceSelect multiple options from a list
📧 EmailemailEmail address input with built-in format validation
📞 PhonephonePhone number with automatic formatting (US, international, or any)
# NumbernumberNumeric input with optional min/max, step, and unit label
📅 DatedateDate picker with optional time component
▾ DropdowndropdownSingle selection from a dropdown list (compact for 5+ options)
↕️ Rank OrderrankingDrag-and-drop to rank a list of options in preferred order
📝 Content Blockcontent_blockdisplay onlyRich markdown content between questions (instructions, context)
— Section Headersection_headerdisplay onlySection heading and divider to organize the form

Page Settings

SettingDescription
Title & DescriptionShown at the top of the public feedback page
URL SlugCustom URL path (e.g. /f/your-org/my-survey)
StatusDraft (private), Published (live), or Archived (closed)
Accent & Background ColorCustomize the page's accent and background colors to match your brand
Thank You MessageCustom message shown after submission, with optional redirect URL
Require Contact InfoAsk respondents for name and email before submitting
Allow AnonymousLet respondents skip contact info even if the field is shown
Single SubmissionLimit to one response per browser using local storage

📋Import from Google Forms

Already have a Google Form? You can migrate it to a Pollenate feedback page in a couple of clicks — no Apps Script, no OAuth, and no AI calls. Pollenate fetches the public form, parses its questions, and creates a matching feedback page for you to review and edit.

Steps

  1. In Google Forms, make sure the form is shared with Anyone with the link (Settings → Responses → toggle off "Restrict to users").
  2. Copy the form's URL. Both docs.google.com/forms/... and forms.gle/... short links work. Editor URLs (/edit) are normalized to /viewform automatically.
  3. In Pollenate, open Feedback Pages → click + New Page → choose the Google Forms tab.
  4. Paste the URL and click Import. You'll see a preview of the parsed title, description, and questions.
  5. Click Create Page to save. The new page opens in the editor where you can tweak anything before publishing.

Question Type Mapping

Google FormsPollenateNotes
SHORT_ANSWERtext (single-line)
PARAGRAPHtext (multi-line)
MULTIPLE_CHOICEsingle_choiceOptions preserved
CHECKBOXESmulti_choiceOptions preserved
DROPDOWNdropdownOptions preserved
LINEAR_SCALE (1–5)stars
LINEAR_SCALE (0–10)nps
LINEAR_SCALE (other)single_choiceNumeric options
DATEdate
SECTION_HEADER / PAGE_BREAKsection_header
IMAGE / VIDEOcontent_blockCaption text imported; media must be re-added
GRID / TIME / FILE_UPLOADNot importedSurfaced in a warning so you can re-add manually

What Doesn't Carry Over

The public Google Forms payload doesn't expose a handful of editor-only settings, so these aren't imported:

  • Confirmation / "form has been submitted" message
  • "Collect email addresses" setting
  • "Limit to one response per user" setting
  • Grid, time, and file-upload question types
  • Images and videos (caption text is imported)

Troubleshooting

  • "Form is not publicly accessible" — open the Google Form's sharing settings and make sure it's set to Anyone with the link. Forms restricted to a Google Workspace can't be imported.
  • "Could not find form data" — Google occasionally changes the embedded payload format. Try re-saving the form in Google Forms, or copy the questions manually using the form builder.
  • Some questions are missing — check the import warning for the list of unsupported types (grid, time, file upload). Re-add them manually after importing.

🔗Sharing & Embedding

Once your Feedback Page is published, you have multiple ways to share it with respondents.

🔗 Direct Link

Share the URL directly via email, messaging apps, or social media.

https://pollenate.dev/f/your-org/your-page-slug

📱 QR Code

Generate QR codes in PNG or SVG format at multiple sizes (128px to 1024px). Perfect for flyers, posters, table tents, and business cards.

GET https://api.pollenate.dev/qr?data=YOUR_PAGE_URL&size=256&format=png&margin=4

Parameters:
  data    - URL to encode (required)
  size    - Image size in pixels: 128, 256, 512, 1024 (default: 256)
  format  - Output format: png or svg (default: png)
  margin  - Quiet zone margin in modules (default: 4)

📧 Email Signature Button

Copy an HTML snippet that renders a branded button in your email signature. The button uses your page's accent color.

<table cellpadding="0" cellspacing="0" border="0">
  <tr>
    <td style="padding: 10px 0;">
      <a href="https://pollenate.dev/f/your-org/your-page"
         style="display: inline-block; padding: 8px 16px;
                background-color: #f59e0b; color: #ffffff;
                text-decoration: none; border-radius: 6px;
                font-family: Arial, sans-serif; font-size: 14px;">
        Share Your Feedback
      </a>
    </td>
  </tr>
</table>

🖥️ Embed on Website

Embed the full feedback page on your website using an iframe. Add ?embed=true to remove the header and footer.

<iframe
  src="https://pollenate.dev/f/your-org/your-page?embed=true"
  width="100%"
  height="600"
  frameborder="0"
  style="border: none; border-radius: 12px;"
  title="Feedback Form"
></iframe>

🖨️ Print Flyer

Generate a ready-to-print flyer with your page title, description, QR code, and a call-to-action. Uses the Print dialog for easy printing on any device. Great for clinics, restaurants, offices, and events.

⚙️Pages API

Manage Feedback Pages programmatically via the REST API. All endpoints require JWT Bearer token authentication.

GET/organizations/:orgId/feedback-pages

List all feedback pages. Supports filtering by status and brandId.

POST/organizations/:orgId/feedback-pages

Create a new feedback page with title, description, slug, status, and settings.

GET/organizations/:orgId/feedback-pages/:pageId

Get a feedback page with its questions and submission count.

PUT/organizations/:orgId/feedback-pages/:pageId

Update page settings (title, description, status, colors, submission settings, etc.).

POST/organizations/:orgId/feedback-pages/:pageId/questions

Add a question to the page.

GET/organizations/:orgId/feedback-pages/:pageId/stats

Get analytics: total responses, page views, conversion rate, daily trends, and per-question breakdowns.

GET/f/:orgSlug/:pageSlug

Public-facing feedback page (no auth required). Append ?embed=true for iframe mode.

Submit Responses (Public)

POST/f/:orgSlug/:pageSlug/submit

Submit responses to a published feedback page. Protected by Cloudflare Turnstile.

{
  "answers": {
    "question-id-1": { "score": 5 },
    "question-id-2": { "textValue": "Great experience!" },
    "question-id-3": { "multiValues": ["Search", "Referral"] }
  },
  "respondentName": "Jane Doe",
  "respondentEmail": "jane@example.com",
  "turnstileToken": "CAPTCHA_TOKEN"
}

API Overview

🗣️ In plain English: The API is a way for other software (your app, your website's code, or third-party tools) to talk to Pollenate directly — sending feedback, pulling reports, or managing inboxes without going through the dashboard. This section is primarily for developers.

The Pollenate API is a RESTful API that uses JSON for request and response bodies.

Base URLs
PRODhttps://api.pollenate.dev
DEVhttp://localhost:8787

For the complete API specification, see our OpenAPI 3.1 spec.

📥Feedback Collection

POST/collect

Submit feedback to an inbox. Requires API key authentication.

Request Body

{
  "type": "emoji",        // emoji, stars, thumbs, nps, csat, text
  "score": 5,             // 0-10 for nps, 1-5 for others
  "comment": "Loving it!",// Optional text feedback
  "context": {            // Optional metadata
    "page": "/checkout",
    "userId": "user_123"
  }
}

Response

{
  "id": "fb_abc123",
  "createdAt": "2026-02-04T12:00:00Z"
}

📬Inboxes

Inboxes are containers for feedback. Each inbox has a unique key used to submit feedback.

🗣️ In plain English: An inbox is like a labeled folder where feedback lands. You might have an inbox called "Website" for your homepage widget and another called "Post-Visit" for patient surveys. When you set up a widget or survey, you tell it which inbox to deliver feedback to using the inbox's unique key.

GET/organizations/:orgId/inboxes

List all inboxes in an organization.

POST/organizations/:orgId/inboxes

Create a new inbox.

GET/organizations/:orgId/inboxes/:inboxId/feedback

Get all feedback for an inbox with pagination and filters.

🤖AI Agent Integration

🗣️ In plain English: If you use AI chatbots (like a support bot on your website), they can automatically send feedback to Pollenate when a user expresses happiness or frustration. This means you capture real sentiment without asking the user to fill out a separate survey.

AI agents can submit feedback during conversations with users — capturing satisfaction, sentiment, and product feedback in real-time without interrupting the flow.

How it works

When a user expresses satisfaction or frustration during a conversation, the AI agent can submit structured feedback to Pollenate using the same /collect endpoint used by widgets. The feedback appears in your dashboard alongside widget submissions.

User talks to AIAgent detects sentimentPOST /collectDashboard

Skill File Discovery

Pollenate publishes a skill.md file that AI agents and platforms can use to discover and learn the API. For deeper integration, see the MCP Server section below.

# Install the skill locally for your AI agent
mkdir -p ~/.agent/skills/pollenate
curl -s https://pollenate.dev/skill.md > ~/.agent/skills/pollenate/SKILL.md
curl -s https://pollenate.dev/skill.json > ~/.agent/skills/pollenate/skill.json

MCP Server (Model Context Protocol)

Pollenate provides a built-in MCP server that allows AI agents and tools like Claude Desktop, Cursor, Windsurf, and other MCP-compatible clients to interact with your feedback data natively — no custom HTTP calls required.

Endpoint: https://api.pollenate.dev/mcp — Uses the Streamable HTTP transport (MCP spec 2025-03-26).

Available Tools

submit_feedback
Submit a feedback event to an inbox
Scope: collect / write
list_inboxes
List all inboxes for the organization
Scope: read / write / collect
list_brands
List brands (sub-organizations)
Scope: read / write
get_feedback
List and filter feedback events with pagination
Scope: read / write
get_feedback_stats
Aggregated stats and NPS/CSAT scores
Scope: read / write
search_feedback
Full-text search across feedback comments
Scope: read / write

Claude Desktop Configuration

Add this to your claude_desktop_config.json:

{
  "mcpServers": {
    "pollenate": {
      "type": "streamableHttp",
      "url": "https://api.pollenate.dev/mcp",
      "headers": {
        "Authorization": "Bearer pk_live_YOUR_API_KEY"
      }
    }
  }
}

Cursor / VS Code Configuration

Add this to your .cursor/mcp.json or VS Code MCP settings:

{
  "servers": {
    "pollenate": {
      "type": "streamableHttp",
      "url": "https://api.pollenate.dev/mcp",
      "headers": {
        "Authorization": "Bearer pk_live_YOUR_API_KEY"
      }
    }
  }
}

Authentication: Pass your Pollenate API key via the Authorization: Bearer pk_live_... header (or X-Pollenate-Key header) when configuring your MCP client. The key is automatically used by all tools. You can also pass an apiKey parameter to any tool to override the connection-level key.

Agent Examples

After resolving a support issue

curl -X POST https://api.pollenate.dev/collect \
  -H "Content-Type: application/json" \
  -H "X-Pollenate-Key: YOUR_API_KEY" \
  -d '{
    "inboxKey": "support-chat",
    "type": "thumbs",
    "score": 1,
    "comment": "User confirmed their issue was resolved",
    "context": {
      "source": "ai-agent",
      "agentName": "SupportBot",
      "topic": "password-reset"
    }
  }'

Capturing conversation sentiment

curl -X POST https://api.pollenate.dev/collect \
  -H "Content-Type: application/json" \
  -H "X-Pollenate-Key: YOUR_API_KEY" \
  -d '{
    "inboxKey": "sentiment-tracker",
    "type": "emoji",
    "score": 4,
    "context": {
      "source": "ai-agent",
      "detectedSentiment": "positive",
      "conversationTurns": 8,
      "agentModel": "claude-sonnet-4"
    }
  }'

Logging product feedback from conversation

curl -X POST https://api.pollenate.dev/collect \
  -H "Content-Type: application/json" \
  -H "X-Pollenate-Key: YOUR_API_KEY" \
  -d '{
    "inboxKey": "product-feedback",
    "type": "text",
    "comment": "User requested dark mode support — 3rd request this week",
    "context": {
      "source": "ai-agent",
      "category": "feature-request",
      "priority": "high"
    }
  }'

Best Practices

  • Ask first — Always confirm with the user before submitting feedback on their behalf.
  • Use context — Include source: "ai-agent" in the context so teams can filter agent-submitted feedback.
  • Group with sessionId — Use the same sessionId for all feedback from one conversation.
  • Pick the right type — Use thumbs for quick yes/no, text for qualitative feedback, nps for loyalty scores.
  • Handle rate limits — If you receive a 429 response, wait for the retryAfter duration before retrying.

Automations & Integrations

🗣️ In plain English: Automations let Pollenate take action the moment feedback arrives — email your team, ping a webhook, or kick off a Zap. Think "when a customer leaves 1-star feedback, create a ticket and notify the team" without writing any code that runs on a schedule.

An automation rule is a simple recipe: when an event happens (a trigger), optionally matching some conditions (filters), do something (an action). You manage rules from Dashboard → Automations, or directly via the API. Every rule execution is logged with its status, HTTP status code, and duration so you can debug deliveries.

📨

Email

Send a formatted notification to one or more recipients when feedback comes in.

🔗

Webhook

Send a signed request to any public URL — with custom method, headers, and body template, it can call an external API (like GitHub) directly.

⚙️

Zapier & more

Use the native Zapier app (or any webhook-compatible tool) to reach 7,000+ apps including Slack, Jira, and GitHub.

Rule scope

Rules can be scoped to a single inbox or to your whole organization:

ScopeFires for
inboxFeedback submitted to one specific inbox. Requires an inboxId.
organizationAny inbox in the organization, plus feedback-page responses. Required for the page_response.created event.

🎯Events & Filters

Each rule listens for one or more events and can narrow down which of those events actually trigger the action using filters. An empty filter set matches everything.

Events

EventFires when…
feedback.createdFeedback is submitted via a widget, the API, the MCP server, an import, or Zapier.
page_response.createdSomeone completes a Feedback Page survey. Organization scope only.

Filters for feedback.created

FilterTypeMatches
typestring | string[]Feedback type(s): thumbs, stars, csat, nps, emoji, text
sourcestring | string[]Origin: widget, api, zapier, import, mcp
minScorenumberScore greater than or equal to value
maxScorenumberScore less than or equal to value
scoreEqualsnumberScore exactly equals value
scoreAbovenumberScore strictly greater than value
scoreBelownumberScore strictly less than value
hasCommentbooleanFeedback includes a non-empty comment
sentiment"positive" | "negative"Thumbs up (positive) or thumbs down (negative)
npsCategory"promoter" | "passive" | "detractor"NPS bucket: promoter 9–10, passive 7–8, detractor 0–6

Filters for page_response.created

FilterTypeMatches
pageIdstringOnly responses from a specific Feedback Page
hasEmailbooleanOnly responses where the respondent left an email address

Create a rule via the API

Send a low-score webhook for an inbox, but only when there's a written comment:

Create a webhook automation
curl -X POST https://api.pollenate.dev/organizations/ORG_ID/automations \
  -H "Authorization: Bearer YOUR_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Low NPS to webhook",
    "scope": "inbox",
    "inboxId": "INBOX_ID",
    "type": "webhook",
    "target": "https://example.com/hooks/pollenate",
    "events": ["feedback.created"],
    "filters": {
      "type": "nps",
      "npsCategory": "detractor",
      "hasComment": true
    }
  }'

🔗Webhooks

🗣️ In plain English: A webhook is a URL you own that Pollenate calls automatically whenever something happens. Pollenate "pushes" the data to you in real time, so you don't have to keep asking "any new feedback yet?"

When a webhook rule fires, Pollenate sends an HTTP POST request to your target URL with a JSON body and a set of identifying headers. Deliveries time out after 10 seconds; a 2xx response is treated as success and anything ≥ 400 is logged as a failure.

Request headers

HeaderDescription
Content-TypeAlways application/json
User-AgentPollenate-Webhook/1.0
X-Pollenate-EventThe event name, e.g. feedback.created
X-Pollenate-DeliveryA unique UUID for this delivery attempt (use for idempotency)
X-Pollenate-TimestampISO 8601 timestamp of when the event fired
X-Pollenate-Signaturesha256=… HMAC signature — only present when a signing secret is set

Payload shape

Every payload is wrapped in an envelope with the event, a timestamp, and the event-specific data. Here's a feedback.created payload:

feedback.created payload
{
  "event": "feedback.created",
  "timestamp": "2026-06-05T14:23:11.482Z",
  "data": {
    "feedbackId": "fb_a1b2c3",
    "inboxId": "inbox_123",
    "inboxName": "Website",
    "inboxKey": "pk_live_abc",
    "brandId": "brand_1",
    "brandName": "Acme",
    "orgId": "org_42",
    "orgName": "Acme Inc.",
    "type": "nps",
    "score": 2,
    "comment": "Checkout kept failing on mobile.",
    "pageUrl": "https://acme.com/checkout",
    "sessionId": "sess_789",
    "context": "checkout-flow",
    "source": "widget",
    "externalUserId": "user_55",
    "createdAt": "2026-06-05T14:23:11.000Z"
  }
}

And a page_response.created payload:

page_response.created payload
{
  "event": "page_response.created",
  "timestamp": "2026-06-05T14:25:02.110Z",
  "data": {
    "responseId": "resp_abc",
    "pageId": "page_99",
    "pageTitle": "Onboarding Survey",
    "pageSlug": "onboarding-survey",
    "orgId": "org_42",
    "orgName": "Acme Inc.",
    "brandName": "Acme",
    "respondentName": "Jordan Lee",
    "respondentEmail": "jordan@example.com",
    "answers": [
      {
        "questionId": "q1",
        "questionPrompt": "How easy was setup?",
        "questionType": "stars",
        "score": 4,
        "textValue": null,
        "multiValues": null
      }
    ],
    "createdAt": "2026-06-05T14:25:02.000Z"
  }
}

Verifying the signature

When you set a signing secret on the rule, Pollenate signs the raw request body with HMAC-SHA256 and sends the hex digest in the X-Pollenate-Signature header as sha256=<digest>. Recompute it on your side and compare to confirm the request genuinely came from Pollenate and wasn't tampered with.

Verify in Node.js / Express
import crypto from "node:crypto";

const SECRET = process.env.POLLENATE_WEBHOOK_SECRET;

// Use the RAW body, not the parsed JSON object.
app.post("/hooks/pollenate", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.header("X-Pollenate-Signature") || "";
  const expected =
    "sha256=" +
    crypto.createHmac("sha256", SECRET).update(req.body).digest("hex");

  const ok =
    signature.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));

  if (!ok) return res.status(401).send("Invalid signature");

  const payload = JSON.parse(req.body.toString("utf8"));
  console.log("Verified Pollenate event:", payload.event);
  res.sendStatus(200);
});

Target URLs must be public

To prevent server-side request forgery (SSRF), Pollenate only delivers to publicly reachable hosts. Webhooks pointed at localhost, loopback, link-local, or private IP ranges (10.x, 192.168.x, 172.16–31.x) are rejected. For local development, use a tunnel such as ngrok or cloudflared.

Test & debug

Use the Test button on any rule in Dashboard → Automations to fire a sample payload, then open Logs on the rule to see each delivery's status, HTTP status code, duration, and any error message.

🔐Secrets & Custom Headers

🗣️ In plain English: Most APIs (GitHub, Slack, Jira…) require an authentication token. Pollenate lets you save those tokens once in an encrypted secrets store, then drop them into your webhook's headers — so a webhook can call another service directly, no middleman required.

Webhook automations support three extra knobs that turn a basic webhook into a full HTTP request to any API:

  • HTTP method — choose POST, PUT, PATCH, DELETE, or GET.
  • Custom headers — add any request headers, e.g. an Authorization token or a specific Accept/Content-Type.
  • Body template — shape the request body exactly how the target API expects, using placeholders for event data.

The secrets store

Create named secrets under Dashboard → Automations → Manage secrets (or via the API). Each secret is encrypted at rest with AES-256-GCM and its value is never returned by the API again — you'll only ever see a masked hint like ••••cdef. Reference a secret anywhere in your headers or body template with {{secrets.NAME}}.

Create a secret via the API
curl -X POST https://api.pollenate.dev/organizations/ORG_ID/secrets \
  -H "Authorization: Bearer YOUR_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "GITHUB_TOKEN",
    "value": "github_pat_11AAA...",
    "description": "Fine-grained PAT, Issues: read & write"
  }'

Templating

Header values and the body template support {{ ... }} placeholders. This is plain text substitution — there is no code execution — resolved against the event at delivery time:

PlaceholderResolves to
{{secrets.NAME}}A decrypted value from your secrets store
{{event}}The event name, e.g. feedback.created
{{timestamp}}ISO 8601 time the event fired
{{data.comment}}Any field from the event payload's data object
{{data.score}}Numbers and booleans are coerced to text

Unknown placeholders resolve to an empty string. See the payload reference for every available data.* field.

Good to know

  • When a body template is set, the X-Pollenate-Signature HMAC is computed over the rendered body.
  • Reserved headers (Host, Content-Length, and the X-Pollenate-* signature/delivery headers) can't be overridden.
  • The public-URL/SSRF restriction still applies — targets must be publicly reachable.

🐙Recipe: Create a GitHub Issue

A common ask: "when a customer leaves negative feedback, open a GitHub issue so the team can triage it." With secrets and custom headers you can do this with a webhook that calls GitHub directly — no relay or extra service needed.

Option A — Direct webhook (recommended)

  1. In Manage secrets, add a secret named GITHUB_TOKEN containing a fine-grained PAT with Issues: Read & write on the target repo.
  2. Create a webhook automation for feedback.created with a filter such as scoreBelow: 3.
  3. Set the URL, method, headers, and body template as shown below.
URLhttps://api.github.com/repos/OWNER/REPO/issues
MethodPOST
Headers
Authorization: Bearer {{secrets.GITHUB_TOKEN}}
Accept: application/vnd.github+json
User-Agent: pollenate

Body template:

Body template → GitHub create issue
{
  "title": "Feedback: {{data.type}} ({{data.score}}) on {{data.inboxName}}",
  "body": "**Comment:** {{data.comment}}\n\n**Score:** {{data.score}}\n**Source:** {{data.source}}\n**Page:** {{data.pageUrl}}\n**Feedback ID:** {{data.feedbackId}}",
  "labels": ["feedback", "needs-triage"]
}

Tip: Hit Test on the rule to send a sample issue, then check Logs for GitHub's HTTP status. The same recipe works for Linear, Jira, Asana, or Slack — just change the URL, headers, and body template. Make sure Content-Type matches what the API expects (GitHub accepts JSON by default).

Option B — No code (Zapier)

Prefer not to manage tokens at all? Use the Zapier app: a New Feedback trigger plus GitHub's Create Issue action, with a filter for negative feedback.

Option C — Webhook + relay

If you'd rather keep credentials entirely in your own infrastructure, send the webhook to a small relay (here, a Cloudflare Worker) that verifies the signature and calls the GitHub REST API.

Cloudflare Worker relay → GitHub issue
export default {
  async fetch(request, env) {
    if (request.method !== "POST") return new Response("Method not allowed", { status: 405 });

    // 1. Read the RAW body so the signature still matches.
    const raw = await request.text();

    // 2. Verify the Pollenate signature (optional but recommended).
    const signature = request.headers.get("X-Pollenate-Signature") || "";
    const key = await crypto.subtle.importKey(
      "raw",
      new TextEncoder().encode(env.POLLENATE_WEBHOOK_SECRET),
      { name: "HMAC", hash: "SHA-256" },
      false,
      ["sign"],
    );
    const mac = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(raw));
    const expected =
      "sha256=" +
      [...new Uint8Array(mac)].map((b) => b.toString(16).padStart(2, "0")).join("");
    if (signature !== expected) return new Response("Invalid signature", { status: 401 });

    // 3. Only act on negative feedback.
    const { event, data } = JSON.parse(raw);
    if (event !== "feedback.created" || (data.score ?? 99) > 3) {
      return new Response("Ignored", { status: 200 });
    }

    // 4. Create the GitHub issue.
    const res = await fetch("https://api.github.com/repos/OWNER/REPO/issues", {
      method: "POST",
      headers: {
        Authorization: `Bearer ${env.GITHUB_TOKEN}`,
        Accept: "application/vnd.github+json",
        "User-Agent": "pollenate-relay",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        title: `Feedback: ${data.type} (${data.score}) on ${data.inboxName}`,
        body: [
          `**Comment:** ${data.comment || "(none)"}`,
          `**Score:** ${data.score}`,
          `**Source:** ${data.source}`,
          `**Page:** ${data.pageUrl || "n/a"}`,
          `**Feedback ID:** ${data.feedbackId}`,
        ].join("\n"),
        labels: ["feedback", "needs-triage"],
      }),
    });

    return new Response(res.ok ? "Issue created" : "GitHub error", {
      status: res.ok ? 200 : 502,
    });
  },
};

⚙️Zapier

🗣️ In plain English: Zapier is a no-code tool that connects apps together. With the Pollenate Zapier app you can route feedback to Slack, Google Sheets, Jira, GitHub, your CRM, and 7,000+ other apps — without writing any code.

Connect your account by creating an API key with the read + write scopes at pollenate.dev/api-keys (the link pre-selects the right scopes), then paste it into Zapier when prompted. Triggers use REST Hooks, so feedback flows through in real time rather than on a polling delay.

Triggers

  • New Feedback
  • New Page Response

Actions

  • Submit Feedback
  • Add Note to Feedback
  • Update Feedback (link Jira / GitHub / Linear)

Searches

  • Find Feedback
  • Get Feedback by ID

Example Zaps

  • Feedback → Slack: post a message when NPS detractors leave a comment.
  • Feedback → GitHub/Jira: auto-create an issue for negative feedback.
  • Feedback → Google Sheets: log every entry to a spreadsheet for analysis.
  • Typeform → Pollenate: pipe survey responses in as feedback.

🧩More Integrations

Because automations support generic webhooks, you can connect Pollenate to nearly anything:

  • Make, n8n, Pipedream: point a webhook rule at their "catch hook" URL and build flows visually — same payload and signature as described above.
  • Slack: use the Zapier "Slack → Send Channel Message" action, or relay through your own endpoint that posts to a Slack Incoming Webhook (Slack expects its own { text } body, so reshape the payload first).
  • Your own backend: receive webhooks directly and write to your database, queue, or analytics pipeline.
  • Email: the built-in email action delivers a formatted notification with no extra tooling required.

Want to push data into Pollenate instead? Use the Feedback API, the MCP server, or the Zapier Submit Feedback action.

🛡️Content Security Policy (CSP)

🗣️ In plain English: Some websites have strict security rules that block outside tools from running. If your widget isn't working, this might be why. Ask your web developer to check if your site has a "Content Security Policy" — if so, they'll need to add Pollenate to the approved list. The instructions below show them exactly what to do.

If your website uses a Content Security Policy, you'll need to allow the Pollenate widget script and API endpoints. Without the correct CSP directives, the widget will load but fail to submit feedback or track impressions.

Common Error

If you see errors like the following in your browser console, your CSP is blocking the Pollenate widget:

Refused to connect to 'https://api.pollenate.dev/collect' because it violates the document's Content Security Policy.

[Pollenate] Error: TypeError: Failed to fetch. Refused to connect because it violates the document's Content Security Policy.

Required CSP Directives

Add the following origins to your Content Security Policy to allow the Pollenate widget to function correctly:

DirectiveValuePurpose
script-srchttps://pollenate.devLoads the widget script
connect-srchttps://api.pollenate.devSubmits feedback and tracks impressions via the API
style-src'unsafe-inline'The widget injects inline styles for its UI

Example: HTTP Header

If you set your CSP via an HTTP response header, add the Pollenate origins to the relevant directives:

Content-Security-Policy header
Content-Security-Policy: 
  script-src 'self' https://pollenate.dev; 
  connect-src 'self' https://api.pollenate.dev; 
  style-src 'self' 'unsafe-inline';

Example: HTML Meta Tag

If you set your CSP via a <meta> tag, include the Pollenate origins:

<meta http-equiv="Content-Security-Policy" content="
  script-src 'self' https://pollenate.dev; 
  connect-src 'self' https://api.pollenate.dev; 
  style-src 'self' 'unsafe-inline';
">

Example: Nginx

add_header Content-Security-Policy "
  script-src 'self' https://pollenate.dev; 
  connect-src 'self' https://api.pollenate.dev; 
  style-src 'self' 'unsafe-inline';
" always;

Example: Apache

Header set Content-Security-Policy "  script-src 'self' https://pollenate.dev;   connect-src 'self' https://api.pollenate.dev;   style-src 'self' 'unsafe-inline';"

Example: Next.js

In your next.config.js:

const securityHeaders = [
  {
    key: 'Content-Security-Policy',
    value: [
      "script-src 'self' https://pollenate.dev",
      "connect-src 'self' https://api.pollenate.dev",
      "style-src 'self' 'unsafe-inline'",
    ].join('; '),
  },
];

module.exports = {
  async headers() {
    return [{ source: '/(.*)', headers: securityHeaders }];
  },
};

Tip: Custom API URL

If you use the data-api-url attribute to point the widget at a custom API endpoint, make sure to add that URL to your connect-src directive instead of (or in addition to) https://api.pollenate.dev.

How to Verify

  1. Open your browser's Developer Tools (F12 or Cmd+Opt+I).
  2. Go to the Console tab and look for CSP violation errors.
  3. Check the Network tab — blocked requests to api.pollenate.dev will show as (blocked:csp).
  4. After updating your CSP, hard-refresh the page (Ctrl+Shift+R / Cmd+Shift+R) and confirm the errors are gone.
  5. Submit a test feedback entry and verify it appears in your Pollenate dashboard.

Developer FAQ

Common questions about integrating and using the Pollenate API and widgets.

Do I need a build step to use the widget?
No. The Pollenate widget is a standalone script you add via a <script> tag. It works on any website — static HTML, WordPress, React, Next.js, or any other framework — with zero build configuration.
What is an inbox key vs. an API key?
An inbox key identifies which inbox receives the feedback. An API key authenticates the request. You need both: the inbox key (from the Inboxes page) tells Pollenate where to route the feedback, and the API key (from Settings → API Keys with the "collect" scope) authorizes the submission.
How do I authenticate API requests?
For widget and server-to-server integrations, pass your API key in the X-Pollenate-Key header. For dashboard API access, use a JWT Bearer token obtained via OTP email verification in the Authorization header.
Can I use Pollenate with React?
Yes! Install @pollenate/react (npm install @pollenate/react) for a fully typed, drop-in React component. See the React Package section in the docs above. You can also use the vanilla script tag widget directly in any React app.
What widget types are available?
Pollenate supports six widget types: thumbs (up/down), stars (1–5 rating), NPS (0–10 scale), CSAT (1–5 satisfaction), emoji (sentiment reactions), and text (open-ended comments). Set the type via the data-type attribute or the type configuration option.
How does the semantic search API work?
POST a natural language query to /organizations/:orgId/search. Pollenate generates an AI embedding of your query and performs vector similarity search against all stored feedback, returning the most semantically relevant results. You can filter by inbox, score range, and more.
Is there a rate limit on the collect endpoint?
The Starter plan allows up to 5,000 events per month. Team plans support 100,000 events per month. Enterprise plans have no hard limits. All plans include burst protection to prevent abuse.
Can I listen for widget events in my app?
Yes. The widget emits a "pollenate:submit" CustomEvent on the window object whenever feedback is submitted. Listen for it with window.addEventListener("pollenate:submit", callback) to integrate with your analytics or trigger custom behavior.
How are Feedback Pages different from widgets?
Widgets are lightweight embeds you drop into existing pages for quick, single-question feedback. Feedback Pages are standalone, multi-question forms hosted at their own URL. Use Feedback Pages when you need multi-question surveys, want to share via QR code or print, or need a branded form that stands on its own without embedding into an existing site.
Can I embed a Feedback Page on my website?
Yes! Each published Feedback Page can be embedded using an iframe. Append ?embed=true to the URL to remove headers and footers for a clean embed. You can also share Feedback Pages via direct link, QR code, email signature button, or a print-ready flyer.
Why is my widget showing "Failed to fetch" errors?
This usually means your site's Content Security Policy (CSP) is blocking the widget from connecting to the Pollenate API. You need to add pollenate.dev and api.pollenate.dev to your CSP connect-src and script-src directives. See the Content Security Policy section in the docs for full details.
How do I get notified when new feedback comes in?
Create an automation rule under Dashboard → Automations. Choose the feedback.created event, optionally add filters (such as low score or a written comment), and pick an action: email, webhook, or Zapier. Webhook and Zapier rules deliver in real time.
How do I verify that a webhook really came from Pollenate?
Set a signing secret on the webhook rule. Pollenate signs the raw request body with HMAC-SHA256 and sends the hex digest in the X-Pollenate-Signature header as sha256=<digest>. Recompute the HMAC on your side using the same secret and compare with a constant-time check before trusting the payload.
Can a webhook create a GitHub issue (or Jira/Linear ticket)?
Yes — directly. Store your API token in the secrets store, then add an Authorization header (Bearer {{secrets.GITHUB_TOKEN}}) and a body template to a webhook automation pointed at GitHub's API. No relay or extra service is needed. You can also use the Zapier app's Create Issue action, or send to your own relay. See the Recipe: Create a GitHub Issue section.
Why won't Pollenate deliver to my localhost webhook?
To prevent SSRF, webhooks only deliver to publicly reachable hosts. Requests to localhost, loopback, link-local, or private IP ranges (10.x, 192.168.x, 172.16–31.x) are rejected. For local development, expose your endpoint with a tunnel such as ngrok or cloudflared.

Ready to start collecting feedback?

Create your free account and have your first widget live in minutes.