Developer Documentation

API Reference

Integrate Valerie feedback collection into your applications. Programmatically manage sites, retrieve responses, and build custom feedback workflows.

Authentication

Valerie uses two types of authentication depending on the endpoint:

Site ID (Widget Endpoints)

Public widget endpoints use your Site ID for identification. Find your Site ID in theDashboard Settings.

Bearer Token (Dashboard API)

Dashboard API endpoints require authentication via Supabase session. Use the dashboard to access your data or integrate via the widget.

Your Site ID (example)

SITE_01HXYZ1234567890ABCDEF

Find your Site ID at: Dashboard → Settings → Site Information

Base URL

All API requests should be made to:

https://talktovalerie.com/api

All endpoints support HTTPS only. HTTP requests will be redirected.

Widget Endpoints

These endpoints power the Valerie feedback widget. They are public and require only your Site ID.

GET/api/valerie/decide

Determines whether to show the feedback widget to a visitor based on targeting rules, sampling rate, and frequency settings.

Query Parameters

sitestringrequiredYour Site ID
pathstringCurrent page path (default: /)
visitorIdstringUnique visitor identifier for consistent A/B assignment

Example Request

cURL

curl "https://talktovalerie.com/api/valerie/decide?site=SITE_01HXYZ&path=/pricing"

JavaScript / TypeScript

const response = await fetch(
  'https://talktovalerie.com/api/valerie/decide?' + 
  new URLSearchParams({
    site: 'SITE_01HXYZ',
    path: window.location.pathname,
    visitorId: localStorage.getItem('valerie_visitor_id')
  })
);

const decision = await response.json();

if (decision.show) {
  // Show feedback widget with decision.question
  console.log('Question:', decision.question.highlight);
  console.log('Session Token:', decision.sessionToken);
}

Example Response

{
  "show": true,
  "reason": "eligible",
  "siteId": "SITE_01HXYZ",
  "path": "/pricing",
  "question": {
    "prefix": "How helpful was",
    "highlight": "this page?",
    "leftLabel": "Not helpful at all",
    "rightLabel": "Extremely helpful",
    "format": "scale",
    "options": []
  },
  "questionId": "123",
  "followup": {
    "low": { "title": "What could we improve?", "placeholder": "Tell us more..." },
    "mid": { "title": "Any suggestions?", "placeholder": "Tell us more..." },
    "high": { "title": "What did you like?", "placeholder": "Tell us more..." }
  },
  "sessionToken": "eyJhbGciOiJIUzI1NiIs...",
  "frequency": { "mode": "always" },
  "popup": {
    "format": "floating_card",
    "timing": "delay_5s",
    "typingAnimation": false
  },
  "theme": {
    "themePreset": "default",
    "colors": {
      "primary": "#8B5CF6",
      "background": "#FFFFFF",
      "text": "#1E293B",
      "buttonText": "#FFFFFF"
    }
  }
}
POST/api/valerie/ingest

Submit feedback events including widget interactions, score selections, and responses. Requires a valid session token from the decide endpoint.

Request Body

sessionTokenstringrequiredSession token from decide endpoint
visitorIdstringrequiredUnique visitor identifier
pageViewIdstringrequiredUnique page view identifier
eventsarrayrequiredArray of event objects
clientobjectClient metadata (referrer, language, timezone)

Example Request

cURL

curl -X POST "https://talktovalerie.com/api/valerie/ingest" \
  -H "Content-Type: application/json" \
  -d '{
    "sessionToken": "eyJhbGciOiJIUzI1NiIs...",
    "visitorId": "visitor_abc123",
    "pageViewId": "pv_xyz789",
    "events": [
      {
        "eventId": "evt_001",
        "type": "response_submit",
        "ts": 1706745600000,
        "score": 9,
        "comment": "Great pricing page!"
      }
    ],
    "client": {
      "referrer": "https://google.com",
      "language": "en-US",
      "timezone": "America/New_York"
    }
  }'

JavaScript / TypeScript

const submitFeedback = async (sessionToken, score, comment) => {
  const response = await fetch('https://talktovalerie.com/api/valerie/ingest', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      sessionToken,
      visitorId: localStorage.getItem('valerie_visitor_id'),
      pageViewId: crypto.randomUUID(),
      events: [{
        eventId: crypto.randomUUID(),
        type: 'response_submit',
        ts: Date.now(),
        score,
        comment
      }],
      client: {
        referrer: document.referrer,
        language: navigator.language,
        timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
      }
    })
  });
  
  return response.status === 204; // Success
};

Example Response

// Success: HTTP 204 No Content
// Error: HTTP 400/401/429 with JSON body

// Rate limit exceeded:
{
  "error": "Rate limit exceeded"
}

// Invalid token:
{
  "error": "Unauthorized"
}

Dashboard API

These endpoints are used by the Valerie dashboard. They require authentication and are designed for internal use. For programmatic access, we recommend using the widget endpoints or contacting us for enterprise API access.

GET/api/dashboard/sitesRequires Auth

List all sites associated with your account.

Example Request

cURL

curl "https://talktovalerie.com/api/dashboard/sites" \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN"

Example Response

