Référence du SDK des functions
Exports attendus, formes d'entrée et de sortie, appels SDK disponibles, contrats des hooks, codes d'erreur, types d'événements et ensemble des limites.
Le contrat complet entre votre TypeScript et l'environnement d'exécution OmniLab.
Exports attendus
Votre fichier doit exporter exactement un gestionnaire, nommé d'après le type de la function. La compilation vérifie cet export face au type correspondant : un gestionnaire manquant ou mal formé fait donc échouer la compilation au lieu d'échouer en production.
| Type | Export | Signature |
|---|---|---|
| Platform event | onPlatformEvent | (event: PlatformEvent) => void | Promise<void> |
Hook contact.create.pre | onContactCreatePre | (input: ContactCreateInput) => HookOutput<ContactCreateResult> |
Hook contact.verification | onContactVerification | (input: ContactVerificationInput) => HookOutput<ContactVerificationResult> |
Les types proviennent du module ./omnilab, disponible dans toute function :
import { OmniLab } from "./omnilab";
import type { PlatformEventHandler, ContactCreatePreHandler } from "./omnilab";Les gestionnaires peuvent être async. Seul TypeScript est pris en charge, dans un fichier unique dont le point d'entrée est user.ts.
Formes d'entrée
PlatformEvent
type PlatformEvent = {
type: string; // par exemple "touchpoint.completed.v1"
id: string; // unique par événement — dédoublonnez sur cette valeur
time: string; // ISO-8601
source: string;
data: Json; // le payload de l'événement, tel qu'OmniLab l'a publié
};La livraison se fait au moins une fois. Un même id peut arriver plusieurs fois : rendez donc les effets de bord externes idempotents.
Charges utiles des événements
data varie selon le type d'événement. Toutes les charges utiles portent le même bloc de contexte, et chaque famille y ajoute ses propres champs.
data: {
web_context: { utm: { … }, user_agent: { is_bot, is_mobile, is_desktop, is_tablet } },
interaction: { interaction_id, interaction_display_name, interaction_public_key, interaction_snapshot_id },
contact: { contact_id },
group_context: { group_id, unique_key },
tenant_context:{ subdomain, db_name },
}contact.contact_id est l'identifiant que vous passez à OmniLab.Contact.get et aux écritures de champs : c'est donc celui que vous lirez le plus souvent.
| Famille | Ajoute à data |
|---|---|
touchpoint.* et cta.clicked.v1 | touchpoint_id, touchpoint_title, touchpoint_type, interactive_totem_type, instant_game_type, event_id |
reward.* | reward_id, internal_reward_id, reward_display_name, reward_winning_method |
reward.won.v1, reward.redeemed.v1 | les champs de récompense, plus coupon_code_id et coupon_code_value |
contact.created.v1 | email, firstname, lastname |
contact.identified.v1, contact.authenticated.v1 | email |
notification.sent.v1 | notification_id, channel |
feedback.submitted.v1 | feedback, rating |
question.answered.v1 | question_id, answer |
Récupérez la charge utile exacte depuis Studio plutôt que depuis ce tableau
Ouvrez l'onglet Test de votre function et utilisez Load example. Le champ d'entrée se remplit avec une enveloppe complète et à jour pour n'importe quel type du catalogue — la forme même que la production livre. Considérez-la comme la référence et ce tableau comme un résumé de cadrage, et collez l'exemple directement pour développer sur de vrais champs.
Lisez néanmoins data avec prudence. Convertissez-le vers la forme attendue et considérez chaque champ comme potentiellement absent plutôt que de supposer une charge utile complète.
Contrats des hooks
Toute entrée de hook comporte un objet context, mais seul contact.verification en renseigne l'idempotency_key. OmniLab ne réessaie jamais l'invocation d'un hook ; la clé existe parce que l'opération qui l'entoure peut être retentée, et elle reste stable d'une tentative à l'autre — transmettez-la à tout fournisseur qui l'accepte afin qu'une répétition ne provoque pas de double envoi. Un renvoi explicite porte volontairement une clé différente.
contact.create.pre reçoit une clé vide. La création d'un contact n'a aucun effet de bord externe dont OmniLab soit responsable : aucune clé n'est donc calculée. Si votre gestionnaire appelle un fournisseur qui facture ou modifie quelque chose, vous devez fournir votre propre valeur de dédoublonnage — l'e-mail du contact est le choix habituel.
Construisez votre valeur de retour avec OmniLab.Flow — ne retournez jamais un objet ordinaire :
OmniLab.Flow.continue(result?) // poursuivre, en fournissant éventuellement des valeurs
OmniLab.Flow.deny(code, message) // bloquer l'opérationcontact.create.pre
S'exécute avant l'enregistrement d'un contact, depuis une inscription, l'admin, un import ou l'API.
{
contact: {
email: string;
phone_number: string;
firstname: string;
lastname: string;
custom_fields: Record<string, string>;
};
source: string; // toujours "signup" aujourd'hui
context: { idempotency_key: string }; // vide pour ce point d'ancrage
}Continuer accepte un email facultatif, un phone_number facultatif et des custom_fields. Seuls les champs que vous retournez sont appliqués — omettez un champ pour le conserver tel qu'il a été soumis. Refuser rejette le contact et fait échouer l'opération.
Le hook s'exécute avant l'enregistrement du contact et avant la mise à jour de tout CRM connecté : l'e-mail que vous retournez est donc la valeur qu'OmniLab persiste et celle qu'il transmet en aval. C'est ce qui rend la normalisation utile ici : retourner une adresse en minuscules évite que Foo@example.com et foo@example.com deviennent deux profils.
source est déclaré comme une chaîne pour permettre d'autres origines, mais tous les appels envoient aujourd'hui "signup". N'y appliquez pas encore de branchement.
contact.verification
S'exécute lorsqu'un message de vérification en double opt-in doit être envoyé. C'est votre gestionnaire qui est responsable de la livraison effective.
{
contact: { email: string; firstname: string; lastname: string };
interaction: { id: string; public_key: string };
verification_url: string;
expires_at: string;
context: { idempotency_key: string };
}Continuer accepte un provider_message_id facultatif, enregistré dans le journal d'exécution et sans autre effet. Continuer signifie que l'envoi a été accepté, pas que le message est arrivé. Refuser fait échouer l'inscription.
coupon.assign n'est pas disponible
Un point d'ancrage coupon.assign apparaît dans l'interface avec la mention « coming soon », et le SDK en porte encore les types, mais il n'est pas encore disponible : l'associer, l'invoquer et le tester sont tous refusés. Ne développez pas dessus avant sa mise à disposition.
Surface du SDK
Ce sont les seuls appels qui sortent de l'environnement isolé. Tout le reste de votre gestionnaire est du calcul pur.
Config
| Appel | Retour | Remarques |
|---|---|---|
OmniLab.Config.get(key) | La valeur de configuration, ou undefined | Configuration non sensible uniquement |
OmniLab.Config.getSecret("bundle.key") | La valeur du secret | Lève NOT_FOUND si absente. Le bundle doit être associé à la function. |
Http
| Appel | Remarques |
|---|---|
OmniLab.Http.fetch(url, options?) | La forme générale |
OmniLab.Http.get / post / put / patch / delete | Raccourcis pratiques |
Chaque requête est contrôlée face à la liste d'hôtes autorisés de la function, et les adresses internes ou privées sont refusées, qu'elles y figurent ou non. Seuls http et https sont autorisés. Chaque requête dispose d'un délai de 1500 ms, et un corps de réponse supérieur à 4 Mio est tronqué, avec un en-tête le signalant.
Contact
| Appel | Retour |
|---|---|
OmniLab.Contact.get(id) | Promise<Contact> |
OmniLab.Contact.setCustomField(id, key, value) | Promise<Contact> — le contact mis à jour |
OmniLab.Contact.setCustomFields(id, fields) | Promise<Contact> — tout ou rien |
Les trois renvoient le même objet : une écriture vous rend donc le contact qu'elle vient de modifier, et une relecture est rarement nécessaire :
{
id: string; // "contacts/<uuid>"
firstname: string;
lastname: string;
email: string;
phoneNumber: string;
externalId: string;
barcode: string;
customFields: Record<string, string>; // la seule partie modifiable
emailVerified: boolean;
optin: boolean;
hasAcceptedTerms: boolean;
isBlacklisted: boolean;
blacklistReason: string;
originInteraction: string;
}Tous les champs hormis customFields sont en lecture seule depuis une function. Les valeurs de champs personnalisés sont toujours des chaînes.
Les clés de champs personnalisés commençant par system. ou feature. sont réservées et rejetées avec INVALID_ARGUMENT. Une clé peut atteindre 128 caractères et une valeur 4 Ko. Préférez setCustomFields pour plusieurs clés : cela ne consomme qu'un appel de votre budget au lieu de plusieurs.
Journalisation
console.log, console.info, console.warn, console.error, console.debug et console.trace sont capturés dans l'enregistrement d'exécution et affichés dans l'onglet Test. La capture est limitée à 16 Ko au total, 256 lignes et 2 Ko par ligne ; au-delà, le journal est marqué comme tronqué.
Concevoir dans les limites du SDK
Les appels ci-dessus constituent toute la surface disponible. Lorsque votre logique demande davantage, joignez vos propres systèmes en HTTP et faites le travail là-bas. Trois conséquences à anticiper :
Gardez votre état hors de la function. Chaque invocation démarre à neuf : une valeur affectée au niveau du module a disparu à l'appel suivant. Conservez ce dont vous avez besoin dans les champs personnalisés d'un contact, ou dans votre propre service.
Accédez aux autres enregistrements OmniLab via l'API. Les functions lisent et écrivent des contacts. Pour les transactions, les récompenses, les smart links et le reste, appelez l'API OmniLab depuis vos propres systèmes.
Les functions s'exécutent sur déclencheur, pas sur horloge. Il n'est pas possible d'en planifier une. Si un traitement doit avoir lieu à heure fixe, lancez-le depuis votre propre ordonnanceur.
Codes d'erreur
Les échecs du SDK lèvent une erreur typée portant un code, un message et un indicateur retryable.
| Code | Réessayable | Cause typique |
|---|---|---|
NOT_FOUND | Non | Un secret absent, ou un contact inexistant |
PERMISSION_DENIED | Non | Une opération que la function n'est pas autorisée à effectuer |
INVALID_ARGUMENT | Non | Une clé de champ réservée, ou une valeur malformée |
ALREADY_EXISTS | Non | Une écriture en conflit |
EGRESS_BLOCKED | Non | Hôte non autorisé, ou résolvant vers une adresse privée |
TIMEOUT | Oui | Une requête HTTP a dépassé sa limite de 1500 ms |
UNAVAILABLE | Oui | Une dépendance était temporairement injoignable |
INTERNAL | Oui | Une erreur inattendue de la plateforme |
Les messages d'erreur sont destinés à une lecture humaine dans le journal d'exécution et leur formulation n'est pas stable. Ne les analysez jamais : branchez sur le code.
Sémantique des échecs
| Déclencheur | En cas d'échec | Réessais |
|---|---|---|
| Hook | Échec en mode fermé — l'opération est refusée. S'applique aux erreurs du gestionnaire, aux dépassements de délai et aux réponses non conformes au contrat. | Aucun |
| Platform event | L'activité à l'origine de l'événement n'est pas affectée ; l'exécution en échec est enregistrée | Un problème transitoire est réessayé avec temporisation croissante, jusqu'à cinq tentatives, puis abandonné. Un problème permanent — function supprimée, événement illisible — est abandonné immédiatement. Un gestionnaire qui lève une exception n'est pas réessayé, puisque le même code échouerait à nouveau. |
Types d'événements abonnables
Voici les types d'événements proposés par le sélecteur, regroupés comme il les regroupe et avec une recherche intégrée. Si OmniLab émet un type absent de cette liste, vous pouvez l'ajouter via le champ d'événement personnalisé.
| Famille | Libellé affiché | Type d'événement |
|---|---|---|
| Touchpoint | Started | touchpoint.started.v1 |
| Touchpoint | Participated | touchpoint.participated.v1 |
| Touchpoint | Participation form filled | touchpoint.participation_form_filled.v1 |
| Touchpoint | Completed | touchpoint.completed.v1 |
| Touchpoint | Terms accepted | touchpoint.terms_accepted.v1 |
| Touchpoint | CTA clicked | cta.clicked.v1 |
| Récompense | Eligible | reward.eligible.v1 |
| Récompense | Won | reward.won.v1 |
| Récompense | Lost | reward.lost.v1 |
| Récompense | Redeemed | reward.redeemed.v1 |
| Récompense | Temporary won | reward.temporary_won.v1 |
| Récompense | Temporary lost | reward.temporary_lost.v1 |
| Récompense | Temporary blocked | reward.temporary_blocked.v1 |
| Récompense | Expired | reward.expired.v1 |
| Contact | Created | contact.created.v1 |
| Contact | Identified | contact.identified.v1 |
| Contact | Authenticated | contact.authenticated.v1 |
| Autres | Notification sent | notification.sent.v1 |
| Autres | Feedback submitted | feedback.submitted.v1 |
| Autres | Question answered | question.answered.v1 |
Deux règles de plateforme s'ajoutent à cette liste. Les événements de visite de page anonymes ne sont jamais abonnables, et OmniLab refuse un abonnement qui en nomme un. Et un événement sans contact identifié ne déclenche jamais de function, quel que soit votre abonnement.
Les filtres acceptent un ensemble fermé de clés — tenant, organisation, interaction et contact — combinées par ET et comparées à l'identique.
Limites
Par invocation
| Limite | Valeur |
|---|---|
| Délai | 50 – 2000 ms, 2000 par défaut |
| Mémoire | 1 – 64 pages de 64 Kio, 32 pages par défaut (2 Mio) |
| Appels SDK | 20 |
| Délai par requête HTTP | 1500 ms |
| Corps de réponse HTTP | 4 Mio, tronqué au-delà |
Le délai ne couvre que votre gestionnaire. Le temps qu'OmniLab consacre à préparer son exécution n'est pas décompté de votre limite — le détail d'exécution présente les deux séparément.
Par function
| Limite | Valeur |
|---|---|
| Taille de la source | 1 Mo |
| Hôtes autorisés | 16 |
| Configuration du plugin | 32 clés, 4 Ko au total |
| Bundles de secrets associés | 5 |
| Abonnements actifs par type d'événement | 5 par tenant |
| Associations actives par point d'ancrage | 1 globale, plus 1 par organisation |
| Compilations simultanées | 1 |
Par tenant
| Limite | Valeur |
|---|---|
| Bundles de secrets | 5 |
| Secrets par bundle | 32 |
| Clé de bundle | Jusqu'à 32 caractères, lettres minuscules, chiffres et tirets bas, commençant par une lettre |
Conservation et troncature
| Élément | Valeur |
|---|---|
| Enregistrements d'exécution | 24 heures |
| Entrée et sortie enregistrées | 256 Ko de chaque côté, tronquées au-delà, avec indication des tailles d'origine |
| Logs capturés | 16 Ko au total, 256 lignes, 2 Ko par ligne |
| Journal de compilation affiché dans Studio | Les 8 derniers Ko |
Compilation
| Limite | Valeur |
|---|---|
| Vérification des types à l'enregistrement | 10 secondes |
| Compilation | 4 minutes |
| Une compilation bloquée est libérée, vous pouvez en relancer une | 15 minutes |
Il n'existe aucune limitation de débit à l'invocation. Le volume est encadré autrement : par le budget d'appels SDK, le plafond d'abonnements et l'exclusion des événements de visite de page anonymes.
Pour aller plus loin
Tester et superviser des functions
Lancer des invocations de test dans l'environnement isolé réel, lire le journal d'exécution et diagnostiquer une function en échec ou silencieuse.
Guides
Approfondir les patterns d'implémentation derrière les scripts, les templates Liquid, les wrappers borne et les parcours de réservation.