UserSay/ Docs

SDK endpoints

SDK config endpoint and planned REST API.

Live endpoints

These endpoints are live and CORS-enabled — used by the JS SDK internally.

GET /api/sdk/config

Returns the Moment configuration for a project. The SDK fetches this on init to know which Moments are active.

curl https://www.usersay.ai/api/sdk/config?project=your-project-slug

Response:

{
  "project": "mymap",
  "productName": "MyMap",
  "interviewer": {
    "name": "Sarah",
    "role": "Product"
  },
  "moments": {
    "credits_exhausted": {
      "enabled": true,
      "triggerOnce": true,
      "opener": "ugh you ran out of credits 😅 what were you working on? quick chat = 50 credits to keep going ✨",
      "trigger": {
        "type": "sdk_manual",
        "sdkEvent": "credits_exhausted"
      },
      "reward": { "type": "credits", "value": "50" },
      "depth": "deep",
      "openMode": "bubble"
    },
    "pricing_visit": {
      "enabled": true,
      "triggerOnce": true,
      "opener": "checking out pricing? lmk if anything's confusing — what are you using MyMap for?",
      "trigger": {
        "type": "url_match",
        "urlPatterns": ["/pricing"],
        "delaySeconds": 10
      },
      "reward": { "type": "coupon", "value": "WELCOME20" },
      "depth": "deep",
      "openMode": "bubble"
    },
    "open_feedback": {
      "enabled": true,
      "triggerOnce": false,
      "opener": "hey 👋 what's on your mind?",
      "trigger": { "type": "user_initiated" },
      "depth": "quick",
      "openMode": "chat"
    }
  }
}

Response fields:

FieldDescription
productNameProduct name — injected into opener templates
interviewer.nameAI persona name (default: Sarah)
interviewer.roleAI persona role (default: Product)
momentsMap of enabled Moments keyed by Moment ID
moments[id].triggerOnceWhether this Moment fires at most once per user
moments[id].trigger.typesdk_manual · url_match · auto_first_visit · behavior · user_initiated
moments[id].depthquick (light touch — close once Sarah has the headline) or deep (full interview — push for one differentiator). Conversation length is signal-based; depth tunes the closing threshold, not a turn budget.
moments[id].openModebubble or chat

Backward compat: the response also includes a duplicate playbooks key with the same map. Older browser-cached SDKs still read playbooks; new SDKs read moments. The legacy key will be dropped once the cached-SDK fleet has fully rotated.

Cached for 60s with stale-while-revalidate=300.


POST /api/sdk/identify

Persists end-user identity for a project. Called by the SDK when you call UserSay.identify(). Only project and uid are required; everything else inside endUser is optional. The server will auto-fill country from the request IP if you don't pass one.

curl -X POST https://www.usersay.ai/api/sdk/identify \
  -H "Content-Type: application/json" \
  -d '{
    "project": "mymap",
    "uid": "user_123",
    "endUser": {
      "name": "Alice",
      "email": "[email protected]",
      "avatarUrl": "https://...",
      "signupAt": "2026-01-15T00:00:00Z",
      "plan": "pro",
      "trialing": false,
      "subscribed": true,
      "credits": 120,
      "role": "admin",
      "locale": "en-US",
      "country": "US",
      "timezone": "America/Los_Angeles",
      "traits": { "teamSize": 5 }
    }
  }'

See the JS SDK identify reference for what each field does. Anything outside the known list is ignored — put extras in traits.


POST /api/sdk/event

Lightweight analytics endpoint for SDK funnel tracking. Called internally by the SDK.

Valid events: bubble_shown · bubble_clicked · bubble_dismissed · mini_opened · mini_closed · modal_opened · modal_closed · identify

curl -X POST https://www.usersay.ai/api/sdk/event \
  -H "Content-Type: application/json" \
  -d '{
    "project": "mymap",
    "uid": "user_123",
    "event": "bubble_shown",
    "trigger": "credits_exhausted"
  }'

The source: "agent-verify" field can be set by AI install agents (Claude Code, Cursor) to mark self-test pings — these count toward the install-connected status in the dashboard without requiring a real browser session.


On this page