Introduction

La Partner API de Performance Hub permet aux plateformes de partenaires externes de s'intégrer facilement aux modules principaux de Performance Hub. L'API prend en charge la vision par ordinateur, l'analyse, la journalisation des accès des membres et les informations sur les installations.

Format des requêtes

L'API accepte des corps de requête encodés en JSON et renvoie des réponses encodées en JSON.

Limitation du débit

Pour garantir une utilisation équitable et la stabilité du système, les comptes Partner API démarrent avec les limites par défaut suivantes :

  • 5 requêtes par seconde par endpoint
  • Recommandé : 1000 éléments maximum par requête par lot pour des performances optimales
  • Taille de charge utile de 10MB maximum

Les limites de débit sont ajustables et peuvent être configurées au cas par cas par votre responsable de compte partenaire Performance Hub. Si votre intégration nécessite un débit plus élevé pour un lancement, des migrations, des importations rétroactives de données de reporting ou des installations à fort volume, signalez-le lors de l'intégration ou avant la fenêtre de modification prévue.

URL de base

https://partner-api.performancehub.co/partner/v1/

Authentification

L'accès à la Partner API nécessite une authentification au moyen de clés secrètes API. Générez et gérez vos clés depuis la Partner Console.

Les clés API constituent vos identifiants d'accès aux ressources partenaires. Chaque clé dispose d'un accès restreint à des données d'organisation spécifiques et peut être désactivée instantanément si nécessaire.

Manipulez vos clés API avec précaution : elles donnent un accès complet à votre compte ! N'exposez jamais ces clés dans des dépôts publics, des applications côté client ou tout autre endroit où elles pourraient être compromises.

Authentifiez les requêtes avec l'authentification HTTP Basic en transmettant votre clé API comme nom d'utilisateur. Laissez le mot de passe vide.

L'API n'accepte que les connexions HTTPS sécurisées. Les requêtes HTTP non chiffrées sont rejetées, de même que toute requête dépourvue d'identifiants d'authentification valides.

Exemple de requête

curl https://partner-api.performancehub.co/partner/v1/organisations \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:
# The colon prevents curl from asking for a password.

Vérifier la clé secrète

curl https://partner-api.performancehub.co/partner/v1/auth/ping \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:
# Returns { "message": "You are successfully authorised as <PartnerName>" }

Liste de contrôle de mise en production

Utilisez cette liste de contrôle avant d'activer une intégration partenaire pour des installations en production.

Configuration de la Partner Console

  • Votre profil de partenaire est complet.
  • L'URL de votre logo, votre site web, votre catégorie et votre description sont exacts.
  • Votre méthode de liaison des données est configurée : Direct Link ou Linking Identifier.
  • La liaison des installations a été testée avec au moins une installation réelle.
  • Le bon type de clé est utilisé :
    • Dryrun pour les tests d'écriture non destructifs.
    • Read-only pour une consommation des données en toute sécurité.
    • Live uniquement lorsque vous êtes prêt à modifier des données de production.

Authentification et gestion des clés

  • Les clés secrètes sont stockées uniquement côté serveur.
  • Les clés ne sont pas intégrées dans des applications côté client, des applications mobiles, des dépôts publics ou des tickets d'assistance.
  • La rotation et la révocation des clés ont été testées.
  • Votre équipe sait quelle clé est utilisée par chaque environnement.

Validation des données

  • Les champs obligatoires sont présents pour chaque endpoint que vous appelez.
  • Les identifiants d'installation correspondent à la méthode de liaison configurée.
  • Les ID provenant de votre système sont stables et ne changent pas d'une synchronisation à l'autre.
  • Les importations volumineuses sont divisées en lots respectant les limites documentées.
  • Les éléments de lot en échec peuvent être identifiés et renvoyés.

Test des endpoints

Avant le lancement, testez chaque endpoint dont dépend votre intégration :

  • Les organisations et les installations peuvent être lues.
  • Les membres peuvent être enregistrés et mis à jour.
  • Les formules d'abonnement, les zones d'accès et les heures d'accès se synchronisent correctement.
  • Les enregistrements d'entrée sont acceptés et peuvent être rapprochés de votre système.
  • Les instantanés de rapports utilisent la période de reporting et les indicateurs attendus.
  • Les flux des contrôleurs de porte/portail sont testés sur du matériel réel, le cas échéant.

Préparation des Access Time Windows

Si l'intégration fournit des Access Time Windows :

  • Les exemples absolus et hebdomadaires satisfont à la liste de contrôle de certification des producteurs.
  • Les epochs sont des secondes Unix entières non entourées de guillemets ; les limites exactes [from, to) ont été testées.
  • Les valeurs UTC par défaut, les fuseaux horaires IANA personnalisés de l'intégration et les changements d'heure d'été pertinents ont été vérifiés.
  • Un flux de révisions par membre/source unique, durable et croissant coordonne les mises à jour incrémentielles, le rapprochement et les nouvelles tentatives.
  • Les opérateurs comprennent les comportements en cas d'omission, de remplacement complet, d'idempotence, de conflit, d'obsolescence et de [] explicite.
  • Chaque scope_key, y compris default, dispose d'une correspondance Performance Hub approuvée ; il a été vérifié que les clés inconnues entraînent un refus par défaut.
  • La fréquence d'actualisation attendue de l'intégration, l'ancienneté maximale des sources, le comportement en cas de source obsolète et les alertes sont configurés.
  • Il a été démontré qu'un échec de récupération en amont n'émet pas d'instantané vide ou partiel.
  • La convergence du flux de staging, l'annulation/la révocation et les limites de début et de fin sur les portes physiques respectent les objectifs convenus.
  • Les règles habituelles et les autorisations permanentes ont été vérifiées comme solutions de repli, et les plages ne peuvent pas contourner la désactivation d'un utilisateur, blockAllAccess, la maintenance ou le géorepérage.

Préparation du contrôle d'accès

Pour les intégrations de contrôle d'accès :

  • Votre système prend la décision d'accès avant d'appeler Performance Hub.
  • L'intégration gère device_offline, maintenance_mode_active et unlock_confirmation_timeout.
  • Les opérateurs savent qu'un délai d'expiration signifie que le déverrouillage n'a pas été confirmé.
  • Vos journaux stockent l'event_id et le command_id renvoyés.
  • Vous disposez d'une procédure de repli manuelle pour le personnel de l'installation.

Gestion des erreurs et nouvelles tentatives

  • Les réponses 400, 401, 403, 404, 409 et 422 sont remontées aux opérateurs ou aux équipes d'assistance au lieu d'être réessayées aveuglément.
  • Les réponses 429, 500, 502, 503 et 504 utilisent une logique de nouvelles tentatives limitée avec backoff.
  • Les échecs répétés déclenchent une alerte dans votre système.
  • L'intégration n'abandonne jamais silencieusement les enregistrements en échec.

Observabilité

  • Vos journaux incluent l'endpoint, l'horodatage, l'identifiant d'installation, l'ID de l'enregistrement source, le résultat de la requête et le corps de la réponse.
  • Votre équipe d'assistance peut effectuer des recherches par ID de membre, ID d'installation, ID d'enregistrement d'entrée ou ID d'événement de déverrouillage.
  • Vous disposez d'un moyen de rejouer ou de rapprocher les synchronisations en échec.

Lancement en production

  • Performance Hub a confirmé votre accès en production.
  • Au moins une installation en production a activé votre intégration.
  • Votre clé de production est active.
  • Vos contacts opérationnels et techniques sont à jour.
  • Votre procédure d'assistance est documentée pour les clients.

Une fois ces vérifications terminées, votre intégration configurée apparaîtra dans l'annuaire des partenaires Performance Hub, au sein de la plateforme principale Performance Hub.

Bonnes pratiques

Appliquez ces modèles pour que les intégrations partenaires restent prévisibles, observables et sûres pour les installations.

Identifier les installations de manière cohérente

Choisissez la méthode de liaison configurée pour votre compte partenaire et utilisez-la de manière cohérente :

  • Direct Link - utilisez le facility_id Performance Hub renvoyé par l'endpoint des installations.
  • Linking Identifier - utilisez votre propre identifiant de site, comme un ID d'agence, un code de site ou un numéro de magasin.

N'envoyez pas les deux identifiants dans la même requête. Performance Hub résout les requêtes à l'aide de la méthode de liaison configurée pour votre compte partenaire.

Consultez Méthodes de liaison des partenaires pour obtenir des conseils de configuration.

Écrire par lots avec précaution

La Partner API accepte des corps de requête JSON et est conçue pour des procédures de synchronisation par lots, lorsque cela est documenté. Limitez la taille des lots et faites en sorte qu'ils puissent être relancés.

Recommandations par défaut :

  • Utilisez jusqu'à 1000 éléments par requête par lot pour des performances optimales.
  • Maintenez les charges utiles sous 10MB.
  • Ne dépassez pas 5 requêtes par seconde et par endpoint.

Les limites de débit sont ajustables et peuvent être configurées au cas par cas par votre responsable de compte partenaire Performance Hub. Si vous prévoyez des importations volumineuses, du trafic lié à un lancement ou des importations rétroactives de données de reporting, convenez du débit nécessaire avant la fenêtre de modification.

Si vous devez synchroniser un jeu de données volumineux, divisez-le en blocs déterministes et journalisez l'ID de chaque bloc de votre côté. Il est ainsi plus facile d'analyser les nouvelles tentatives en échec.

Réessayer en toute sécurité

Utilisez un backoff exponentiel pour les échecs temporaires tels que 429, 500, 502, 503 et 504.

Évitez les boucles de nouvelles tentatives rapprochées. Un bon point de départ consiste à :

  1. Réessayer après 1 seconde.
  2. Réessayer après 2 secondes.
  3. Réessayer après 4 secondes.
  4. Arrêter et déclencher une alerte si l'opération échoue toujours.

Pour les échecs de validation tels que 400 ou 422, corrigez la requête avant de réessayer.

Utiliser l'idempotence lorsqu'elle est prise en charge

L'endpoint de déverrouillage des contrôleurs de porte/portail prend en charge une idempotency_key facultative. Réutiliser la même clé pour le même partenaire, la même installation et la même porte renvoie le résultat existant lorsqu'il est disponible.

Utilisez une clé stable pour l'action effectuée, par exemple un ID d'enregistrement d'entrée ou un ID d'événement d'accès provenant de votre système.

Ne supposez pas que tous les endpoints d'écriture prennent en charge l'idempotence, sauf si cela est documenté pour l'endpoint concerné.

