1. Home
  2. Docs
  3. Verified identity

Verified identity

A signed identity proves to Pong that the visitor is a user of yours. Their chats from every browser and device merge into one client, and the desk marks them as verified.

How it works

  1. Your server computes sig = HMAC-SHA256(identitySecret, publicKey + ":" + userhash + ":" + ts) as lowercase hex.
  2. The page passes { userhash, ts, sig } in PongSetup.identity or with Pong('identify', …).
  3. Pong checks the signature. If it is valid, this browser joins the client with that userhash, and earlier anonymous chats from it are merged in — nothing is lost.
FieldWhat
userhashYour stable user id (or a hash of it), up to 200 characters. Don’t use an email that can change.
tsUnix time in seconds when you signed. Signatures are accepted for 7 days and up to 5 minutes into the future.
sigHex HMAC-SHA256 of "<publicKey>:<userhash>:<ts>" with your identity secret.
Never put the identity secret in browser code. Anyone with it can read any user's chat.

PHP

<?php
$ts = time();
$sig = hash_hmac('sha256', PONG_PUBLIC_KEY . ":$userhash:$ts", PONG_IDENTITY_SECRET);
?>
<script>
  PongSetup = { identity: { userhash: '<?= $userhash ?>', ts: <?= $ts ?>, sig: '<?= $sig ?>' } };
</script>

Node.js

import { createHmac } from 'node:crypto';

export function pongIdentity(userId) {
  const userhash = String(userId);
  const ts = Math.floor(Date.now() / 1000);
  const sig = createHmac('sha256', process.env.PONG_IDENTITY_SECRET)
    .update(`${process.env.PONG_PUBLIC_KEY}:${userhash}:${ts}`)
    .digest('hex');
  return { userhash, ts, sig };
}

Python

import hashlib, hmac, os, time

def pong_identity(user_id):
    userhash, ts = str(user_id), int(time.time())
    msg = f"{os.environ['PONG_PUBLIC_KEY']}:{userhash}:{ts}".encode()
    sig = hmac.new(os.environ['PONG_IDENTITY_SECRET'].encode(), msg, hashlib.sha256).hexdigest()
    return {'userhash': userhash, 'ts': ts, 'sig': sig}

After login in a single-page app

Fetch a fresh signature from your API and pass it, with a name and email if you like:

const id = await fetch('/api/pong-identity').then((r) => r.json());
Pong('identify', { ...id, name: user.name, email: user.email });
Pong('on', 'identity', (e) => console.log('verified:', e.verified, 'merged:', e.merged));

On sign-out call Pong('logout') so the next user of this browser starts clean.

Their Instagram account

If you know a user’s Instagram account and your workspace has Instagram connected, tell Pong: their direct messages to your account then land in the same chat as their messages on your site, automatically. It works in both orders — an Instagram chat that already exists is joined at once, a future one soon after its first message.

Name the account by Instagram’s own numeric user id (for example 12281817) — the permanent one, not a username and not an app-scoped id from a login. A username can be renamed away and registered by somebody else; the id never changes.

Add two fields to the signed identity. The claim has its own signature, so a browser cannot attach somebody else’s account to an identity it was given:

FieldWhat
instagramIdThe numeric Instagram user id, as a string or a number.
instagramSigHex HMAC-SHA256 of "<publicKey>:<userhash>:<ts>:instagram:<id>" with your identity secret.
import { createHmac } from 'node:crypto';

export function pongIdentity(userId, instagramId) {
  const userhash = String(userId);
  const ts = Math.floor(Date.now() / 1000);
  const sign = (text) => createHmac('sha256', process.env.PONG_IDENTITY_SECRET).update(text).digest('hex');
  const base = `${process.env.PONG_PUBLIC_KEY}:${userhash}:${ts}`;
  const id = { userhash, ts, sig: sign(base) };
  if (instagramId) {
    id.instagramId = String(instagramId);
    id.instagramSig = sign(`${base}:instagram:${instagramId}`);
  }
  return id;
}

From your server you can do the same without the widget: PUT /api/clients/{id} with { "instagramId": "12281817" } (see the REST API). Only send an id the user proved is theirs, because whoever writes from that account continues this user’s chat there. Two users with verified identities are never merged by a claim; the latest claim of an account wins.

Pong learns the id of a person who writes to you from their public profile, shortly after their first message, so a waiting claim joins the chat within moments of that message rather than before it.

← PongSetup optionsREST API →