Webhooks
Chatoshi can send user data events to your server in real time. When users sign in or out through your widget, you get their information delivered to your endpoint.
Configuration
Configure your webhook in the User Data section of the partner portal:
- Webhook URL: The HTTPS endpoint on your server that will receive the events
- Secret Key: A cryptographic key used to verify webhook authenticity. Generate a new one at any time
- Shared Fields: Select which user fields you want to receive (Email, Phone, Username, User ID, Crypto Wallet Provider, Registration Country)
Payload Format
When an event occurs, Chatoshi sends a POST request to your webhook URL:
POST https://partner-webhook-url.com/webhook
Headers:
Content-Type: application/json
X-Webhook-Signature: a1b2c3d4e5...
X-Webhook-Timestamp: 1714838400
Body:
{
"event": "sso_sign_in",
"data": {
"user_id": "abc-123-def",
"email": "[email protected]",
"phone": "+1234567890",
"username": "johndoe",
"crypto_wallet_provider": "metamask",
"registration_country": "US"
}
}
Sign Out Event
The sso_sign_out event uses the same format; only the event field changes:
{
"event": "sso_sign_out",
"data": {
"user_id": "abc-123-def"
}
}
Headers
| Header | Description |
|---|---|
Content-Type | Always application/json |
X-Webhook-Signature | HMAC signature for verifying the payload |
X-Webhook-Timestamp | Unix timestamp of when the event was sent |
Data Fields
The data object contains only the fields you selected in the Shared Fields configuration. Available fields:
| Field | Type | Description |
|---|---|---|
user_id | string | Unique user identifier |
email | string | null | User's email address |
phone | string | null | User's phone number |
username | string | null | User's username |
crypto_wallet_provider | string | null | Wallet provider (e.g. metamask) |
registration_country | string | null | ISO country code |
Security
Verifying Signatures
Verify the X-Webhook-Signature header to ensure the webhook came from Chatoshi:
const crypto = require('crypto');
function verifyWebhookSignature(payload, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature, 'hex'),
Buffer.from(expected, 'hex')
);
}
// Usage
app.post('/webhook', (req, res) => {
const signature = req.headers['x-webhook-signature'];
const secret = process.env.WEBHOOK_SECRET;
if (!verifyWebhookSignature(JSON.stringify(req.body), signature, secret)) {
return res.status(401).send('Invalid signature');
}
// Process the event
console.log('Received event:', req.body.event);
console.log('User data:', req.body.data);
res.status(200).send('OK');
});
Best Practices
- Always verify the webhook signature before processing the payload
- Use HTTPS endpoints only
- Store your webhook secret securely (environment variables)
- Respond with a 2xx status code quickly; perform heavy processing asynchronously
- Handle duplicate events gracefully (webhooks may be retried)
Events
Currently supported event types:
| Event | Description |
|---|---|
sso_sign_in | Fired when a user signs in through the Chatoshi widget |
sso_sign_out | Fired when a user signs out through the Chatoshi widget |
Response Requirements
Your endpoint must respond with a 200 OK within a few seconds. If Chatoshi receives a non-2xx response or a timeout, the webhook may be retried.
app.post('/webhook', async (req, res) => {
// Acknowledge immediately
res.status(200).send('OK');
// Process asynchronously
await processUserData(req.body);
});