Client Portal
Affiche des champs personnalises (usage, texte, nombre, date, selection, action) sur les fiches de tes utilisateurs et entreprises depuis le dashboard Saasy.
Client Portal
Le Client Portal te permet d'afficher des donnees metier sur les fiches de tes utilisateurs dans le dashboard Saasy. Usage API, plan actif, date d'expiration, boutons d'action : tu decides ce qui est pertinent pour ton equipe support.
Deux modes d'alimentation sont disponibles :
- Push : ton application envoie les donnees a Saasy via l'API portal.
- Pull : Saasy interroge ton API en temps reel lorsqu'un collaborateur ouvre une fiche utilisateur.
SSO obligatoire
Le Client Portal ne peut etre active que si ton application est en mode SSO. L'externalId du token SSO est utilise comme cle de correspondance. Si tu n'as pas encore configure le SSO, consulte le guide Authentification SSO et active le mode SSO dans Parametres > SSO avant de configurer le portal.
Modes de fonctionnement
Le portal supporte deux modes :
| Mode | Description |
|---|---|
| Users uniquement | Chaque utilisateur est un tenant independant. Les champs custom s'affichent sur la fiche user. |
| Entreprises et users | Les utilisateurs sont rattaches a des entreprises. Tu peux definir des champs custom sur les fiches user ET sur les fiches entreprise. |
Types de champs
| Type | Description | Exemple |
|---|---|---|
usage_bar | Barre de progression avec valeur courante / max | 150 / 500 appels API |
text | Texte libre | Statut: Actif |
number | Nombre formate | 42 projets |
datetime | Date/heure localisee | Expire le 15/03/2026 |
select | Menu deroulant editable | Plan: free / starter / growth / scale |
action | Bouton qui declenche un webhook | Reinitialiser le mot de passe |
Valeurs illimitees
Pour les barres d'utilisation (usage_bar), une valeur max de -1 est interpretee comme illimitee. La barre affiche 150 / ∞ appels API au lieu d'un nombre negatif. Utile pour les plans sans limites.
Type select : champs editables
Le type select permet a tes collaborateurs de modifier une valeur directement depuis la fiche utilisateur. Par exemple, changer le plan d'un client.
Configuration :
- Ajoute un champ de type Selection.
- Renseigne les options (une par ligne) :
free,starter,growth,scale. - Un bouton Sauvegarder apparait automatiquement sur la fiche lorsqu'une valeur est modifiee.
Lorsqu'un collaborateur clique sur Sauvegarder, Saasy :
- Met a jour les
customAttributesdu TrackedUser en base. - Si le Pull mode est active, pousse la modification vers ton API via
PATCH {apiBaseUrl}/users/{externalId}/fieldavec{ "key": "plan", "value": "growth" }.
Configuration
Activer le portal
- Va dans Parametres > SSO de ton app.
- Scrolle jusqu'a la section Client Portal.
- Active le toggle.
- Choisis le mode : Users uniquement ou Entreprises et users.
Configurer les champs
- Clique sur Ajouter un champ dans la section User Fields (ou Company Fields).
- Remplis la cle API (identifiant unique, ex:
api_calls), le label, et le type. - Pour un champ
usage_bar, renseigne l'unite et la cle max (ex:api_calls_max). - Pour un champ
select, renseigne les options (une par ligne). - Pour un champ
action, renseigne l'URL du webhook et le texte du bouton. - Enregistre.
Recuperer le token portal
Le Portal Token est affiche dans la section configuration. Copie-le et stocke-le dans les variables d'environnement de ton serveur. Ce token authentifie les appels API du portal.
Configurer le Pull mode (optionnel)
Si tu souhaites que Saasy recupere les donnees en temps reel depuis ton API :
- Renseigne l'URL de ton API dans le champ dedie (ex:
https://mon-app.com/api/portal). - Clique sur Enregistrer.
- Ton API doit exposer les endpoints decrits dans la section Pull mode ci-dessous.
Push mode (defaut)
Le push mode est le fonctionnement classique : ton serveur envoie les donnees a Saasy lorsqu'elles changent.
API Reference
Toutes les routes du portal sont authentifiees via le header X-Portal-Token.
Token serveur
Le portal token ne doit jamais etre expose cote client. Utilise-le uniquement depuis ton serveur backend.
Mettre a jour les champs d'un utilisateur
Pousse les valeurs des champs custom pour un utilisateur identifie par son externalId (l'id que tu passes dans le token SSO).
curl -X PATCH https://api.saasy.fr/api/portal/users/user_123 \
-H "X-Portal-Token: pt_votre_token" \
-H "Content-Type: application/json" \
-d '{
"fields": {
"api_calls": 150,
"api_calls_max": 500,
"plan": "Enterprise",
"trial_ends_at": "2026-04-15T00:00:00Z"
}
}'Creer ou mettre a jour une entreprise
Cree l'entreprise si elle n'existe pas, ou la met a jour.
curl -X PUT https://api.saasy.fr/api/portal/companies/company_456 \
-H "X-Portal-Token: pt_votre_token" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Corp",
"domain": "acme.com",
"customAttributes": {
"seats_used": 12,
"seats_max": 25,
"plan": "Business"
}
}'Mettre a jour les champs d'une entreprise
curl -X PATCH https://api.saasy.fr/api/portal/companies/company_456 \
-H "X-Portal-Token: pt_votre_token" \
-H "Content-Type: application/json" \
-d '{
"fields": {
"seats_used": 15,
"mrr": 299
}
}'Assigner un utilisateur a une entreprise
curl -X PATCH https://api.saasy.fr/api/portal/users/user_123/company \
-H "X-Portal-Token: pt_votre_token" \
-H "Content-Type: application/json" \
-d '{
"companyExternalId": "company_456"
}'Pull mode
Le pull mode permet a Saasy d'interroger ton API en temps reel pour recuperer les donnees utilisateur, au lieu d'attendre que tu les pousses.
Principe
Collaborateur ouvre une fiche utilisateur
→ Saasy appelle GET {apiBaseUrl}/users/{externalId}
→ Ton API retourne { customAttributes: { ... } }
→ Saasy affiche les donnees fraiches et les met en cacheSi ton API est indisponible (timeout 5s, erreur), Saasy affiche les dernieres donnees en cache.
Configuration
Renseigne l'URL de ton API dans Parametres > SSO > Client Portal (ex: https://mon-app.com/api/portal) et clique sur Enregistrer.
Endpoints a implementer
Ton API doit exposer les endpoints suivants :
Recuperer les donnees d'un utilisateur
Reponse attendue :
{
"customAttributes": {
"plan": "growth",
"api_calls": 1523,
"api_calls_max": 5000,
"trial_ends_at": "2026-06-15T00:00:00Z"
}
}Les cles dans customAttributes doivent correspondre aux cles API des champs configures dans le portal.
Recevoir une modification de champ (optionnel)
Quand un collaborateur modifie un champ select et clique sur Sauvegarder, Saasy envoie la modification a ton API :
{
"key": "plan",
"value": "growth"
}Ce endpoint est optionnel. Si tu ne l'implementes pas, la valeur est quand meme sauvegardee dans Saasy.
Verification de signature
Chaque requete pull de Saasy inclut des headers de signature :
| Header | Description |
|---|---|
X-Portal-Token | Le portal token de l'app |
X-Saasy-Signature | HMAC-SHA256 de {timestamp}:{path} |
X-Saasy-Timestamp | Timestamp ISO de la requete |
const crypto = require('crypto');
function verifySaasySignature(req, portalToken) {
const signature = req.headers['x-saasy-signature'];
const timestamp = req.headers['x-saasy-timestamp'];
const path = req.path; // ex: /users/user_123
const expected = crypto
.createHmac('sha256', portalToken)
.update(`${timestamp}:${path}`)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}Pour les requetes PATCH (modification de champ), la signature est calculee sur {timestamp}:PATCH:{path}.
Creation et rattachement automatique via SSO
En mode Entreprises et users, Saasy cree et rattache automatiquement les entreprises a partir des donnees de ton token JWT SSO. Tu n'as pas besoin de creer les entreprises au prealable.
A chaque connexion SSO, si le JWT contient un champ companies avec au moins un objet comportant externalId et name, Saasy :
- Cree ou met a jour l'entreprise dans sa base (upsert sur l'
externalId). - Rattache l'utilisateur a la premiere entreprise de la liste.
const ssoToken = jwt.sign({
id: user.id,
email: user.email,
name: user.name,
companies: [
{
externalId: user.company.id, // obligatoire
name: user.company.name, // obligatoire
domain: user.company.domain, // optionnel
},
],
}, process.env.SAASY_SSO_KEY, { expiresIn: '1h' });Format du champ companies
Utilise le format objet avec au minimum externalId et name pour que Saasy puisse creer automatiquement l'entreprise. Le format string (["company_456"]) permet uniquement le rattachement a une entreprise deja existante.
Recuperer le profil utilisateur (widget)
Si tu as besoin de recuperer les informations de l'utilisateur connecte et sa societe depuis le widget ou ton front-end, utilise la route suivante :
Cette route necessite un token widget (obtenu via l'authentification SSO) dans le header Authorization: Bearer <token>.
Reponse
{
"data": {
"id": "...",
"externalId": "user_123",
"email": "john@acme.com",
"name": "John Doe",
"authMethod": "sso",
"customAttributes": { "plan": "Enterprise" },
"companies": ["company_456"],
"company": {
"id": "...",
"externalId": "company_456",
"name": "Acme Corp",
"domain": "acme.com",
"customAttributes": { "seats_used": 12, "seats_max": 25 }
},
"lastSeenAt": "2026-03-11T10:00:00.000Z",
"createdAt": "2026-01-15T08:30:00.000Z"
}
}Si l'utilisateur n'est rattache a aucune entreprise, le champ company est absent de la reponse.
Exemples d'integration
Push mode
const PORTAL_TOKEN = process.env.SAASY_PORTAL_TOKEN;
const SAASY_API = 'https://api.saasy.fr/api';
// Mettre a jour l'usage d'un user apres chaque appel API
async function syncUserUsage(userId, apiCalls, apiCallsMax) {
await fetch(`${SAASY_API}/portal/users/${userId}`, {
method: 'PATCH',
headers: {
'X-Portal-Token': PORTAL_TOKEN,
'Content-Type': 'application/json',
},
body: JSON.stringify({
fields: { api_calls: apiCalls, api_calls_max: apiCallsMax },
}),
});
}Pull mode
const express = require('express');
const crypto = require('crypto');
const app = express();
const PORTAL_TOKEN = process.env.SAASY_PORTAL_TOKEN;
// Middleware de verification de signature
function verifySaasy(req, res, next) {
const token = req.headers['x-portal-token'];
if (token !== PORTAL_TOKEN) return res.status(401).json({ error: 'Unauthorized' });
next();
}
// GET /api/portal/users/:externalId — retourne les customAttributes
app.get('/api/portal/users/:externalId', verifySaasy, async (req, res) => {
const user = await db.users.findById(req.params.externalId);
if (!user) return res.status(404).json({ error: 'Not found' });
res.json({
customAttributes: {
plan: user.plan,
api_calls: user.usage.apiCalls,
api_calls_max: user.limits.apiCalls,
trial_ends_at: user.trialEndsAt?.toISOString() || null,
},
});
});
// PATCH /api/portal/users/:externalId/field — recoit une modification
app.patch('/api/portal/users/:externalId/field', verifySaasy, express.json(), async (req, res) => {
const { key, value } = req.body;
const user = await db.users.findById(req.params.externalId);
if (!user) return res.status(404).json({ error: 'Not found' });
// Applique la modification (ex: changement de plan)
if (key === 'plan') user.plan = value;
await user.save();
res.json({ success: true });
});Webhooks d'action
Les champs de type action declenchent un webhook POST vers l'URL configuree lorsqu'un collaborateur clique sur le bouton dans le dashboard.
Format du payload
{
"action": "reset_password",
"fieldId": "abc123",
"user": {
"id": "...",
"externalId": "user_123",
"email": "john@acme.com",
"name": "John Doe"
},
"company": null,
"timestamp": "2026-03-09T14:30:00.000Z"
}Verification HMAC
Chaque webhook est signe avec le portal token comme secret HMAC-SHA256. La signature est envoyee dans le header X-Saasy-Signature.
const crypto = require('crypto');
function verifyWebhook(body, signature, portalToken) {
const expected = crypto
.createHmac('sha256', portalToken)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Dans ton handler Express
app.post('/webhook/saasy', express.text({ type: '*/*' }), (req, res) => {
const signature = req.headers['x-saasy-signature'];
if (!verifyWebhook(req.body, signature, process.env.PORTAL_TOKEN)) {
return res.status(401).send('Invalid signature');
}
const payload = JSON.parse(req.body);
// Traite l'action...
res.sendStatus(200);
});Securite
- Token serveur : le portal token ne doit jamais etre expose cote client. Stocke-le dans tes variables d'environnement.
- HMAC-SHA256 : les webhooks d'action et les requetes pull sont signes. Verifie toujours la signature avant de traiter le payload.
- SSRF : les URLs de webhook et l'API Base URL ne peuvent pas pointer vers des adresses IP privees en production (localhost, 10.x, 192.168.x, etc.).
- Rate limiting : les routes portal sont limitees a 1000 requetes par 15 minutes par IP.
- Taille : chaque push de
customAttributesest limite a 64 KB. Maximum 50 champs par entite. - Timeout pull : les requetes pull vers ton API ont un timeout de 5 secondes. Si ton API ne repond pas a temps, les donnees en cache sont utilisees.