SDK .NET
Le client officiel .NET de l’API LIKYLY : envoyez votre catalogue et ce que font vos visiteurs, recevez des recommandations personnalisées.
catalogue → événements → recommandations → impression → clic → achat
Installation
dotnet add package Likyly.NET 8 ou plus récent. Aucune dépendance hors framework (HttpClient, System.Text.Json). Asynchrone partout, CancellationToken sur chaque appel.
Démarrage rapide
Les extraits sont des instructions de niveau supérieur (Program.cs) ; dans une classe, placez-les dans une méthode async.
1. Initialiser
Créez un client et réutilisez-le. Utilisez votre clé secrète côté serveur (voir Quelle clé ?).
using Likyly;
// Backend only: the secret key gives access to your catalog and your users.
var likyly = new LikylyClient(new LikylyClientOptions
{
ApiKey = Environment.GetEnvironmentVariable("LIKYLY_SECRET_KEY")!,
});2. Envoyer votre catalogue
// Send your catalog once (and whenever it changes). Ids are YOUR ids: SKUs, UUIDs, Shopify gids...
await likyly.Items.UpsertManyAsync(
[
new ItemImport { ItemId = "SKU-1", Title = "Nike Air Max", Description = "Running shoe with air cushioning", Properties = new Dictionary<string, object?> { ["category"] = "shoes", ["brand"] = "Nike", ["price"] = 129.9 } },
new ItemImport { ItemId = "SKU-2", Title = "Adidas Ultraboost", Description = "Responsive running shoe", Properties = new Dictionary<string, object?> { ["category"] = "shoes", ["brand"] = "Adidas", ["price"] = 149 } },
new ItemImport { ItemId = "SKU-3", Title = "Nike Pegasus", Description = "Everyday running shoe", Properties = new Dictionary<string, object?> { ["category"] = "shoes", ["brand"] = "Nike", ["price"] = 119 } },
]);
// Users are optional: describe them if you want the profile to travel with their events.
await likyly.Users.UpsertAsync("user_123", new UserInput { Properties = new Dictionary<string, object?> { ["country"] = "FR", ["segment"] = "premium" } });Les identifiants sont les vôtres : n’importe quelle chaîne (SKU-123, un UUID, gid://shopify/Product/123). properties est libre ; category et description améliorent la similarité, le reste est stocké tel quel.
3. Suivre les événements
// Tell LIKYLY what your visitors do.
await likyly.Events.ViewAsync(new EventInput { UserId = "user_123", ItemId = "SKU-1" });
// Purchases carry an EventId: replaying the call can never count the sale twice.
await likyly.Events.PurchaseAsync(new EventInput
{
EventId = "purchase_order_9281_SKU-1",
UserId = "user_123",
ItemId = "SKU-1",
Quantity = 1,
Properties = new Dictionary<string, object?> { ["price"] = 129.9, ["currency"] = "EUR", ["orderId"] = "order_9281" },
});4. Obtenir des recommandations
// Ask what to show. You never pick an algorithm: LIKYLY chooses the best strategy for what it knows.
var recs = await likyly.Recommendations.GetAsync(new RecommendationRequest { UserId = "user_123", Placement = "homepage", Limit = 3 });
Console.WriteLine($"strategy: {recs.Strategy}");
foreach (var item in recs.Items)
{
Console.WriteLine($"{item.ItemId} {item.Title}");
}Vous ne choisissez jamais d’algorithme : LIKYLY sélectionne la meilleure stratégie selon ce qu’il sait du visiteur et vous dit laquelle il a utilisée (strategy : hybrid, content, collaborative, session ou popular). Sans utilisateur, sans session et sans article, vous obtenez tout de même une réponse utile : ce qui est populaire.
5. Boucler : l’attribution
// Send the RecommendationId back: LIKYLY measures which recommendations get seen, clicked and bought.
var shown = new EventInput { UserId = "user_123", ItemId = recs.Items[0].ItemId, RecommendationId = recs.RecommendationId, Placement = "homepage" };
await likyly.Events.ImpressionAsync(shown);
await likyly.Events.ClickAsync(shown);Renvoyez l’identifiant de recommandation sur les événements suivants (impression à l’affichage, click, add_to_cart, purchase) : LIKYLY mesure ainsi quelles recommandations convertissent réellement.
Quelle clé ?
| Clé secrète | Clé publique | |
|---|---|---|
| Où l'utiliser | sur votre serveur uniquement | n'importe où, y compris une page web |
| Recommandations | oui | oui |
| Enregistrer des événements | oui | oui |
| Articles et utilisateurs (créer, lire, supprimer) | oui | non (PermissionDeniedException) |
Explications debug | oui | non |
La clé publique ne peut que lire des recommandations et écrire des événements : elle peut donc être exposée. Ne mettez jamais la clé secrète dans du code livré à un navigateur ou à une application mobile.
// Where the code is exposed to visitors, use the PUBLIC key: it can only read recommendations and record events.
var front = new LikylyClient(new LikylyClientOptions
{
ApiKey = Environment.GetEnvironmentVariable("LIKYLY_PUBLIC_KEY")!,
});
// A visitor who is not logged in: use an anonymous session id (any string you generate and keep in a cookie).
await front.Events.ViewAsync(new EventInput { SessionId = "sess_abc", ItemId = "SKU-2" });
var forVisitor = await front.Recommendations.GetAsync(new RecommendationRequest { SessionId = "sess_abc", ViewedItemIds = ["SKU-2"], Placement = "product_page", Limit = 3 });Événements
track est le mécanisme unique ; impression, view, click, add_to_cart, remove_from_cart et purchase sont des raccourcis vers celui-ci. Les types d’événements sont des chaînes libres : utilisez les vôtres (favorite, share, video_played...) aussi librement que les officiels.
// Any string is a valid event type: track what matters to your business.
await likyly.Events.TrackAsync("favorite", new EventInput { UserId = "user_123", ItemId = "SKU-2", Properties = new Dictionary<string, object?> { ["list"] = "wishlist" } });
// Up to 1000 events per call, each with its own type.
await likyly.Events.TrackManyAsync(
[
new TypedEventInput { Type = "view", UserId = "user_123", ItemId = "SKU-3" },
new TypedEventInput { Type = "add_to_cart", UserId = "user_123", ItemId = "SKU-3", Quantity = 1 },
]);- Un événement porte un article et un utilisateur, une session, ou les deux (les deux relient la session anonyme à l’utilisateur une fois connecté).
- Visiteurs anonymes : générez un identifiant de session (n’importe quelle chaîne) et gardez-le dans un cookie ; aucune connexion requise.
propertiesest un objet libre (prix, devise, numéro de commande...). Ses clés sont stockées telles que vous les envoyez et jamais renommées par le SDK.- L’identifiant d’événement (clé d’idempotence) : rejouer un événement avec le même identifiant n’enregistre rien et renvoie
duplicate: true. Renseignez-le sur les achats, par exemplepurchase_<commande>_<article>. placementest un libellé libre de l’endroit où quelque chose s’est produit ou sera affiché (homepage,product_page,cart...).
Recommandations avancées
recommendations.get est la voie normale. Pour choisir vous-même la stratégie :
// Advanced Recommendations: one strategy at a time, when you want to choose.
var similar = await likyly.Recommendations.SimilarAsync(new SimilarOptions { ItemId = "SKU-1", Limit = 3 });
var hybrid = await likyly.Recommendations.HybridAsync(new HybridOptions { UserId = "user_123", ItemId = "SKU-1", Alpha = 0.7, Limit = 3 });
var session = await likyly.Recommendations.SessionAsync(new SessionOptions { ViewedItemIds = ["SKU-1", "SKU-2"], Limit = 3 });| Stratégie | Ce qu'elle fait |
|---|---|
popular | Les articles les plus populaires : le repli pour un visiteur sans historique |
similar | Articles similaires à un article (similarité de contenu), sans utilisateur |
collaborative | Ce qu'ont aimé les utilisateurs aux historiques proches. Nécessite un modèle entraîné, sinon une erreur « introuvable » |
hybrid | Articles similaires personnalisés pour un utilisateur ; alpha (0 à 1) pondère le collaboratif face au contenu |
session | Pondéré par la récence, à partir des articles vus ou de l'historique d'un utilisateur (l'un des deux, exactement) |
Ces endpoints portent les identifiants dans le chemin de l’URL : un identifiant contenant / (comme gid://shopify/Product/1) ou, pour la liste de session, ,, est refusé par une erreur de validation. Utilisez alors recommendations.get, qui prend les identifiants dans le corps de la requête.
Erreurs
Les erreurs sont typées (toutes sont des LikylyException). Interceptez celles qui vous concernent :
| Erreur | Quand |
|---|---|
ValidationException | Requête invalide : détectée par le SDK avant tout envoi, ou 422 de l'API (le message nomme les champs fautifs) |
AuthenticationException | 401 : clé API absente, invalide ou révoquée |
PermissionDeniedException | 403 : par exemple une clé publique utilisée pour une opération réservée à la clé secrète, ou une limite de plan |
NotFoundException | 404 |
RateLimitException | 429 : voir la valeur de retry-after |
ApiException | Tout autre statut d'erreur de l'API (les quatre lignes précédentes en sont des sous-types) |
NetworkException | Aucune réponse HTTP : DNS, connexion coupée, TLS |
RequestTimeoutException | La requête a expiré |
Les erreurs issues de l’API portent : StatusCode, RequestId, RetryAfter (seconds), Body. Communiquez l’identifiant de requête lorsque vous contactez le support.
try
{
await likyly.Items.GetAsync("does-not-exist");
}
catch (NotFoundException e)
{
Console.WriteLine($"no such item, request {e.RequestId}");
}
catch (RateLimitException e)
{
Console.WriteLine($"slow down, retry in {e.RetryAfter}");
}Tentatives et délais
Le SDK relance pour vous les échecs transitoires (2 tentatives par défaut, backoff exponentiel à partir de 0,5 s plafonné à 8 s, avec jitter complet) :
- 429 : toujours relancé, en respectant
Retry-After. UnRetry-Aftersupérieur à 60 s n’est pas attendu : l’erreur de limite de débit est renvoyée. - 502, 503, 504, connexions coupées et délais dépassés : relancés uniquement quand rejouer l’appel est sans danger : lectures, upserts, suppressions, événements portant un identifiant d’événement, et lots dont chaque événement en a un.
- Un événement sans identifiant d’événement n’est jamais relancé après un échec ambigu : un réseau capricieux ne peut donc jamais enregistrer un achat deux fois. Renseignez l’identifiant sur les événements qui comptent et ils deviennent relançables.
- Les autres erreurs 4xx ne sont jamais relancées.
Configuration
| Réglage | Option | Par défaut |
|---|---|---|
| Clé API | ApiKey | obligatoire |
| URL de base | BaseUrl | https://api.likyly.com |
| Catalogue | Catalog | votre seul catalogue |
| Délai d'attente | Timeout (TimeSpan) | 10 s |
| Nouvelles tentatives | MaxRetries | 2 |
| Suffixe User-Agent | UserAgent | aucun |
| Client HTTP personnalisé | new LikylyClient(options, httpClient) | a new HttpClient |
var tuned = new LikylyClient(new LikylyClientOptions
{
ApiKey = Environment.GetEnvironmentVariable("LIKYLY_SECRET_KEY")!,
Timeout = TimeSpan.FromSeconds(5), // per attempt (default 10 s)
MaxRetries = 3, // automatic retries on 429 / 502 / 503 / 504 / network errors (default 2, 0 disables)
UserAgent = "my-shop/1.4", // appended to the SDK's User-Agent
});Passez un CancellationToken en dernier argument ; le Timeout du client s'applique à chaque tentative. Enregistrez LikylyClient en singleton (services.AddSingleton(...)).
Référence
Clé : S = clé secrète uniquement, P = clé publique ou secrète.
| Méthode | Effet | Clé |
|---|---|---|
likyly.Items.GetAsync | Un article par votre identifiant | S |
likyly.Items.ListAsync | Une page du catalogue (limit / offset), avec le total | S |
likyly.Items.UpsertAsync | Crée ou remplace un article (idempotent) | S |
likyly.Items.DeleteAsync | Supprime un article (les événements déjà enregistrés sont conservés) | S |
likyly.Items.UpsertManyAsync | Upsert par lot, 1 à 1000 articles | S |
likyly.Items.ImportAsync | Alias de l'upsert par lot ci-dessus (lot JSON) | S |
likyly.Items.DeleteManyAsync | Suppression par lot, 1 à 1000 identifiants ; les inconnus sont signalés, pas levés | S |
likyly.Users.GetAsync | Un profil utilisateur | S |
likyly.Users.ListAsync | Une page d'utilisateurs (limit / offset) | S |
likyly.Users.UpsertAsync | Crée ou remplace un profil (properties libres) | S |
likyly.Users.DeleteAsync | Efface le profil et tous les événements enregistrés pour cet utilisateur | S |
likyly.Users.ImportAsync | Upsert d'utilisateurs par lot, 1 à 1000 | S |
likyly.Events.TrackAsync | Enregistre un événement de n'importe quel type (chaîne libre) | P |
likyly.Events.TrackManyAsync | Jusqu'à 1000 événements par appel, chacun avec son propre type | P |
likyly.Events.ImpressionAsync | L'article a été affiché au visiteur | P |
likyly.Events.ViewAsync | Le visiteur a consulté l'article | P |
likyly.Events.ClickAsync | Le visiteur a cliqué sur l'article | P |
likyly.Events.AddToCartAsync | Le visiteur a ajouté l'article au panier | P |
likyly.Events.RemoveFromCartAsync | Le visiteur a retiré l'article du panier | P |
likyly.Events.PurchaseAsync | Le visiteur a acheté l'article (renseignez un identifiant d'événement) | P |
likyly.Recommendations.GetAsync | Recommandations avec stratégie automatique : utilisateur, session, article ou articles vus | P |
likyly.Recommendations.PopularAsync | Avancé : les articles les plus populaires | P |
likyly.Recommendations.SimilarAsync | Avancé : articles similaires à un article | P |
likyly.Recommendations.CollaborativeAsync | Avancé : ce qu'ont aimé les utilisateurs similaires (modèle entraîné requis) | P |
likyly.Recommendations.HybridAsync | Avancé : articles similaires personnalisés pour un utilisateur (alpha) | P |
likyly.Recommendations.SessionAsync | Avancé : à partir des articles vus ou de l'historique d'un utilisateur | P |
Les listes sont paginées avec limit et offset (la page par défaut de l’API compte 100 articles) ; le total est fourni avec la page. Les utilisateurs ont un import par lot mais pas de suppression par lot : l’API n’a pas de tel endpoint. L’import des articles est un alias de l’upsert par lot (l’import CSV est une fonction du tableau de bord).
Notes .NET
- Les modèles de requête et de réponse sont des records/classes C# avec annotations de nullabilité ; les identifiants sont des chaînes, jamais converties. Les clés de
Properties(unDictionary<string, object?>) vous appartiennent et ne sont jamais renommées.
Paquet : Likyly sur NuGet. Référence REST complète : api.likyly.com/docs.