Build on your feedback data, or just ask for it.
Send people and events in a few lines of code, read answers through the API, or ask an AI assistant in plain words.
A chat. Someone asks which customers gave low scores this week. The assistant lists four people and their scores, then saves them as a segment on request.
You
Which customers gave low scores this week?
Claude
Four people scored you 6 or lower this week:
Emily Carter · Northwind Books
3Tamara Novak · Copperleaf
4Priya Raman · Harbor Fitness
5Lucia Ferreira · Paperplane
6
You
Save those four as a segment called Needs a call.
Claude
Done. I saved the segment for you.
Needs a call4
Send your first person in one request
Tell Pulseri who someone is, then what they did. Surveys can start from either.
curl https://api.pulseri.com/v1/identify \
-H "Authorization: Bearer pk_live_..." \
-H "Content-Type: application/json" \
-d '{
"external_id": "user_123",
"name": "Emily Carter",
"email": "[email protected]",
"group_id": "northwind-books",
"traits": { "plan": "pro", "seats": 12 }
}'
A public key is safe in your web app because it only sends data in. Use a service account key on your server for everything else. The button at the top of this page opens the full reference.
Most of the dashboard is in the API
The API reference covers people, groups, segments, campaigns, answers, alerts and team settings.
- People, groups and traits: send, search and update them.
- Segments and campaigns: create, change, start and pause.
- Answers and score reports by campaign, group or person.
- Alert rules and webhook endpoints, with their delivery history.
- Your team, roles and service accounts.
Lists use cursor paging, search and filters everywhere. Errors share one shape, and every response shows your remaining rate limit.
Keys, service accounts and limits by plan
Two kinds of key keep browser code and server code apart.
A public key (pk_live_) goes into your web app and only sends data in. A service account key (sk_live_) belongs to a service account, which has one role and does what that role allows. It never signs in and is not a team member.
Rate limits are per key and per minute. Every response shows the limit and what is left, and a 429 includes a Retry-After header. Limits grow with your plan:
- API requests a minute per key
- 300
- Assistant requests a minute per app
- 120
- Service accounts
- 2
- Public keys
- 2
- Connected assistants
- 3
- API requests a minute per key
- 1,000
- Assistant requests a minute per app
- 300
- Service accounts
- 5
- Public keys
- 5
- Connected assistants
- 10
- API requests a minute per key
- 3,000
- Assistant requests a minute per app
- 600
- Service accounts
- 20
- Public keys
- 10
- Connected assistants
- 50
- API requests a minute per key
- Custom
- Assistant requests a minute per app
- Custom
- Service accounts
- Custom
- Public keys
- Custom
- Connected assistants
- Unlimited
Sign who is calling
A public key sits in browser code, where anyone can copy it. A signature from your server shows that a request came from your app.
import { createHmac } from 'node:crypto'
const sign = (kind, id) =>
createHmac('sha256', process.env.PULSERI_IDV_SECRET)
.update(`${kind}:${id}`)
.digest('hex')
const signature = sign('person', user.id)
const groupSignature = sign('group', company.id)
Identity verification is optional and starts off. Create a signing secret in Settings, then have your server compute HMAC-SHA256(secret, message) as lowercase hex and send it as signature in the body of /identify, /group and /track. For a person (/identify with its external_id, /track with its person_id) the message is person: followed by the id, such as person:user_123. For a group (/group with its external_id) it is group: followed by the id, such as group:acme. The prefix means a person and a group with the same id never share a signature, and a signature of the bare id is refused. The secret stays on your server and never goes into browser code.
A request with a matching signature is verified and can set a person's email, name and group. A request without one still records traits and events and can show in-app surveys, but it leaves an existing person's name and group as they are and does not store an email. A signature that does not match gets 401 invalid_signature, whether or not you enforce signing.
When your server signs everything, turn on Reject unsigned requests in Settings. Public-key requests without a signature then get 401 signature_required. Settings shows how many requests of the last seven days were signed, so you can check before you switch. A new secret replaces the old one at once, so update your server first.
Email surveys that start from website events need a verified event. Pulseri refuses to start such an email survey until the account has a signing secret, with 422 identity_verification_required. Events from webhooks, integrations and /events are already verified.
/identify and /group answer a public key with 204 No Content, so a browser never gets person data back.
A request without a signature cannot set an email, not even for a new person. It can still create a new person and, when the person is new, the group named by group_id.
Customers and employees. /identify takes an optional audience_type, customer (the default) or employee, and /group takes customer_account (the default) or department. Send it from any key. It only applies when Pulseri creates the person or group: on one that already exists it is ignored and nothing changes. A customer belongs to a customer account and an employee to a department. When /identify creates the group named by group_id, the group gets the matching type, so an employee creates a department. If the group already exists with the other type, a secret key or a signed request gets 422 group_audience_mismatch and nothing is written. An unsigned public key still gets 204 No Content and the person is saved without the link, so a browser cannot learn which groups exist. A value that is not one of the listed types gets 422 validation_failed.
Connect an assistant
Ask about your customers in plain words, and let an assistant save what it finds.
Pulseri has an MCP server. MCP, the Model Context Protocol, is a standard that lets an AI assistant use another product. A person connects an assistant and approves what it may do, and it then works with their feedback data as that person.
- Up to 14 tools, set by permissions: find people, read answers, see trends, run surveys.
- Claude, Claude Code, ChatGPT and Cursor work. Other MCP assistants can connect too.
- An assistant gets only what the person approved, and loses it when they do.
- Admins decide which apps may connect. Anyone can disconnect their own.
- Scripts and agents without a browser can use a service account key.
Pulseri marks customer comments as untrusted text and tells the assistant so. Review what an assistant proposes before you approve it.
Keep reading
- The REST API: send people and events and read answers and score reports.
- AI assistants: ask about your feedback in plain words, using only what you approve.
- Signed webhooks: send events in and get alerts out.
Questions people ask
Start building with Pulseri
Open the docs, create a key and send your first person. Try the whole product for 14 days.
Free for 14 days. Cancel any time before it ends.