Maintenir des données sources propres

Envoyez des valeurs stables et normalisées dans la mesure du possible :

  • Utilisez des ID de membres, de formules, d'installations et d'enregistrements d'entrée cohérents provenant de votre système source.
  • Utilisez des horodatages ISO 8601 pour les dates et les heures.
  • Envoyez les numéros de téléphone au format international lorsqu'ils sont disponibles.
  • Envoyez les devises à l'aide des champs documentés pour chaque endpoint.
  • Évitez de surcharger les champs de texte libre avec des données qui ont leur place dans des champs structurés.

Prévoir des synchronisations incrémentielles et complètes

La plupart des intégrations partenaires ont besoin des deux :

  • Synchronisations incrémentielles pour les modifications quotidiennes.
  • Synchronisations de rapprochement complètes pour corriger les événements manqués, les importations historiques ou les dérives de données.

Votre intégration doit pouvoir renvoyer l'état actuel d'un membre, d'une formule d'abonnement ou d'un instantané de rapport sans dépendre de la réussite d'une requête précédente.

Publier les Access Time Windows en toute sécurité

Traitez la valeur access_time_windows de chaque membre comme un instantané complet, doté de sa propre révision indépendante :

  • Coordonnez un flux unique et strictement croissant de access_time_windows_revision entre les processus incrémentiels et de rapprochement.
  • Relancez une livraison incertaine ou temporairement en échec avec la même révision et la même charge utile normalisée.
  • Omettez access_time_windows lorsque vous ne le mettez pas à jour.
  • N'envoyez [] qu'après qu'une récupération réussie depuis la source de référence a prouvé qu'il ne reste aucune plage. Ne transformez jamais un délai d'expiration en amont, un résultat partiel ou un échec d'analyse en révocation.
  • Gardez external_id et scope_key stables, et certifiez chaque correspondance de portée avant la mise en production.
  • Surveillez l'ancienneté des données sources par rapport à la politique de fraîcheur convenue pour cette intégration.

Consultez le guide de référence pour les producteurs d'Access Time Windows pour les instantanés complets, les fuseaux horaires, l'heure d'été, l'annulation et les vecteurs de test.

Traiter le contrôle d'accès comme critique pour la sécurité

Pour les déverrouillages de contrôleurs de porte/portail :

  • Votre système doit décider si la personne est autorisée à entrer avant d'appeler Performance Hub.
  • Performance Hub ne renvoie 200 qu'après que le contrôleur sur site a confirmé le déverrouillage physique du relais.
  • La plupart des confirmations aboutissent en 3 à 5 secondes.
  • Un délai d'expiration correspond à un échec explicite du déverrouillage, et non à une réussite en arrière-plan.
  • Ne réessayez que conformément à vos propres règles d'accès et de sécurité.

Journalisez toujours l'event_id et le command_id renvoyés par l'endpoint de déverrouillage.

Surveiller votre intégration

Utilisez les journaux de requêtes de la Partner Console pendant le développement et le lancement. En production, tenez également vos propres journaux avec :

  • L'horodatage de la requête.
  • L'endpoint.
  • L'ID de l'enregistrement source.
  • L'identifiant d'installation.
  • Le code de statut HTTP.
  • Le corps de la réponse ou le message d'erreur de Performance Hub.

Ces champs accélèrent considérablement l'assistance et le rapprochement lorsqu'un client demande ce qui s'est passé.

Organisations

Représente l'entité commerciale de premier niveau qui exploite une ou plusieurs installations intégrées à la plateforme Performance Hub.

Points de terminaison

GET /partner/v1/organisations - List all organisations

L'objet Organisation

Attributs

  • org_id string obligatoire

    Identifiant unique de l'organisation.

  • name string obligatoire

    Nom de l'organisation.

L'objet Organisation

{
  "org_id": "00000000-0000-0000-0000-000000000050",
  "name": "Harbourside Group"
}

Lister toutes les organisations

Renvoie la liste des organisations accessibles à votre compte partenaire.

Valeur renvoyée

Renvoie un objet JSON dont la propriété data contient un tableau d'objets Organisation.

Requête

curl https://partner-api.performancehub.co/partner/v1/organisations \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:

Réponse

{
  "data": [
    {
      "org_id": "00000000-0000-0000-0000-000000000050",
      "name": "Harbourside Group"
    },
    {
      "org_id": "6ba7b811-9dad-11d1-80b4-00c04fd430c8",
      "name": "Wellness Studios Network"
    }
  ]
}

Installations

Représente un lieu physique, comme un bureau, un magasin, une salle de spectacle, un campus, une clinique ou une salle de sport, où les membres peuvent accéder à des services.

Points de terminaison

GET /partner/v1/facilities - List all facilities

L'objet installation

Attributs

  • facility_id string obligatoire

    Identifiant unique de l'installation.

  • org_id string obligatoire

    Identifiant unique de l'organisation parente.

  • name string obligatoire

    Nom de l'installation.

  • brand string facultatif

    Nom de la marque utilisée par l'installation.

  • website_url string facultatif

    URL du site web de l'installation.

  • place_data object facultatif

    Données de localisation issues de l'API Google Places contenant le pays, l'adresse formatée, le Google Place ID, la province et la ville.

  • social_links object facultatif

    Liens vers les réseaux sociaux de l'installation.

  • timezone string facultatif

    Identifiant de fuseau horaire IANA (par exemple, "Australia/Sydney", "America/New_York").

L'objet installation

{
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "org_id": "00000000-0000-0000-0000-000000000050",
  "name": "Harbourside Central",
  "brand": "Harbourside",
  "website_url": "https://example.com/central",
  "social_links": {
    "instagram": {
      "handle": "@harbourside.central"
    },
    "facebook": {
      "handle": "HarboursideCentral"
    }
  },
  "place_data": {
    "country": {
      "iso_code": "AU",
      "full_name": "Australia"
    },
    "formatted_address": "123 Main Street, Suite 100, Sydney NSW 2000, Australia",
    "google_place_id": "ChIJN1t_tDeuEmsRUsoyG83frY4",
    "province": {
      "abbreviation": "NSW",
      "full_name": "New South Wales"
    },
    "city": {
      "short_name": "Sydney",
      "full_name": "Sydney"
    }
  },
  "timezone": "Australia/Sydney"
}

Lister toutes les installations

Renvoie la liste des installations accessibles à votre compte partenaire.

Valeur renvoyée

Renvoie un objet JSON dont la propriété data contient un tableau d'objets installation.

Requête

curl https://partner-api.performancehub.co/partner/v1/facilities \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:

Réponse

{
  "data": [
    {
      "facility_id": "550e8400-e29b-41d4-a716-446655440000",
      "org_id": "00000000-0000-0000-0000-000000000050",
      "name": "Harbourside Central",
      "brand": "Harbourside",
      "website_url": "https://example.com/central",
      "place_data": {
        "country": {
          "iso_code": "AU",
          "full_name": "Australia"
        },
        "formatted_address": "123 Main Street, Sydney NSW 2000, Australia",
        "google_place_id": "ChIJN1t_tDeuEmsRUsoyG83frY4",
        "province": {
          "abbreviation": "NSW",
          "full_name": "New South Wales"
        },
        "city": {
          "short_name": "Sydney",
          "full_name": "Sydney"
        }
      },
      "timezone": "Australia/Sydney"
    }
  ]
}

Formules d'abonnement

Définit une offre achetable proposée par une organisation ou une installation, y compris les services, les droits, le calendrier et les tarifs.

Points de terminaison

POST /partner/v1/membership-plans - Save a membership plan

L'objet Membership Plan

Attributs

  • membership_plan_id string obligatoire

    Identifiant unique de la formule d'abonnement.

  • facility_id string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Identifiant unique de l'installation dans Performance Hub.

  • linking_identifier string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Valeur d'identifiant unique que le partenaire utilise pour chaque site (par exemple, « Branch ID », « Location Code », « Store Number »). En savoir plus

  • name string obligatoire

    Nom lisible de la formule (par exemple, "Office tenant – business hours", "Student – term access", "Hotel guest", "10 Class Pack").

  • billing_cycle string facultatif

    Fréquence de facturation. L'une des valeurs weekly, fortnightly, monthly, annually ou one_time.

  • price integer facultatif

    Coût de base dans la plus petite unité monétaire (cents pour USD/AUD).

  • currency string facultatif

    Code de devise ISO 4217 (par exemple, "AUD", "USD").

  • description string facultatif

    Description marketing ou interne facultative.

  • active_members integer facultatif

    Nombre de membres actifs utilisant cette formule d'abonnement

  • is_recurring boolean facultatif

    Indique si la formule est renouvelée automatiquement.

  • is_trial boolean facultatif

    Indique s'il s'agit d'une offre d'essai.

  • visible_to_public boolean facultatif

    Indique si la formule peut être achetée en libre-service.

  • duration_days integer facultatif

    Nombre de jours de validité de la formule d'abonnement.

  • access_zones array facultatif

    Tableau des zones d'accès incluses dans la formule d'abonnement. Chaque zone peut définir ses propres heures d'accès. Consultez Zones d'accès pour plus de détails.

  • class_limit integer facultatif

    Nombre maximal de cours autorisés (null pour un nombre illimité).

L'objet Membership Plan

Cet exemple utilise facility_id. Pour une intégration à identifiant de liaison, envoyez plutôt "linking_identifier": "branch_downtown".

{
  "membership_plan_id": "plan_99999",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Office tenant – business hours",
  "description": "Weekday access to the level 3 tenancy and the end-of-trip facilities",
  "billing_cycle": "monthly",
  "price": 8900,
  "currency": "AUD",
  "duration_days": 30,
  "is_recurring": true,
  "is_trial": false,
  "visible_to_public": false,
  "active_members": 245,
  "access_zones": [
    {
      "name": "Level 3 Tenancy",
      "description": "Lift lobby, offices and meeting rooms on level 3",
      "access_hours": {
        "mon": [["07:00", "19:00"]],
        "tue": [["07:00", "19:00"]],
        "wed": [["07:00", "19:00"]],
        "thu": [["07:00", "19:00"]],
        "fri": [["07:00", "19:00"]]
      }
    },
    {
      "name": "End-of-trip Facilities",
      "description": "Bike storage, lockers and showers",
      "access_hours": {
        "mon": [["06:00", "20:00"]],
        "tue": [["06:00", "20:00"]],
        "wed": [["06:00", "20:00"]],
        "thu": [["06:00", "20:00"]],
        "fri": [["06:00", "20:00"]]
      }
    }
  ],
  "class_limit": null
}

