Customer identity

Log chat customers in with a JWT signed by your server so their conversations follow them across devices. Getting the signing secret, building the token, calling login on each SDK, describing customers, and logging out.

Chat starts anonymous: Kustomer knows the browser or device, not the person. When your user signs in to your site or app, log them in to chat too, so Kustomer attaches the conversation to their customer profile and shows them the same conversations on every device.

InteractiveFrom anonymous visitor to known customer
Your app
Your server
Kustomer
Your app → Kustomerfirst chat message
acme.com/orders
acmeSign in
Chat with AcmeAnonymous
Where’s my order #4417?
Let me check that for you.
Type a message…
Kustomer · customers
Waiting for a login token
Anonymous customer
known only by this browser
Where’s my order #4417?open
chat · started anonymously
JR
Jane Rivera
externalId: user_8412 · 6512ef18…
Return label for #3920done
chat · from their phone, last week

1. Visitor chats. Chat starts anonymous: Kustomer knows this browser, not the person. When the visitor first chats, Kustomer creates a customer for them, and the conversation lives on that anonymous customer.

Your app’s console
  1. ◆event onConversationCreate
Token from your server
Your server signs a token only when your app asks for one, after the user signs in. The chat secret never leaves your server.
Shown with the web SDK’s method names. iOS, Android and React Native use logIn and logOut the same way; on iOS the identity is kept in the keychain, and on Android you deregister push before logOut.

The token is signed with a secret only your server holds, so a visitor can't claim to be someone else, even though the chat key in your page is public.

1. Get the chat secret

The secret comes from an admin-only endpoint. Call it once and store the result in your server's secrets manager:

get/v1/auth/customer/settings
cURL
curl "https://acme.api.kustomerapp.com/v1/auth/customer/settings" \
  -H "Authorization: Bearer $KUSTOMER_ADMIN_KEY"

The key needs the org.admin.apps role, which org.admin includes. The secret is in the response at data.attributes.secret.

2. Sign a token

The JWT is signed with HS256 and has two claims:

ClaimValue
externalId or emailWho the user is. externalId is your own user ID, stored on the customer as externalId. If you send both, email wins.
iatIssued-at time in seconds. Kustomer rejects tokens issued more than 15 minutes before they're used.
import jwt from 'jsonwebtoken'

export function kustomerChatToken(user) {
  return jwt.sign(
    { externalId: user.id, iat: Math.floor(Date.now() / 1000) },
    process.env.KUSTOMER_CHAT_SECRET,
    { algorithm: 'HS256', header: { alg: 'HS256', typ: 'JWT' } },
  )
}

Serve it from an authenticated endpoint, for example POST /api/kustomer-token, and mint a fresh token each time: the 15-minute window means you can't cache them. A complete Express example is in kustomer/web-chat-sdk-samples.

3. Log in

Call login right after your user signs in, and skip it when isLoggedIn says they already are.

JavaScript
async function identifyChat(user) {
  Kustomer.isLoggedIn({ externalId: user.id }, async function (loggedIn) {
    if (loggedIn) return
    const { token } = await fetch('/api/kustomer-token', { method: 'POST' }).then((r) => r.json())
    Kustomer.login({ jwtToken: token }, function (res, error) {
      if (error) return console.error(error)
      console.log('Chat identified as', res.customerId)
    })
  })
}

The response is { identified, customerId, email, externalId }, and the widget fires onLogin. With Core, use KustomerCore.login and KustomerCore.isLoggedIn the same way.

Describe the customer

Once a customer is identified, add details agents should see. These write to the customer's profile in Kustomer.

JavaScript
Kustomer.describeCustomer(
  {
    attributes: { sharedPhones: ['+15555550123'] },
    customAttributes: { loyaltyTierStr: 'gold', lifetimeValueNum: 1840 },
  },
  function (res, error) {},
)
FieldMeaning
sharedEmails, sharedPhones, sharedSocialsContact details to add. (The older emails, phones and socials still work but are deprecated.)
Custom attributesUse attribute names with their type suffix, such as loyaltyTierStr or lifetimeValueNum, not display names. The attributes must already exist on the customer Klass. See Custom attributes.

To set attributes on a conversation instead, use describeConversation with the conversation ID.

Log out

Log out of chat whenever your user signs out, so the next person on the device starts fresh:

PlatformCall
WebKustomer.logout(cb) or KustomerCore.logout(cb)
iOSKustomer.logOut { error in } (also deregisters push)
AndroidderegisterDeviceForPushNotifications(), then logOut()
React Nativeawait KustomerChat.logOut() (also deregisters push)

Logging out clears the chat session and cached conversations from the device. It doesn't end or delete conversations in Kustomer.

Troubleshooting

SymptomCause
Login fails with a valid-looking tokeniat is more than 15 minutes old, or your server's clock is wrong. Mint tokens on demand and sync the clock.
Login fails with a fresh tokenIt's signed with the wrong secret. Fetch it again from /v1/auth/customer/settings, and check you're using the right org.
isLoggedIn is always falseYou're checking a different field than you signed: { email } versus { externalId }.
Customer has a different email than expectedThe token had both email and externalId, and email took precedence.