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.

9 min de lecture

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.

TypeExportSignature
Platform eventonPlatformEvent(event: PlatformEvent) => void | Promise<void>
Hook contact.create.preonContactCreatePre(input: ContactCreateInput) => HookOutput<ContactCreateResult>
Hook contact.verificationonContactVerification(input: ContactVerificationInput) => HookOutput<ContactVerificationResult>

Les types proviennent du module ./omnilab, disponible dans toute function :

Importer le SDK et ses types
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

Ce que reçoit une function d'événement
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.

Présent dans toute charge utile d'événement
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.

FamilleAjoute à data
touchpoint.* et cta.clicked.v1touchpoint_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.v1les champs de récompense, plus coupon_code_id et coupon_code_value
contact.created.v1email, firstname, lastname
contact.identified.v1, contact.authenticated.v1email
notification.sent.v1notification_id, channel
feedback.submitted.v1feedback, rating
question.answered.v1question_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 :

Les deux décisions possibles
OmniLab.Flow.continue(result?)          // poursuivre, en fournissant éventuellement des valeurs
OmniLab.Flow.deny(code, message)        // bloquer l'opération

contact.create.pre

S'exécute avant l'enregistrement d'un contact, depuis une inscription, l'admin, un import ou l'API.

Entrée
{
  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.

Entrée
{
  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

AppelRetourRemarques
OmniLab.Config.get(key)La valeur de configuration, ou undefinedConfiguration non sensible uniquement
OmniLab.Config.getSecret("bundle.key")La valeur du secretLève NOT_FOUND si absente. Le bundle doit être associé à la function.

Http

AppelRemarques
OmniLab.Http.fetch(url, options?)La forme générale
OmniLab.Http.get / post / put / patch / deleteRaccourcis 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

AppelRetour
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 :

Contact
{
  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.

CodeRéessayableCause typique
NOT_FOUNDNonUn secret absent, ou un contact inexistant
PERMISSION_DENIEDNonUne opération que la function n'est pas autorisée à effectuer
INVALID_ARGUMENTNonUne clé de champ réservée, ou une valeur malformée
ALREADY_EXISTSNonUne écriture en conflit
EGRESS_BLOCKEDNonHôte non autorisé, ou résolvant vers une adresse privée
TIMEOUTOuiUne requête HTTP a dépassé sa limite de 1500 ms
UNAVAILABLEOuiUne dépendance était temporairement injoignable
INTERNALOuiUne 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éclencheurEn cas d'échecRé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 eventL'activité à l'origine de l'événement n'est pas affectée ; l'exécution en échec est enregistréeUn 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é.

FamilleLibellé affichéType d'événement
TouchpointStartedtouchpoint.started.v1
TouchpointParticipatedtouchpoint.participated.v1
TouchpointParticipation form filledtouchpoint.participation_form_filled.v1
TouchpointCompletedtouchpoint.completed.v1
TouchpointTerms acceptedtouchpoint.terms_accepted.v1
TouchpointCTA clickedcta.clicked.v1
RécompenseEligiblereward.eligible.v1
RécompenseWonreward.won.v1
RécompenseLostreward.lost.v1
RécompenseRedeemedreward.redeemed.v1
RécompenseTemporary wonreward.temporary_won.v1
RécompenseTemporary lostreward.temporary_lost.v1
RécompenseTemporary blockedreward.temporary_blocked.v1
RécompenseExpiredreward.expired.v1
ContactCreatedcontact.created.v1
ContactIdentifiedcontact.identified.v1
ContactAuthenticatedcontact.authenticated.v1
AutresNotification sentnotification.sent.v1
AutresFeedback submittedfeedback.submitted.v1
AutresQuestion answeredquestion.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

LimiteValeur
Délai50 – 2000 ms, 2000 par défaut
Mémoire1 – 64 pages de 64 Kio, 32 pages par défaut (2 Mio)
Appels SDK20
Délai par requête HTTP1500 ms
Corps de réponse HTTP4 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

LimiteValeur
Taille de la source1 Mo
Hôtes autorisés16
Configuration du plugin32 clés, 4 Ko au total
Bundles de secrets associés5
Abonnements actifs par type d'événement5 par tenant
Associations actives par point d'ancrage1 globale, plus 1 par organisation
Compilations simultanées1

Par tenant

LimiteValeur
Bundles de secrets5
Secrets par bundle32
Clé de bundleJusqu'à 32 caractères, lettres minuscules, chiffres et tirets bas, commençant par une lettre

Conservation et troncature

ÉlémentValeur
Enregistrements d'exécution24 heures
Entrée et sortie enregistrées256 Ko de chaque côté, tronquées au-delà, avec indication des tailles d'origine
Logs capturés16 Ko au total, 256 lignes, 2 Ko par ligne
Journal de compilation affiché dans StudioLes 8 derniers Ko

Compilation

LimiteValeur
Vérification des types à l'enregistrement10 secondes
Compilation4 minutes
Une compilation bloquée est libérée, vous pouvez en relancer une15 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

Sur cette page