Zones d'accès

Les zones d'accès définissent les espaces précis d'une installation auxquels une formule d'abonnement donne accès. Chaque zone peut avoir ses propres heures d'accès, ce qui permet d'appliquer des restrictions horaires différentes selon l'espace.

Structure

Chaque zone d'accès comprend :

  • name string obligatoire

    Le nom de la zone (par exemple, « Level 3 Tenancy », « Library », « Guest Floors », « Main Gym Floor »).

  • description string facultatif

    Description de ce qui est inclus dans cette zone.

  • access_hours object facultatif

    Heures d'accès propres à cette zone. Consultez Heures d'accès pour le format des plages horaires.

Exemple

Un immeuble de bureaux doté d'une salle de sport pour ses locataires : accès aux locaux loués pendant les heures ouvrables, et à la salle de sport du petit matin jusqu'en fin de soirée.

{
  "access_zones": [
    {
      "name": "Level 3 Tenancy",
      "description": "Lift lobby, offices and meeting rooms on level 3",
      "access_hours": {
        "mon": [["07:00", "19:00"]],
        "tue": [["07:00", "19:00"]],
        "wed": [["07:00", "19:00"]],
        "thu": [["07:00", "19:00"]],
        "fri": [["07:00", "19:00"]]
      }
    },
    {
      "name": "Tenant Gym",
      "description": "Ground-floor gym and change rooms for building tenants",
      "access_hours": {
        "mon": [["05:00", "22:00"]],
        "tue": [["05:00", "22:00"]],
        "wed": [["05:00", "22:00"]],
        "thu": [["05:00", "22:00"]],
        "fri": [["05:00", "22:00"]],
        "sat": [["07:00", "18:00"]],
        "sun": [["07:00", "18:00"]]
      }
    }
  ]
}

Heures d'accès

Les heures d'accès indiquent quand les membres peuvent accéder à une zone donnée pour chaque jour de la semaine. Elles sont définies par zone d'accès, ce qui permet d'appliquer des restrictions horaires différentes aux différents espaces d'une installation.

Structure

{
  "mon": [["start_time", "end_time"]],
  "tue": [["start_time", "end_time"]],
  "wed": [["start_time", "end_time"]],
  "thu": [["start_time", "end_time"]],
  "fri": [["start_time", "end_time"]],
  "sat": [["start_time", "end_time"]],
  "sun": [["start_time", "end_time"]]
}

Chaque jour est un tableau de plages horaires au format HH:MM (format 24 heures). Un tableau vide [] signifie qu'aucun accès n'est possible ce jour-là. Les jours omis sont traités comme un tableau vide.

Exemples de cas d'usage

Locataire de bureaux – heures ouvrables : accès pendant les heures ouvrables, en semaine uniquement.

{
  "access_hours": {
    "mon": [["07:00", "19:00"]],
    "tue": [["07:00", "19:00"]],
    "wed": [["07:00", "19:00"]],
    "thu": [["07:00", "19:00"]],
    "fri": [["07:00", "19:00"]]
  }
}

Étudiant – accès pendant le trimestre : une bibliothèque de campus ouverte en semaine, avec une pause en milieu de journée, ainsi que le samedi matin.

{
  "access_hours": {
    "mon": [["08:00", "12:30"], ["13:30", "21:00"]],
    "tue": [["08:00", "12:30"], ["13:30", "21:00"]],
    "wed": [["08:00", "12:30"], ["13:30", "21:00"]],
    "thu": [["08:00", "12:30"], ["13:30", "21:00"]],
    "fri": [["08:00", "12:30"], ["13:30", "17:00"]],
    "sat": [["09:00", "13:00"]]
  }
}

Salle de sport – lève-tôt : accès le matin uniquement, en semaine.

{
  "access_hours": {
    "mon": [["04:00", "12:00"]],
    "tue": [["04:00", "12:00"]],
    "wed": [["04:00", "12:00"]],
    "thu": [["04:00", "12:00"]],
    "fri": [["04:00", "12:00"]]
  }
}

Client d'hôtel – 24 h/24, 7 j/7 : accès permanent, le cas d'usage le plus courant.

{
  "access_hours": {
    "mon": [["00:00", "23:59"]],
    "tue": [["00:00", "23:59"]],
    "wed": [["00:00", "23:59"]],
    "thu": [["00:00", "23:59"]],
    "fri": [["00:00", "23:59"]],
    "sat": [["00:00", "23:59"]],
    "sun": [["00:00", "23:59"]]
  }
}

Exemple par zone

Une formule pour les clients d'un hôtel, avec un accès 24 heures sur 24 aux étages des chambres et des horaires plus restreints pour la piscine :

{
  "access_zones": [
    {
      "name": "Guest Floors",
      "description": "Lifts and corridors on the guest room levels",
      "access_hours": {
        "mon": [["00:00", "23:59"]],
        "tue": [["00:00", "23:59"]],
        "wed": [["00:00", "23:59"]],
        "thu": [["00:00", "23:59"]],
        "fri": [["00:00", "23:59"]],
        "sat": [["00:00", "23:59"]],
        "sun": [["00:00", "23:59"]]
      }
    },
    {
      "name": "Swimming Pool",
      "description": "Access to the swimming pool and change rooms",
      "access_hours": {
        "mon": [["06:00", "20:00"]],
        "tue": [["06:00", "20:00"]],
        "wed": [["06:00", "20:00"]],
        "thu": [["06:00", "20:00"]],
        "fri": [["06:00", "20:00"]],
        "sat": [["08:00", "18:00"]],
        "sun": [["08:00", "18:00"]]
      }
    }
  ]
}

Enregistrer une formule d'abonnement

Crée ou met à jour des fiches de formules d'abonnement. L'API détermine s'il faut créer ou mettre à jour la fiche en fonction du membership_plan_id.

Paramètres

  • membership_plans array obligatoire

    Tableau d'objets Membership Plan à enregistrer. Maximum recommandé : 1000 éléments par requête pour des performances optimales. L'API regroupe automatiquement les éléments en lots pour leur traitement. Chaque élément doit inclure soit facility_id (obligatoire si vous n'utilisez pas linking_identifier), soit linking_identifier (obligatoire si vous n'utilisez pas facility_id).

Valeur renvoyée

Renvoie un message simple confirmant l'opération de synchronisation.

Requête

Cet exemple utilise facility_id. Pour une intégration à identifiant de liaison, envoyez plutôt "linking_identifier": "branch_downtown".

curl -X POST https://partner-api.performancehub.co/partner/v1/membership-plans \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "membership_plans": [
      {
        "membership_plan_id": "plan_99999",
        "facility_id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Student – term access",
        "description": "Library and student hub access for one teaching term",
        "billing_cycle": "one_time",
        "price": 4500,
        "currency": "AUD",
        "duration_days": 84,
        "is_recurring": false,
        "is_trial": false,
        "visible_to_public": true,
        "active_members": 245,
        "access_zones": [
          {
            "name": "Library",
            "description": "Reading rooms, study spaces and bookable rooms",
            "access_hours": {
              "mon": [["08:00", "21:00"]],
              "tue": [["08:00", "21:00"]],
              "wed": [["08:00", "21:00"]],
              "thu": [["08:00", "21:00"]],
              "fri": [["08:00", "17:00"]],
              "sat": [["09:00", "13:00"]]
            }
          },
          {
            "name": "Student Hub",
            "description": "Common room, kitchen and lockers",
            "access_hours": {
              "mon": [["07:00", "22:00"]],
              "tue": [["07:00", "22:00"]],
              "wed": [["07:00", "22:00"]],
              "thu": [["07:00", "22:00"]],
              "fri": [["07:00", "22:00"]]
            }
          }
        ],
        "class_limit": null
      }
    ]
  }'

Réponse

Renvoie un message de réussite accompagné de métadonnées sur l'opération de synchronisation.

{
  "message": "Membership plan sync successful.",
  "metadata": {
    "success": 98,
    "skipped": 2,
    "skipped_items": [
      {
        "membership_plan_id": "plan_456",
        "facility_id": null,
        "reason": "Missing facility_id"
      }
    ]
  }
}

Champs de métadonnées :

  • success - Nombre de formules d'abonnement traitées avec succès
  • skipped - Nombre de formules ignorées (le cas échéant)
  • skipped_items - Tableau des formules ignorées, avec leurs motifs (le cas échéant)
  • dry-run - Indicateur booléen en cas d'utilisation d'une clé de type dryrun (le cas échéant)

Membres

Représente un client individuel d'une installation. Il peut s'agir d'un lead, d'un prospect, d'un client régulier, d'un membre expiré ou d'un utilisateur bloqué.

Les upserts de membres peuvent également contenir un instantané access_time_windows complet et révisionné, comprenant des accès absolus et hebdomadaires récurrents. Consultez Access Time Windows avant de produire ces champs. Il s'agit d'autorisations supplémentaires qui ne remplacent pas les règles habituelles ni les autorisations permanentes de l'installation.

Points de terminaison

POST /partner/v1/members - Save a member

L'objet Member

Attributs

  • member_id string obligatoire

    Identifiant unique du membre.

  • facility_id string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Identifiant unique de l'installation dans Performance Hub.

  • linking_identifier string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Valeur d'identifiant unique que le partenaire utilise pour chaque site (par exemple, « Branch ID », « Location Code », « Store Number »). En savoir plus

  • first_name string obligatoire

    Prénom du membre.

  • last_name string obligatoire

    Nom de famille du membre.

  • email string facultatif

    Adresse e-mail.

  • mobile_phone string facultatif

    Numéro de téléphone mobile au format E.123 (par exemple, "+1 415 555 2671").

  • address object facultatif

    Objet d'adresse physique doté des propriétés suivantes :

    • address_line_1 (string) - Adresse principale (rue)
    • address_line_2 (string, optional) - Ligne d'adresse secondaire
    • city (string) - Nom de la ville
    • state (string) - Code de l'État/de la province
    • postal_code (string) - Code postal/ZIP
    • country (string) - Code pays ISO 3166-1 alpha-2 (par exemple, « AU », « US »)
    • country_name (string, optional) - Nom complet du pays
  • date_of_birth string facultatif

    Date de naissance au format YYYY-MM-DD.

  • gender string facultatif

    L'une des valeurs male, female, non_binary, prefer_not_to_say ou unknown.

  • type string facultatif

    Type de membre (par exemple, staff, member, prospect).

  • tags array facultatif

    Chaînes ou textes d'aide rapide permettant de classer et d'identifier des membres spécifiques (par exemple, ["vip", "personal-training", "student"]).

  • membership_status string facultatif

    Statut d'adhésion actuel (par exemple, active, expired, on_hold).

  • membership_plans array facultatif

    Tableau des formules d'abonnement associées au membre.

  • profile_picture string facultatif

    Photo de profil encodée en Base64 (facultatif).

  • created string facultatif

    Horodatage ISO 8601 de la création du membre.

  • updated string facultatif

    Horodatage ISO 8601 de la dernière mise à jour du membre.

  • access_time_windows array facultatif

    Instantané complet, appartenant à la source, des Access Time Windows absolues et/ou hebdomadaires récurrentes. Omettez le champ pour conserver l'instantané tel quel ; envoyez un tableau vide avec une révision plus récente pour le révoquer. Consultez Access Time Windows pour les formes, la validation, la correspondance des portées et la sémantique de remplacement.

  • access_time_windows_revision integer obligatoire avec access_time_windows

    Entier non négatif, strictement croissant et compatible JSON, propre au flux de ce membre/cette source. Obligatoire dès que access_time_windows est présent.