{
  "sites": [
    {
      "id": "SITE_01HXYZ",
      "name": "example.com",
      "allowedDomains": ["example.com", "www.example.com"],
      "createdAt": "2024-01-15T10:30:00Z"
    }
  ]
}
POST/api/dashboard/sitesRequires Auth

Create a new site for feedback collection.

Request Body

namestringrequiredDisplay name for the site
domainstringrequiredPrimary domain (e.g., example.com)

Example Request

cURL

curl -X POST "https://talktovalerie.com/api/dashboard/sites" \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My New Site",
    "domain": "newsite.com"
  }'

Example Response

{
  "site": {
    "id": "SITE_01HABCD",
    "name": "My New Site",
    "allowedDomains": ["newsite.com"],
    "createdAt": "2024-02-01T15:00:00Z"
  }
}
GET/api/dashboard/responsesRequires Auth

Retrieve feedback responses with filtering and pagination.

Query Parameters

siteIdstringrequiredYour Site ID
pagenumberPage number (default: 1)
limitnumberResults per page (default: 20, max: 100)
periodstringTime period: 24h, 7d, 30d
pathstringFilter by page path
scoreMinnumberMinimum score filter
scoreMaxnumberMaximum score filter
withCommentsbooleanOnly show responses with comments
groupBystringGroup results: question, date

Example Request

cURL

curl "https://talktovalerie.com/api/dashboard/responses?siteId=SITE_01HXYZ&period=7d&limit=50" \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN"

JavaScript / TypeScript

const getResponses = async (siteId, options = {}) => {
  const params = new URLSearchParams({
    siteId,
    page: options.page || 1,
    limit: options.limit || 20,
    ...(options.period && { period: options.period }),
    ...(options.path && { path: options.path }),
    ...(options.withComments && { withComments: 'true' }),
  });

  const response = await fetch(
    `https://talktovalerie.com/api/dashboard/responses?${params}`,
    {
      headers: {
        'Authorization': `Bearer ${sessionToken}`
      }
    }
  );

  return response.json();
};

Example Response

{
  "responses": [
    {
      "id": 12345,
      "pageViewId": "pv_abc123",
      "visitorId": "v_xyz789",
      "path": "/pricing",
      "score": 9,
      "comment": "Very clear pricing tiers!",
      "occurredAt": "2024-02-01T14:30:00Z",
      "country": "US",
      "device": "desktop",
      "question": {
        "prefix": "How helpful was",
        "highlight": "this page?",
        "path": "/*",
        "format": "scale"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "totalCount": 156,
    "totalPages": 8
  },
  "distribution": {
    "promoters": 89,
    "passives": 42,
    "detractors": 25
  },
  "paths": ["/", "/pricing", "/features", "/docs"]
}
DELETE/api/dashboard/responsesRequires Auth

Delete individual or bulk responses.

Request Body

siteIdstringrequiredYour Site ID
responseIdnumberSingle response ID to delete
visitorIdstringDelete all responses from a visitor
periodstringBulk delete by period: 24h, 7d, 30d

Example Request

cURL

curl -X DELETE "https://talktovalerie.com/api/dashboard/responses" \
  -H "Authorization: Bearer YOUR_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "siteId": "SITE_01HXYZ",
    "responseId": 12345
  }'

Example Response

{
  "success": true,
  "deleted": 1
}

Rate Limits

To ensure fair usage and protect our infrastructure, API endpoints have rate limits:

EndpointLimitWindowScope
/api/valerie/decide60 requests1 minutePer Site + IP
/api/valerie/ingest100 requests1 minutePer Site + Session
/api/dashboard/*100 requests1 minutePer User

Rate Limit Headers

When rate limited, the API returns a 429 Too Many Requests status.

HTTP/1.1 429 Too Many Requests
Retry-After: 30

Error Codes

The API uses standard HTTP status codes and returns JSON error responses:

200Success

Request completed successfully.

204No Content

Request successful, no content to return (used for ingest).

400Bad Request

Invalid request parameters or malformed JSON.

{ "error": "siteId required" }
401Unauthorized

Missing or invalid authentication credentials.

{ "error": "Unauthorized" }
429Too Many Requests

Rate limit exceeded. Wait before retrying.

{ "error": "Rate limit exceeded" }
500Internal Server Error

Unexpected server error. Contact support if persistent.

{ "error": "Internal server error" }

Webhooks

Coming Soon

Webhook support for real-time feedback notifications is on our roadmap. Get notified instantly when new feedback arrives.

In the meantime, use our email notifications for daily digests and urgent feedback alerts.

SDKs & Integrations

The easiest way to integrate Valerie is with our JavaScript widget:

Add to your HTML

<script src="https://talktovalerie.com/valerie/v1.js" data-site-id="YOUR_SITE_ID" async></script>

JavaScript API

Once loaded, the widget exposes a global window.Valerie object:

// Open the feedback widget programmatically
window.Valerie.open();

// Close the widget
window.Valerie.close();

// Track a custom event
window.Valerie.track('button_click', { buttonId: 'cta-main' });

// Identify a user (for logged-in experiences)
window.Valerie.identify({
  userId: 'user_123',
  email: 'user@example.com',
  plan: 'pro'
});

// Update page context (for SPAs)
window.Valerie.page('/new-path');

Need Help?

Have questions about the API or need enterprise-level access? Our team is here to help you integrate Valerie into your workflow.