Skip to main content
Peeve stores two different things about a user, in two different ways, and the split is about how each is protected.
Put personal data in identify. Do not put email addresses, phone numbers or anything else identifying into setContext — it is stored as plaintext, because it is designed for non-secret business context.

identify — who they are

email, name, company and phone are encrypted at rest. The email is additionally indexed by a keyed hash, so contact lookup and email search keep working without decrypting anything. plan is the user’s plan in your product, as a display name. It is stored plaintext because it is not personal data — and it has no effect on your Peeve entitlements, which come from your own subscription. createdAt is worth setting. It is how Peeve distinguishes a genuine new signup from an existing customer browsing logged out, so a returning user is not miscounted as a visitor-to-user conversion.
Identity is never trusted for authentication. Anything the browser sends in contact is enrichment only — it cannot resolve a workspace, grant access, or read another person’s data. The workspace always comes from the validated key.

setContext — what you know about them

An open bag, merged across calls — the last value for a key wins. It is sanitized and capped on write. It surfaces in two places: a teammate sees it as a details panel when a conversation is handed off, and the agent gets it as reference context so it can tailor an answer. The agent treats it as data, never as instructions.

The server-side equivalent

Both have a server-side counterpart on POST /v1/users, so you can establish a complete contact before the person ever opens a browser:
The top-level identity fields are identify; the attributes object is setContext. A later browser identify with the same external_user_id merges into the same record.

Identity is keyed on your id, not on the key

A contact is identified by (workspace, external_user_id). It is deliberately not keyed on the API key — if it were, every user would be orphaned the day you rotated a key, and their history would vanish. So: use a stable id. Your internal user id is right. An email address is usable but changes; a session id is not an identity at all.

Anonymous visitors

Before someone signs in, Peeve still tracks them, using a persistent browser visitor id. When you later call identify, the pre-login history links to the now-known user. This is why calling identify at the right moment matters: it is what joins “someone browsed pricing three times” to “Dana signed up”.

On logout

Clears both identity and context. Without it, the next person to use that browser inherits the previous user’s identity.

Vendor tokens

POST /v1/users accepts a vendor_token — a token your backend issues for a user, so a Custom API connector can call your API as them.
A vendor token never travels through the browser. It is pushed server-to-server, stored encrypted, and used server-side. There is no browser-side equivalent, by design: anything in the DOM is reachable by any script on the page, including a prompt-injection payload.
Rotating a token supersedes the previous one in the token history, which stores a fingerprint only — never the recoverable secret. A freshly pushed token is marked valid but unverified, so it is re-checked against your API on the next agent request. That is what stops a token you have already revoked staying valid inside Peeve.
A token authenticates; it does not authorize. Possession of one is full account authority, so the capability allowlist you configure — not the token — is what stands between a leaked token and real damage.