L'objet Member

{
  "member_id": "member_11111",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  // "linking_identifier": "branch_downtown",  // If using linking identifier
  "first_name": "John",
  "last_name": "Smith",
  "email": "john.smith@example.com",
  "mobile_phone": "+61 412 345 678",
  "date_of_birth": "1985-06-15",
  "gender": "male",
  "type": "member",
  "tags": ["vip", "personal-training"],
  "profile_picture": "/9j/4AAQSkZJRgABAQAAAQ...",
  "membership_status": "current",
  "created": "2024-01-15T10:30:00Z",
  "updated": "2024-01-20T14:45:00Z",
  "access_time_windows_revision": 1842,
  "access_time_windows": [
    {
      "external_id": "access-4b1782",
      "kind": "absolute",
      "from": 1792053000,
      "to": 1792058400,
      "timezone": "Australia/Perth",
      "scope_key": "after_hours_entry",
      "label": "Temporary site access"
    }
  ],
  "address": {
    "address_line_1": "456 Bourke Street",
    "address_line_2": "Apt 12",
    "city": "Melbourne",
    "state": "VIC",
    "postal_code": "3000",
    "country": "AU",
    "country_name": "Australia"
  },
  "membership_plans": [
    {
      "membership_plan_id": "plan_99999",
      "name": "Unlimited Monthly",
      "start_date": "2024-01-01T00:00:00Z",
      "status": "current",
      "is_current": true,
      "end_date": null
    }
  ]
}

Plages horaires d'accès

Les Access Time Windows permettent à un membre partenaire de bénéficier d'un accès temporaire ou récurrent fourni par un système externe. Cette page est le guide de référence pour les producteurs de access_time_windows et de access_time_windows_revision.

Les Access Time Windows sont des autorisations supplémentaires qui ne peuvent qu'accorder l'accès. Une plage active peut autoriser l'accès, mais une plage inactive, expirée, obsolète ou sans correspondance ne refuse pas un accès déjà autorisé par une règle habituelle ou une autorisation permanente. La désactivation d'un utilisateur, le blocage explicite d'une porte, le mode de maintenance, le géorepérage, les limites de débit et la disponibilité du contrôleur continuent de s'appliquer.

Les implémentations destinées à des systèmes en amont particuliers sont des adaptateurs distincts. Elles doivent traduire leurs données sources dans ce contrat indépendant des fournisseurs sans modifier la signification de ses champs.

Champs de l'instantané de membre

  • access_time_windows array facultatif

    L'instantané complet des Access Time Windows pour ce membre et cette source partenaire. Un instantané peut combiner des plages absolute et weekly. Omettez le champ pour conserver l'instantané stocké tel quel ; envoyez [] pour révoquer toutes les plages appartenant à cette source.

  • access_time_windows_revision integer obligatoire avec access_time_windows

    Un entier non négatif, strictement croissant et compatible JSON, propre au flux de révisions de ce membre/cette source. Il est obligatoire dès que access_time_windows est présent et ne doit pas dépasser 9007199254740991.

Chaque instantané accepte au maximum 50 plages. external_id, scope_key et label acceptent au maximum 120 caractères. Une plage absolue peut couvrir au maximum 90 jours. Un jour de la semaine accepte au maximum huit blocs sans chevauchement. Les données invalides sont rejetées au lieu d'être tronquées ou interprétées.

Formes des plages

Une plage absolue désigne un intervalle exact unique :

Requête mixte complète

Cette requête remplace l'instantané précédent du membre appartenant à la source par une plage absolue et une plage hebdomadaire :

Règles relatives aux heures et aux limites

from, to, active_from et active_until sont des entiers JSON exprimés en secondes Unix, qui désignent des instants UTC :

  • Envoyez un entier JSON sans guillemets. Les chaînes numériques, les nombres à virgule flottante, les chaînes RFC 3339 et les millisecondes Unix sont rejetés.
  • Chaque intervalle est semi-ouvert : [from, to). L'accès peut commencer exactement à from ; il n'est plus actif exactement à to.
  • from doit être inférieur à to. Lorsque les deux bornes hebdomadaires sont présentes, active_from doit être inférieur à active_until.
  • Ne calculez pas d'epochs à partir d'une date/heure locale sans fuseau horaire. Convertissez d'abord l'heure source en instant UTC, puis divisez les millisecondes Unix par 1000 et vérifiez que le résultat est un entier.

Fuseaux horaires et heure d'été

timezone doit être un fuseau horaire IANA valide, comme UTC, Australia/Perth ou America/New_York. Sa valeur par défaut est UTC lorsqu'il est omis.

  • Plages absolues : from et to désignent déjà des instants UTC exacts. timezone n'est qu'une métadonnée d'affichage et d'audit ; la modifier ne décale jamais aucun des deux instants.
  • Plages hebdomadaires : timezone définit l'heure locale et les règles d'heure d'été utilisées pour évaluer access_hours. Ne convertissez pas les blocs hebdomadaires en UTC au préalable.
  • Lors du passage à l'heure d'été, une heure locale sautée par le fuseau horaire ne se produit jamais.
  • Lors du retour à l'heure d'hiver, les deux instants réels correspondant à l'heure locale répétée correspondent au bloc hebdomadaire.
  • active_from et active_until restent des instants UTC, même lorsqu'une plage hebdomadaire utilise un fuseau horaire autre qu'UTC.

access_hours utilise les valeurs en minuscules sun à sat. Chaque valeur est un tableau de paires [start, end] au format HH:mm sur 24 heures, complété par des zéros. Les blocs doivent être non vides, ordonnés et sans chevauchement. end est exclusif. Un bloc ne peut pas franchir minuit ; scindez un intervalle source de nuit en un bloc tardif le premier jour et un bloc matinal le jour suivant.

ID stables et correspondance des portées

external_id doit rester stable pendant toute la durée de vie d'une plage logique et être unique au sein de l'instantané du membre. Conservez-le lorsque les heures ou les libellés changent, afin que les opérateurs et les enregistrements d'audit puissent rapprocher les révisions. Ne le réutilisez pas pour une plage sans rapport.

scope_key est votre cible d'accès logique stable. Sa valeur par défaut est default. Un administrateur de l'installation associe chaque clé autorisée à des cibles Performance Hub : l'ensemble de l'installation, une porte ou une Portée d'accès :

Instantanés complets et révisions

Traitez ces deux champs comme une partition versionnée séparément au sein de l'upsert de membre :

Entrée Résultat
access_time_windows omis L'instantané existant reste inchangé ; aucune révision n'est requise
Présent avec une révision plus récente L'intégralité de l'instantané stocké pour ce membre/cette source est remplacée
Même révision et même charge utile normalisée Relecture idempotente ; aucune modification de l'instantané
Même révision et charge utile normalisée différente Conflit ; attribuez une nouvelle révision après rapprochement
Révision plus ancienne L'instantané obsolète est ignoré ; il ne peut pas écraser un état plus récent
access_time_windows: [] avec une révision plus récente Révoque explicitement toutes les plages appartenant à la source

Un instantané est complet, ce n'est pas un correctif partiel. Si la révision 1842 contient les plages A et B, alors la révision 1843 qui ne contient que B supprime A.

Maintenez un flux de révisions coordonné unique par partenaire/membre pour l'ensemble des processus de webhook, d'interrogation périodique, de relecture et de rapprochement complet. Une séquence de base de données durable est préférable. Une valeur en millisecondes Unix ne peut servir de révision que si tous les processus d'écriture se coordonnent et garantissent un ordre strict et l'unicité ; il s'agit d'une valeur de révision, et non d'un champ d'epoch.

Conservez la requête et la révision exactes jusqu'à ce que la réponse de l'API soit connue. Après un délai d'expiration ou une erreur temporaire, renvoyez le même instantané normalisé avec la même révision. N'attribuez pas une nouvelle révision au seul motif que la livraison est incertaine.

Annulation, expiration et échecs en amont

  • Annuler une plage : récupérez avec succès l'état de référence de la source, supprimez cette plage et envoyez l'instantané complet restant avec une révision plus récente.
  • Annuler toutes les plages : envoyez access_time_windows: [] avec une révision plus récente.
  • Laisser expirer une plage absolue : elle cesse d'autoriser l'accès à to ; envoyez ultérieurement un rapprochement sans l'entrée expirée pour garder des instantanés propres.
  • Mettre fin à une plage hebdomadaire : supprimez-la dans un instantané plus récent, ou utilisez active_until lorsque la source connaît l'instant de fin.
  • La récupération depuis la source échoue ou est incomplète : n'envoyez pas [] et ne publiez pas d'instantané partiel. Déclenchez une alerte et relancez la récupération. Transformer un échec en amont en instantané vide revient à révoquer l'accès involontairement.
  • La requête Partner API échoue à la validation : corrigez les données et n'envoyez une révision plus récente que si l'instantané normalisé change. Relancez les échecs temporaires avec la révision d'origine.

La fraîcheur des sources est configurée pour chaque intégration en fonction de la fréquence de livraison et de rapprochement qu'elle garantit. Avant l'activation, convenez de l'ancienneté maximale des sources, de ce qui se passe lorsqu'elle est dépassée, des seuils d'alerte et de la possibilité pour les opérateurs d'accepter de ne s'appuyer que sur les bornes propres à chaque plage. Une plage provenant d'une source obsolète ne devient jamais une règle de refus ; les règles habituelles et les autorisations permanentes restent des solutions de repli.

Algorithme du producteur

Utilisez cette procédure indépendante du langage pour chaque mise à jour de membre :

Liste de contrôle de certification

Effectuez ces vérifications dans une installation de staging en utilisant la correspondance scope_key définitive :

  • Une plage absolue sans fuseau horaire est acceptée et évaluée en UTC.
  • Une plage absolue avec un fuseau IANA personnalisé conserve les mêmes instants from/to.
  • Une plage hebdomadaire avec un fuseau IANA personnalisé suit l'heure locale lors du passage à l'heure d'été et du retour à l'heure d'hiver.
  • Un instantané mixte absolu/hebdomadaire remplace entièrement l'instantané précédent.
  • L'accès commence exactement à from et s'arrête exactement à to ; les fins de blocs hebdomadaires sont également exclusives.
  • La modification des heures sous le même external_id avec une révision plus récente prend effet.
  • L'omission de access_time_windows modifie les autres champs du membre sans modifier les plages.
  • La relecture de la même révision/charge utile est idempotente.
  • La même révision avec une charge utile différente renvoie un conflit et ne modifie pas l'instantané.
  • Une révision plus ancienne reçue dans le désordre est signalée comme obsolète et ne peut pas rétablir un état antérieur.
  • Un [] plus récent révoque les plages appartenant à la source ; un échec de récupération en amont n'envoie rien.
  • Un scope_key inconnu ou sans correspondance entraîne un refus par défaut et génère un avertissement visible par les opérateurs.
  • Les alertes d'ancienneté des sources et la politique configurée pour les sources obsolètes fonctionnent au seuil convenu.
  • Les chemins de webhook et de rapprochement complet convergent vers le même instantané et le même ordre de révisions.
  • La livraison du flux atteint Door & Gate Access dans le délai convenu.
  • Une plage active peut autoriser l'accès à la porte physique associée, mais ne peut pas contourner la désactivation d'un utilisateur, blockAllAccess, la maintenance ou le géorepérage.
  • Avant, en dehors et après une plage, les règles habituelles et les autorisations permanentes continuent d'autoriser les utilisateurs qui remplissent par ailleurs les conditions.

Ressources associées

{
  "external_id": "access-4b1782",
  "kind": "absolute",
  "from": 1792053000,
  "to": 1792058400,
  "timezone": "Australia/Perth",
  "scope_key": "after_hours_entry",
  "label": "Temporary site access"
}

Les champs obligatoires sont external_id, kind, from et to. timezone, scope_key et label sont facultatifs.

Une plage hebdomadaire désigne des intervalles récurrents en heure locale, éventuellement bornés par des instants UTC :

{
  "external_id": "weekly-early-access",
  "kind": "weekly",
  "timezone": "America/New_York",
  "access_hours": {
    "sun": [],
    "mon": [["06:00", "08:30"]],
    "tue": [["06:00", "08:30"]],
    "wed": [["06:00", "08:30"]],
    "thu": [["06:00", "08:30"]],
    "fri": [["06:00", "08:30"]],
    "sat": []
  },
  "active_from": 1790812800,
  "active_until": 1796083200,
  "scope_key": "main_entry",
  "label": "Weekday early access"
}

Les champs obligatoires sont external_id, kind et access_hours. timezone, active_from, active_until, scope_key et label sont facultatifs.

curl -X POST https://partner-api.performancehub.co/partner/v1/members \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "members": [
      {
        "member_id": "member_11111",
        "facility_id": "550e8400-e29b-41d4-a716-446655440000",
        "first_name": "John",
        "last_name": "Smith",
        "access_time_windows_revision": 1842,
        "access_time_windows": [
          {
            "external_id": "access-4b1782",
            "kind": "absolute",
            "from": 1792053000,
            "to": 1792058400,
            "timezone": "Australia/Perth",
            "scope_key": "after_hours_entry",
            "label": "Temporary site access"
          },
          {
            "external_id": "weekly-early-access",
            "kind": "weekly",
            "timezone": "America/New_York",
            "access_hours": {
              "sun": [],
              "mon": [["06:00", "08:30"]],
              "tue": [["06:00", "08:30"]],
              "wed": [["06:00", "08:30"]],
              "thu": [["06:00", "08:30"]],
              "fri": [["06:00", "08:30"]],
              "sat": []
            },
            "active_from": 1790812800,
            "active_until": 1796083200,
            "scope_key": "main_entry",
            "label": "Weekday early access"
          }
        ]
      }
    ]
  }'

Utilisez facility_id ou linking_identifier selon la méthode de liaison configurée. N'envoyez pas les deux.

Conversion en epoch

2026-10-15T08:30:00Z est converti en 1792053000 :

UTC instant                 Unix milliseconds   Unix seconds sent
2026-10-15T08:30:00Z        1792053000000        1792053000

Conversion indépendante du langage :

instant = parse_source_timestamp_with_explicit_zone(source_timestamp)
milliseconds = instant.to_unix_milliseconds()
assert milliseconds modulo 1000 equals 0
seconds = milliseconds / 1000
assert seconds is a JSON-safe nonnegative integer

Si la source offre une précision inférieure à la seconde, définissez une politique d'arrondi explicite pour le début et la fin avant les tests d'intégration. Ne transmettez jamais de millisecondes en comptant sur l'API pour deviner l'unité.

Partner scope_key           Performance Hub mapping
default                     Facility's configured default target
main_entry                  Access Scope "Main entrances"
after_hours_entry           Doors approved for after-hours entry

Les partenaires n'envoient pas d'ID d'installation, de porte ou de Portée d'accès Performance Hub dans une plage. Un scope_key inconnu, désactivé ou sans correspondance entraîne un refus par défaut pour cette plage ; il n'élargit pas l'accès. Convenez des correspondances et testez-les avant le lancement.

function publish_member(member_key):
    source_result = fetch_authoritative_access_state(member_key)

    if source_result.failed or source_result.incomplete:
        record_failure(member_key)
        schedule_retry(member_key)
        return without sending access_time_windows

    windows = []
    for source_window in source_result.current_windows:
        if source_window.is_absolute:
            windows.append({
                external_id: stable_window_id(source_window),
                kind: "absolute",
                from: to_integer_unix_seconds(source_window.start_instant),
                to: to_integer_unix_seconds(source_window.end_instant),
                timezone: source_window.display_timezone or "UTC",
                scope_key: stable_scope_key(source_window) or "default",
                label: safe_label(source_window)
            })
        else:
            windows.append({
                external_id: stable_window_id(source_window),
                kind: "weekly",
                timezone: valid_iana_zone(source_window.timezone) or "UTC",
                access_hours: split_validate_and_sort_weekly_blocks(source_window),
                active_from: optional_integer_unix_seconds(source_window.active_start),
                active_until: optional_integer_unix_seconds(source_window.active_end),
                scope_key: stable_scope_key(source_window) or "default",
                label: safe_label(source_window)
            })

    normalized_snapshot = validate_sort_and_deduplicate(windows)
    revision = next_durable_member_revision(member_key)
    request = member_upsert_with(normalized_snapshot, revision)
    persist_for_retry(member_key, request)

    response = post_request(request)
    if response.is_transient_or_unknown:
        retry_exact_request_with_same_revision(request)
    else if response.is_revision_conflict:
        reconcile_with_latest_committed_source_state(member_key)
    else if response.is_stale_skip:
        stop_replaying_this_revision_and_reconcile(member_key)

Vecteurs de test minimaux

[
  {
    "name": "absolute-half-open",
    "window": {
      "external_id": "tv-absolute",
      "kind": "absolute",
      "from": 1792053000,
      "to": 1792058400
    },
    "expect": {
      "1792052999": "inactive",
      "1792053000": "active",
      "1792058399": "active",
      "1792058400": "inactive"
    }
  },
  {
    "name": "weekly-dst",
    "window": {
      "external_id": "tv-weekly-dst",
      "kind": "weekly",
      "timezone": "America/New_York",
      "access_hours": {
        "sun": [["01:00", "03:30"]],
        "mon": [],
        "tue": [],
        "wed": [],
        "thu": [],
        "fri": [],
        "sat": []
      }
    },
    "expect": "Evaluate local wall time: skipped times do not occur; both repeated 01:30 instants qualify."
  }
]

Soumettez également des vecteurs négatifs pour les millisecondes Unix, les chaînes numériques, les nombres à virgule flottante, les fuseaux IANA invalides, from >= to, les external_id en double, les blocs qui se chevauchent, les blocs de nuit, plus de huit blocs dans une journée, plus de 50 plages et les clés de portée sans correspondance.

Enregistrer un membre

Crée ou met à jour des fiches de membres. L'API détermine s'il faut créer ou mettre à jour la fiche en fonction du member_id.

Paramètres

  • members array obligatoire

    Tableau d'objets Member à enregistrer. Maximum recommandé : 1000 éléments par requête pour des performances optimales. L'API regroupe automatiquement les éléments en lots pour leur traitement. Chaque élément doit inclure soit facility_id (obligatoire si vous n'utilisez pas linking_identifier), soit linking_identifier (obligatoire si vous n'utilisez pas facility_id).

    Lorsqu'un membre inclut access_time_windows, il doit également inclure access_time_windows_revision. Le tableau est un instantané complet de la source, et non un correctif partiel. Omettez-le pour conserver l'instantané existant, ou envoyez [] avec une révision plus récente pour révoquer toutes les plages appartenant à la source. Lisez le guide des Access Time Windows avant d'activer ce champ.

Valeur renvoyée

Renvoie un message simple confirmant l'opération de synchronisation.

Les champs du profil du membre restent des upserts normaux, même lorsqu'un instantané de plages est signalé comme une relecture idempotente ou ignoré car obsolète. Un instantané en conflit (même révision, charge utile différente) ne peut pas être renvoyé tel quel ; rapprochez l'état de la source et envoyez une révision plus récente.

Requête

curl -X POST https://partner-api.performancehub.co/partner/v1/members \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "members": [
      {
        "member_id": "member_11111",
        "facility_id": "550e8400-e29b-41d4-a716-446655440000",
        // "linking_identifier": "branch_downtown",  // If using linking identifier
        "first_name": "John",
        "last_name": "Smith",
        "email": "john.smith@example.com",
        "mobile_phone": "+61 412 345 678",
        "date_of_birth": "1985-06-15",
        "gender": "male",
        "type": "member",
        "tags": ["vip", "personal-training"],
        "profile_picture": "/9j/4AAQSkZJRgABAQAAAQ...",
        "membership_status": "current",
        "created": "2024-01-15T10:30:00Z",
        "updated": "2024-01-20T14:45:00Z",
        "access_time_windows_revision": 1842,
        "access_time_windows": [
          {
            "external_id": "access-4b1782",
            "kind": "absolute",
            "from": 1792053000,
            "to": 1792058400,
            "timezone": "Australia/Perth",
            "scope_key": "after_hours_entry",
            "label": "Temporary site access"
          }
        ],
        "address": {
          "address_line_1": "456 Bourke Street",
          "address_line_2": "Apt 12",
          "city": "Melbourne",
          "state": "VIC",
          "postal_code": "3000",
          "country": "AU",
          "country_name": "Australia"
        },
        "membership_plans": [
          {
            "membership_plan_id": "plan_99999",
            "name": "Unlimited Monthly",
            "start_date": "2024-01-01T00:00:00Z",
            "status": "current",
            "is_current": true,
            "end_date": null
          }
        ]
      }
    ]
  }'

Réponse

Renvoie un message de réussite accompagné de métadonnées sur l'opération de synchronisation.

{
  "message": "Member sync successful.",
  "metadata": {
    "success": 245,
    "skipped": 5,
    "skipped_items": [
      {
        "member_id": "member_123",
        "facility_id": null,
        "reason": "Facility integration is disabled in facility-partner-config"
      }
    ]
  }
}

Champs de métadonnées :

  • success - Nombre de membres traités avec succès
  • skipped - Nombre de membres ignorés (le cas échéant)
  • skipped_items - Tableau des membres ignorés, avec leurs motifs (le cas échéant)
  • dry-run - Indicateur booléen en cas d'utilisation d'une clé de type dryrun (le cas échéant)

Enregistrements d'entrée

Représente l'enregistrement de la présence d'une personne dans une installation, à un cours ou à une activité. Utilisé pour le suivi de la fréquentation, l'analyse de l'utilisation et l'amélioration de la précision de la détection visuelle.

Points de terminaison

POST /partner/v1/check-ins - Save check-ins

L'objet Check-In

Attributs

  • check_in_id string facultatif

    Identifiant unique de l'enregistrement d'entrée. Facultatif : généré automatiquement à partir de la paire member_id et timestamp s'il n'est pas fourni.

  • member_id string obligatoire

    Identifiant unique du membre.

  • facility_id string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Identifiant unique de l'installation dans Performance Hub.

  • linking_identifier string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Valeur d'identifiant unique que le partenaire utilise pour chaque site (par exemple, « Branch ID », « Location Code », « Store Number »). En savoir plus

  • check_in_type string facultatif

    Type de visite. L'une des valeurs member, class, staff, guest ou trial.

  • source string facultatif

    Mode de saisie de l'enregistrement d'entrée. Les valeurs courantes sont manual, tag, rfid, kiosk, app. D'autres valeurs comme door_reader ou booking peuvent également être utilisées.

  • class_id string facultatif

    Facultatif. À renseigner si l'enregistrement est lié à une réservation ou à une participation à un cours.

  • timestamp string obligatoire

    Horodatage au format ISO 8601 du moment où l'enregistrement d'entrée a eu lieu.

L'objet Check-In

{
  // "check_in_id": "checkin_abc123",  // Optional - auto-generated from member_id + timestamp if not provided
  "member_id": "mem_12345",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  // "linking_identifier": "branch_downtown",  // If using linking identifier
  "check_in_type": "member",
  "source": "rfid",
  "timestamp": "2025-01-20T08:45:30Z"
}

Enregistrer des entrées

Crée des enregistrements d'entrée. L'API dédoublonne automatiquement les enregistrements en fonction du membre, de l'installation et de l'horodatage.

Paramètres

  • check_ins array obligatoire

    Tableau d'objets Check-In à enregistrer. Maximum recommandé : 1000 éléments par requête pour des performances optimales. L'API regroupe automatiquement les éléments en lots pour leur traitement. Chaque élément doit inclure soit facility_id (obligatoire si vous n'utilisez pas linking_identifier), soit linking_identifier (obligatoire si vous n'utilisez pas facility_id).

Valeur renvoyée

Renvoie un message simple confirmant l'opération de synchronisation.

Requête

curl -X POST https://partner-api.performancehub.co/partner/v1/check-ins \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "check_ins": [
      {
        // "check_in_id": "checkin_abc123",  // Optional - auto-generated from member_id + timestamp if not provided
        "member_id": "mem_12345",
        "facility_id": "550e8400-e29b-41d4-a716-446655440000",
        // "linking_identifier": "branch_downtown",  // If using linking identifier
        "check_in_type": "member",
        "source": "rfid",
        "timestamp": "2025-01-20T08:45:30Z"
      }
    ]
  }'

Réponse

Renvoie un message de réussite accompagné de métadonnées sur l'opération de synchronisation.

{
  "message": "Check-in sync successful.",
  "metadata": {
    "success": 523,
    "skipped": 3,
    "skipped_items": [
      {
        "member_id": "member_789",
        "facility_id": null,
        "reason": "No matching facility_id for the linking_identifier"
      }
    ]
  }
}

Champs de métadonnées :

  • success - Nombre d'enregistrements d'entrée traités avec succès
  • skipped - Nombre d'enregistrements d'entrée ignorés (le cas échéant)
  • skipped_items - Tableau des enregistrements d'entrée ignorés, avec leurs motifs (le cas échéant)
  • dry-run - Indicateur booléen en cas d'utilisation d'une clé de type dryrun (le cas échéant)

Contrôle d'accès

La Partner API peut déclencher le matériel de contrôleur de porte/portail Performance Hub pour les installations où votre intégration est activée.

Utilisez cette API lorsque votre système de gestion des membres a déjà décidé qu'une personne est autorisée à entrer. Performance Hub n'évalue pas le statut d'adhésion, les heures d'accès, les règles de zones de portes, les contrôles antifraude ni la logique de suspension. Votre système reste l'autorité pour ces décisions.

La requête de déverrouillage est synchrone. Performance Hub envoie la commande au contrôleur sur site en temps réel et ne renvoie 200 qu'après que l'appareil a confirmé le déverrouillage physique du relais. La plupart des confirmations devraient aboutir en 3 à 5 secondes. Si aucune confirmation n'est reçue dans le délai imparti, l'API renvoie un échec explicite.

Vous pouvez également enregistrer une tentative d'accès ou un refus qui n'a jamais atteint le contrôleur. Cet appel ne déverrouille pas la porte. Il écrit un événement dans les Journaux d'accès de Door & Gate Access.

Points de terminaison

GET /partner/v1/access-control/doors - List available Door/Gate Controllers
GET /partner/v1/access-control/doors/{door_id} - Get Door/Gate Controller status
POST /partner/v1/access-control/doors/{door_id}/unlock - Unlock a Door/Gate Controller
POST /partner/v1/access-control/doors/{door_id}/access-events - Record an access attempt or denial

L'objet contrôleur de porte/portail

Attributs

  • door_id string obligatoire

    Identifiant unique du contrôleur de porte/portail. Stockez-le dans votre système après la découverte et utilisez-le lorsque vous appelez l'endpoint de déverrouillage.

  • facility_id string obligatoire

    ID d'installation Performance Hub.

  • linking_identifier string facultatif

    Identifiant d'installation du partenaire lorsque votre intégration utilise des identifiants de liaison. En savoir plus

  • name string obligatoire

    Nom lisible de l'appareil configuré dans Performance Hub.

  • zone string facultatif

    Zone ou emplacement de la porte, par exemple Entrance.

  • status string obligatoire

    online ou offline.

  • relay_state string facultatif

    Dernier état connu du relais, par exemple locked ou unlocked.

  • maintenance_mode string obligatoire

    Mode de maintenance actuel. L'une des valeurs off, force-open ou force-closed.

  • hold_time_seconds integer obligatoire

    Durée de maintien du déverrouillage configurée par défaut pour la porte.

  • last_seen string facultatif

    Horodatage ISO de la dernière mise à jour des métadonnées de l'appareil.

L'objet contrôleur de porte/portail

{
  "door_id": "00000000000000000000000000000007",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "linking_identifier": "branch_downtown",
  "name": "Front Door",
  "zone": "Entrance",
  "status": "online",
  "online": true,
  "relay_state": "locked",
  "maintenance_mode": "off",
  "hold_time_seconds": 10,
  "last_seen": "2026-05-01T04:30:00.000Z"
}

Lister les contrôleurs de porte/portail

Renvoie les contrôleurs de porte/portail disponibles pour votre intégration.

Si ni facility_id ni linking_identifier n'est fourni, l'API renvoie les contrôleurs de porte/portail de toutes les installations liées à votre compte partenaire.

Recommended: Include a facility identifier whenever possible. Requests without `facility_id` or `linking_identifier` may take longer to load for partners with hundreds or thousands of Door/Gate Controllers.

Paramètres

  • facility_id string facultatif l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    ID d'installation Performance Hub. À utiliser pour les intégrations à liaison directe.

  • linking_identifier string facultatif l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Votre propre identifiant d'installation/de site. À utiliser lorsque votre intégration est configurée pour les identifiants de liaison.

Valeur renvoyée

Renvoie un tableau d'objets contrôleur de porte/portail.

Requête

curl -X GET "https://partner-api.performancehub.co/partner/v1/access-control/doors?facility_id=550e8400-e29b-41d4-a716-446655440000" \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:

Réponse

{
  "data": [
    {
      "door_id": "00000000000000000000000000000007",
      "facility_id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Front Door",
      "zone": "Entrance",
      "status": "online",
      "relay_state": "locked",
      "maintenance_mode": "off",
      "hold_time_seconds": 10,
      "last_seen": "2026-05-01T04:30:00.000Z"
    }
  ]
}

Obtenir l'état d'un contrôleur de porte/portail

Récupère le dernier état connu et la configuration de base d'un contrôleur de porte/portail.

Fournissez un identifiant d'installation afin que Performance Hub puisse vérifier que la porte appartient à une installation à laquelle votre intégration a accès.

Paramètres

  • door_id string obligatoire

    ID du contrôleur de porte/portail renvoyé par l'endpoint de liste.

  • facility_id string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    ID d'installation Performance Hub. Obligatoire pour les intégrations à liaison directe.

  • linking_identifier string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Identifiant d'installation du partenaire. Obligatoire pour les intégrations à identifiant de liaison.

Valeur renvoyée

Renvoie un objet contrôleur de porte/portail.

Requête

curl -X GET "https://partner-api.performancehub.co/partner/v1/access-control/doors/00000000000000000000000000000007?facility_id=550e8400-e29b-41d4-a716-446655440000" \
  -u sk_live_YOUR_PARTNER_SECRET_KEY:

Réponse

{
  "data": {
    "door_id": "00000000000000000000000000000007",
    "facility_id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Front Door",
    "zone": "Entrance",
    "status": "online",
    "relay_state": "locked",
    "maintenance_mode": "off",
    "hold_time_seconds": 10,
    "last_seen": "2026-05-01T04:30:00.000Z"
  }
}

Déverrouiller un contrôleur de porte/portail

Envoie une commande de déverrouillage au contrôleur de porte/portail et attend que le relais physique confirme le déverrouillage.

Votre système doit décider si la personne est autorisée avant d'appeler cet endpoint. Performance Hub vérifie uniquement la clé partenaire, l'accès à l'installation, l'appartenance de la porte et l'état de l'appareil.

Paramètres

  • door_id string obligatoire

    ID du contrôleur de porte/portail renvoyé par l'endpoint de liste.

  • facility_id string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    ID d'installation Performance Hub. Obligatoire pour les intégrations à liaison directe.

  • linking_identifier string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Identifiant d'installation du partenaire. Obligatoire pour les intégrations à identifiant de liaison.

  • member_id string obligatoire

    ID du membre chez le partenaire. Utilisé pour la journalisation d'audit et la liaison ultérieure des données.

  • first_name string obligatoire

    Prénom de la personne qui ouvre la porte.

  • last_name string obligatoire

    Nom de famille de la personne qui ouvre la porte.

  • full_name string facultatif

    Facultatif. S'il est omis, Performance Hub combine first_name et last_name.

  • check_in_id string facultatif

    ID d'enregistrement d'entrée facultatif provenant de votre système, pour la corrélation.

  • idempotency_key string facultatif

    Clé facultative de protection contre la relecture. Réutiliser la même clé pour le même partenaire, la même installation et la même porte renvoie le résultat existant lorsqu'il est disponible.

Valeur renvoyée

Renvoie 200 uniquement lorsque l'appareil confirme le déverrouillage physique du relais. La porte utilise la durée de maintien configurée dans Gestion d'appareils de Performance Hub ; les partenaires ne peuvent pas modifier la temporisation du relais pour chaque requête. La plupart des confirmations devraient aboutir en 3 à 5 secondes. Considérez le délai d'expiration documenté comme un échec explicite du déverrouillage et ne réessayez que conformément à vos propres règles de sécurité.

Échecs courants :

  • 403 integration_not_enabled - votre intégration n'est pas activée pour l'installation.
  • 409 maintenance_mode_active - le personnel de l'installation a activé le mode de maintenance pour la porte.
  • 503 device_offline - le contrôleur de porte/portail est hors ligne ou ses données sont obsolètes.
  • 504 unlock_confirmation_timeout - la commande a été envoyée, mais aucune confirmation correspondante n'a été reçue avant l'expiration du délai.

Requête

curl -X POST https://partner-api.performancehub.co/partner/v1/access-control/doors/00000000000000000000000000000007/unlock \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "facility_id": "550e8400-e29b-41d4-a716-446655440000",
    "member_id": "mem_12345",
    "first_name": "Jane",
    "last_name": "S.",
    "full_name": "Jane S.",
    "source": "door_reader",
    "idempotency_key": "unlock_20260501_0001"
  }'

Réponse

{
  "status": "unlocked",
  "door_id": "00000000000000000000000000000007",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_id": "00000000-0000-0000-0000-000000000045",
  "command_id": "00000000-0000-0000-0000-000000000049",
  "hold_time_seconds": 10,
  "confirmed_at": "2026-05-01T04:30:00.000Z"
}

Réponse en cas d'échec

{
  "status": "failed",
  "reason": "device_offline",
  "door_id": "00000000000000000000000000000007",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_id": "00000000-0000-0000-0000-000000000045"
}

Enregistrer une tentative d'accès ou un refus

Enregistre une tentative d'accès ou un refus déjà décidé par votre système. Le contrôleur de porte/portail n'est pas contacté et la porte ne se déverrouille pas.

Utilisez cet endpoint lorsque votre système refuse l'entrée, ou lorsqu'une tentative doit apparaître dans les Journaux d'accès de Door & Gate Access sans déverrouillage. Performance Hub n'évalue pas le statut d'adhésion, les heures d'accès ni les autres règles d'accès.

Un événement refusé apparaît dans les Journaux d'accès de Door & Gate Access avec le résultat Denied, le nom de votre entreprise partenaire, la porte, l'identifiant du membre lorsque vous en envoyez un, et le code de motif sous une forme lisible. Par exemple, membership_expired s'affiche sous la forme « Membership Expired ». reason_display_text et user_email sont stockés avec l'événement.

Paramètres

  • door_id string obligatoire

    ID du contrôleur de porte/portail renvoyé par l'endpoint de liste.

  • facility_id string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    ID d'installation Performance Hub. Obligatoire pour les intégrations à liaison directe.

  • linking_identifier string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Identifiant d'installation du partenaire. Obligatoire pour les intégrations à identifiant de liaison.

  • outcome string obligatoire

    denied ou attempted.

  • reason string facultatif

    Code de motif en snake_case minuscule, 64 caractères maximum. Obligatoire lorsque outcome vaut denied. Exemple : membership_expired.

  • reason_display_text string facultatif

    Motif lisible, 256 caractères maximum. Exemple : Membership expired on 2026-04-01.

  • member_id string facultatif

    ID du membre chez le partenaire. Omettez-le lorsque l'identifiant d'accès ne correspond à aucun membre, par exemple une carte inconnue.

  • user_email string facultatif

    Adresse e-mail de la personne qui a tenté d'accéder.

  • timestamp string facultatif

    Moment de la tentative, au format ISO 8601 avec fuseau horaire. Exemple : 2026-05-18T10:15:30Z. Par défaut, l'heure de réception de la requête. Ne doit pas être située plus de 5 minutes dans le futur ni dater de plus de 30 jours.

  • idempotency_key string facultatif

    Clé de protection contre la relecture, 128 caractères maximum. Réutiliser la même clé pour le même partenaire, la même installation et la même porte renvoie le résultat d'origine.

Valeur renvoyée

Renvoie 201 lorsque l'événement est enregistré. Rejouer la même idempotency_key renvoie 200 avec idempotent défini sur true et ne crée pas de nouvel événement.

Une clé dry-run renvoie 200 avec dry_run défini sur true. Rien n'est enregistré.

Échecs courants :

  • 400 - le corps est invalide, un refus n'a pas de reason, l'horodatage est hors plage, ou facility_id / linking_identifier ne correspond à aucune installation liée.
  • 403 - la clé est en lecture seule.
  • 403 integration_not_enabled - votre intégration n'est pas activée pour l'installation. message en indique la raison.
  • 404 - le contrôleur de porte/portail est introuvable pour l'installation liée.

Requête

curl -X POST https://partner-api.performancehub.co/partner/v1/access-control/doors/00000000000000000000000000000007/access-events \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "facility_id": "550e8400-e29b-41d4-a716-446655440000",
    "outcome": "denied",
    "reason": "membership_expired",
    "reason_display_text": "Membership expired on 2026-04-01",
    "member_id": "mem_12345",
    "user_email": "member@example.com",
    "timestamp": "2026-05-18T10:15:30Z",
    "idempotency_key": "evt_abc12345"
  }'

Réponse

{
  "status": "recorded",
  "event_id": "00000000-0000-0000-0000-000000000045",
  "outcome": "denied",
  "door_id": "00000000000000000000000000000007",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-05-18T10:15:30.000Z"
}

Relecture idempotente

{
  "status": "recorded",
  "event_id": "00000000-0000-0000-0000-000000000045",
  "outcome": "denied",
  "door_id": "00000000000000000000000000000007",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2026-05-18T10:15:30.000Z",
  "idempotent": true
}

Rapports d'installation

Un enregistrement ponctuel qui capture les principaux indicateurs opérationnels et financiers d'une installation pour une période donnée.

Points de terminaison

POST /partner/v1/report-snapshots - Save report snapshots

L'objet Report Snapshot

Attributs

  • facility_id string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Identifiant unique de l'installation dans Performance Hub.

  • linking_identifier string l'un ou l'autreIndiquez le type d'identifiant d'installation configuré pour votre intégration : utilisez facility_id pour les intégrations à liaison directe, ou linking_identifier pour les intégrations à identifiant de liaison. N'envoyez pas les deux ; l'API utilise le type d'identifiant configuré pour votre compte partenaire et ignore l'autre. En savoir plus

    Valeur d'identifiant unique que le partenaire utilise pour chaque site (par exemple, « Branch ID », « Location Code », « Store Number »). En savoir plus

  • start_date string obligatoire

    Date de début de la période de l'instantané au format YYYY-MM-DD.

  • end_date string obligatoire

    Date de fin de la période de l'instantané au format YYYY-MM-DD.

  • metrics object obligatoire

    Objet d'indicateurs contenant les données sur les membres et le chiffre d'affaires pour la période.

L'objet Report Snapshot

{
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  // "linking_identifier": "branch_downtown",  // If using linking identifier
  "start_date": "2025-01-01",
  "end_date": "2025-01-31",
  "metrics": {
    "members": {
      "active": 1200,
      "new": 45,
      "cancelled": 12
    },
    "revenue": {
      "total_payments": 289000.00,
      "total_payments_count": 1200,
      "currency": "AUD"
    }
  }
}

Enregistrer des instantanés de rapports

Soumet les indicateurs opérationnels et financiers d'une installation pour une période donnée.

Paramètres

  • snapshots array obligatoire

    Tableau d'objets Report Snapshot à enregistrer. 25 éléments maximum par requête. Chaque élément doit inclure soit facility_id (obligatoire si vous n'utilisez pas linking_identifier), soit linking_identifier (obligatoire si vous n'utilisez pas facility_id).

Valeur renvoyée

Renvoie un message simple confirmant l'opération de synchronisation.

Requête

curl -X POST https://partner-api.performancehub.co/partner/v1/report-snapshots \
  -u sk_live_YOUR_PARTNER_SECRET_KEY: \
  -H "Content-Type: application/json" \
  -d '{
    "snapshots": [
      {
        "facility_id": "550e8400-e29b-41d4-a716-446655440000",
        // "linking_identifier": "branch_downtown",  // If using linking identifier
        "start_date": "2025-01-01",
        "end_date": "2025-01-31",
        "metrics": {
          "members": {
            "active": 1200,
            "new": 45,
            "cancelled": 12
          },
          "revenue": {
            "total_payments": 289000.00,
            "currency": "AUD"
          }
        }
      }
    ]
  }'

Réponse

{
  "message": "Report snapshot sync successful."
}

Erreurs

La Partner API utilise des codes de statut HTTP standard et des corps de réponse JSON pour expliquer pourquoi une requête a échoué.

Structure des réponses d'erreur

La plupart des erreurs de l'API renvoient un corps JSON contenant un message et, le cas échéant, des détails structurés.

Codes de statut

Échecs liés aux Access Time Windows

La validation des plages échoue avant l'écriture de l'instantané invalide. Les causes courantes qui ne justifient pas une nouvelle tentative sont notamment :

  • L'absence de access_time_windows_revision lorsque access_time_windows est présent.
  • Des valeurs d'epoch sous forme de chaîne, de nombre à virgule flottante ou probablement exprimées en millisecondes.
  • Un fuseau horaire IANA invalide, un intervalle inversé, un external_id en double, des blocs hebdomadaires qui se chevauchent ou un bloc de nuit qui n'a pas été scindé.
  • Une valeur qui dépasse les limites documentées de nombre, de longueur ou d'étendue.

Une révision répétée avec un contenu normalisé différent renvoie une erreur 409 Conflict. Effectuez un rapprochement avec le dernier état de référence et attribuez une révision plus récente ; ne continuez pas à réessayer la paire en conflit. Une révision plus ancienne est signalée comme un instantané obsolète ignoré au lieu d'être autorisée à écraser un état plus récent. Un scope_key inconnu ou sans correspondance entraîne un refus d'autorisation par défaut et doit être traité comme une alerte de configuration.

Si la livraison expire ou reçoit une erreur 5xx temporaire, renvoyez exactement le même instantané normalisé avec la même révision. Si la récupération depuis la source en amont a échoué, omettez entièrement le champ des plages ; ne le remplacez jamais par un tableau vide.

Recommandations pour les nouvelles tentatives

Réessayez les codes suivants avec un backoff exponentiel limité :

  • 429
  • 500
  • 502
  • 503
  • 504

Ne réessayez pas aveuglément les codes suivants :

  • 400
  • 401
  • 403
  • 404
  • 409
  • 422

Pour le contrôle d'accès, considérez toujours les réponses d'expiration de délai et d'appareil hors ligne comme des déverrouillages en échec, sauf si vos propres règles d'accès et processus de sécurité en disposent autrement.

Informations à fournir à l'assistance

Lorsque vous contactez l'assistance, indiquez :

  • L'endpoint et la méthode HTTP.
  • L'horodatage et le fuseau horaire.
  • L'ID de la clé partenaire, et non la clé secrète.
  • L'identifiant d'installation utilisé.
  • L'ID de l'enregistrement source dans votre système.
  • Le statut et le corps de la réponse.
  • L'event_id ou le command_id, le cas échéant.
{
  "success": false,
  "message": "Request body.members[0].member_id is required"
}

Certains endpoints propres à un domaine renvoient un statut et un motif explicites. Par exemple, les échecs de contrôle d'accès incluent des champs tels que reason, door_id, facility_id et event_id.

{
  "status": "failed",
  "reason": "device_offline",
  "door_id": "00000000000000000000000000000007",
  "facility_id": "550e8400-e29b-41d4-a716-446655440000",
  "event_id": "00000000-0000-0000-0000-000000000045"
}

400 Bad Request

La requête est mal formée ou des champs obligatoires sont manquants. Corrigez la requête avant de réessayer.

401 Unauthorized

L'authentification a échoué. Vérifiez que vous utilisez l'authentification HTTP Basic avec votre clé secrète API comme nom d'utilisateur et un mot de passe vide.

403 Forbidden

La clé est valide, mais l'opération n'est pas autorisée. Les causes courantes sont l'utilisation d'une clé en lecture seule pour une opération d'écriture ou une tentative d'accès à une installation pour laquelle votre intégration n'est pas activée.

404 Not Found

La ressource demandée est introuvable ou n'est pas disponible pour votre intégration.

409 Conflict

La requête est en conflit avec l'état actuel de la ressource. Pour le contrôle d'accès, cela peut signifier qu'un contrôleur de porte/portail est en mode de maintenance.

422 Unprocessable Entity

La requête est un JSON valide, mais elle échoue à la validation sémantique. Corrigez les données avant de réessayer.

429 Too Many Requests

L'intégration a dépassé la limite de débit documentée. Réessayez avec un backoff exponentiel.

500 Internal Server Error

Performance Hub n'a pas pu traiter la requête en raison d'une erreur interne. Réessayez avec un backoff. Si le problème persiste, contactez l'assistance en indiquant l'endpoint, l'horodatage et le corps de la réponse.

503 Service Unavailable

Un service ou un appareil dont dépend la requête est indisponible. Pour les déverrouillages de contrôleurs de porte/portail, cela peut signifier que le contrôleur est hors ligne.

504 Gateway Timeout

Le délai de l'opération a expiré. Pour les déverrouillages de contrôleurs de porte/portail, cela signifie que le déverrouillage n'a pas été confirmé avant l'expiration du délai.

Gestion des versions

La version de la Partner API est indiquée dans l'URL. La version actuelle est :

Politique de modification

Performance Hub s'efforce, dans la mesure du possible, d'apporter des modifications additives et rétrocompatibles.

Exemples de modifications rétrocompatibles :

  • L'ajout d'un nouveau champ facultatif à une requête.
  • L'ajout d'un nouveau champ à une réponse.
  • L'ajout d'un nouvel endpoint.
  • L'ajout d'une nouvelle valeur d'énumération lorsque les clients sont censés gérer les valeurs inconnues.
  • L'amélioration des messages de validation sans modifier le contrat des codes de statut.

access_time_windows et access_time_windows_revision sont des champs additifs des requêtes Member. Les clients existants qui les omettent conservent tel quel l'instantané des Access Time Windows stocké. Cette règle d'omission préserve la compatibilité des upserts partiels ; l'omission n'équivaut pas à l'envoi d'un tableau vide.

Exemples de modifications non rétrocompatibles :

  • La suppression ou le renommage d'un champ.
  • La modification du type d'un champ.
  • Le passage d'un champ de requête facultatif à obligatoire.
  • La suppression d'un endpoint.
  • La modification de la signification d'un code de statut existant ou d'un reason propre à un domaine.

Obsolescences

Lorsqu'une modification non rétrocompatible est nécessaire, Performance Hub évite, dans la mesure du possible, de modifier la version existante en place. Nous introduisons plutôt une nouvelle version ou un chemin de migration et communiquons la modification aux partenaires concernés.

Les avis d'obsolescence peuvent prendre la forme de :

  • Une entrée dans le journal des modifications.
  • Une communication directe aux partenaires.
  • Des notes dans la Partner Console.
  • Des conseils de migration dans la documentation.

Recommandations pour les clients

Concevez des clients qui tolèrent les modifications additives :

  • Ignorez les champs de réponse que vous n'utilisez pas.
  • Traitez les valeurs d'énumération inconnues comme non prises en charge au lieu de provoquer un plantage.
  • Évitez les contrôles d'égalité stricts sur les réponses.
  • Journalisez les valeurs non reconnues afin de pouvoir mettre à jour votre intégration de façon délibérée.

Pour les partitions Member révisionnées telles que les Access Time Windows, conservez la révision soumise et la charge utile normalisée jusqu'à ce que le résultat soit connu. Réutiliser une révision pour un contenu modifié constitue un conflit, tandis que rejouer le même contenu est idempotent. Consultez Access Time Windows.

Spécifications OpenAPI

Utilisez les spécifications OpenAPI publiées pour générer des clients, valider les requêtes et suivre l'évolution de la structure de l'API au fil du temps.

Consultez Spécifications OpenAPI.

https://partner-api.performancehub.co/partner/v1/

Journal des modifications

Ce journal des modifications recense les changements notables de la Partner API et de la Partner Console qui affectent le comportement des intégrations.

2026-09-09

2026-05-06

2026-05-01

Catégories de modifications

Les prochaines entrées utiliseront ces catégories :

  • Ajouts - nouvel endpoint, champ, guide ou fonctionnalité.
  • Modifications - comportement ou documentation modifiés sans suppression de la prise en charge.
  • Obsolescences - fonctionnalité toujours disponible, mais faisant l'objet d'un plan de migration.
  • Suppressions - fonctionnalité ou endpoint supprimé.
  • Corrections - correction de bug ou de documentation.
  • Sécurité - modification touchant l'authentification, les autorisations, le traitement des données ou la gestion des clés.

Ajouts

  • Ajout d'instantanés access_time_windows de membres, indépendants des fournisseurs, combinant plages absolues et plages hebdomadaires récurrentes.
  • Ajout de la sémantique d'ordonnancement par access_time_windows_revision, de relecture idempotente, de conflit, d'ignorance des données obsolètes et de révocation explicite.
  • Ajout de recommandations sur les fuseaux horaires IANA, l'heure d'été, la correspondance logique scope_key, la fraîcheur des sources, l'implémentation côté producteur et la certification.

Ajouts

  • Ajout d'une documentation du programme partenaires couvrant les types de partenaires, l'intégration, les bonnes pratiques, la préparation à la mise en production, les erreurs, la gestion des versions et l'assistance.
  • Ajout d'un parcours de demande Devenir partenaire dédié aux futurs partenaires techniques et commerciaux.
  • Ajout d'une documentation expliquant comment les intégrations partenaires approuvées apparaissent dans l'annuaire des partenaires Performance Hub, au sein de la plateforme principale Performance Hub.

Ajouts

  • Ajout de la documentation du contrôle d'accès des contrôleurs de porte/portail, y compris les opérations de liste, d'état et de déverrouillage.
  • Ajout de la documentation du comportement de déverrouillage synchrone : l'API ne renvoie un succès qu'après que le contrôleur sur site a confirmé le déverrouillage physique du relais.
  • Ajout de la documentation de la prise en charge de la idempotency_key facultative pour les requêtes de déverrouillage des contrôleurs de porte/portail.