L’API clients permet de consulter les sites, les sondes et leurs mesures en lecture seule.
Télécharger le contrat OpenAPI
Racine de l’API : https://app.marinade.pro/api/customer/v1
Première requête
- Dans votre compte, ouvrez Réglages, puis la section Développeurs en bas de page.
- Créez une clé pour votre intégration et copiez-la. Sa valeur n’est affichée qu’une seule fois.
- Définissez
MARINADE_API_BASE_URLavec l’URL racine indiquée ci-dessus. - Stockez la clé dans la variable d’environnement
MARINADE_API_KEY, puis exécutez la commande suivante depuis votre serveur.
curl --fail-with-body \
--header "Authorization: Bearer ${MARINADE_API_KEY}" \
"${MARINADE_API_BASE_URL}/sensors?limit=100"Authentification
Passez la clé dans l’en-tête Authorization: Bearer ${MARINADE_API_KEY} de chaque requête. Les cookies de session et les jetons de l’application mobile ne sont pas acceptés.
Utilisez HTTPS pour tous les appels. Conservez la clé côté serveur, dans une variable d’environnement ou un gestionnaire de secrets. Ne l’incluez pas dans une URL, dans le code source ou dans du code exécuté par le navigateur.
Clés API
Chaque clé donne accès à tous les sites du compte, en lecture seule. Les clés sont sans date d’expiration. Un compte peut avoir jusqu’à 10 clés non révoquées ; les clés suspendues comptent dans cette limite.
Les administrateurs et les opérateurs en mode assistance peuvent créer et révoquer des clés dans les réglages du compte. Si le compte est en lecture seule, les clés existantes restent utilisables et peuvent être révoquées, mais aucune nouvelle clé ne peut être créée.
Conditions d’accès
L’installation du compte doit être terminée et l’accès à l’application ouvert. Sinon, l’API renvoie 403 customer_access_denied.
Une clé reste liée à l’utilisateur qui l’a créée :
- L’administrateur doit rester actif, conserver son accès au compte et son rôle d’administrateur.
- L’opérateur doit conserver l’accès au compte.
Si ces conditions ne sont plus remplies, la clé est suspendue et l’API renvoie 401 unauthorized.
Rotation d’une clé
La révocation est immédiate. Pour remplacer une clé sans interrompre l’intégration :
- Créez une nouvelle clé.
- Remplacez la clé dans la configuration de votre intégration et vérifiez qu’un appel à l’API réussit.
- Révoquez l’ancienne clé.
Endpoints
Les chemins sont relatifs à MARINADE_API_BASE_URL. L’API accepte les méthodes GET et HEAD.
GET /sites
Liste des sites : id (UUID), name.
GET /sensors
Liste des sondes non supprimées et de leur dernière mesure valide. Filtres optionnels : site_id et status.
GET /sensors/{id}
Détail d’une sonde. Les sondes désactivées restent visibles ; les sondes supprimées renvoient 404.
GET /sensors/{id}/readings
Mesures individuelles, sans moyenne ni interpolation. Paramètres obligatoires : from et to, dates RFC 3339 avec fuseau. Exemple : 2026-11-01T00:00:00Z. La borne de début est incluse, celle de fin exclue. Aucune limite de durée ; la fin ne peut pas être dans le futur.
Toutes les listes acceptent limit (100 par défaut, de 1 à 1000) et cursor. Copiez pagination.next_cursor pour la suite, en conservant vos filtres et votre limite. Le curseur expire après 24 heures. has_more: false et next_cursor: null indiquent la dernière page. Aucun total n’est calculé.
Le détail renvoie data: {…}, les listes data: […]. Chaque réponse contient meta.api_version et meta.generated_at. Les listes ajoutent pagination. Ignorez les champs inconnus pour permettre l’évolution de l’API.
Comprendre les données
Une sonde contient id, name, site_id, status, last_seen_at, disabled, alerts_enabled, min_temperature_c, max_temperature_c et latest_reading. Les seuils sont les réglages actuels, pas ceux de chaque ancienne mesure.
États : pending (en attente), ok, temperature_high, temperature_low, battery_low, offline ou disabled. Un dépassement de seuil n’indique pas nécessairement qu’une alerte a déjà été déclenchée. Prévoyez un affichage de secours pour un nouvel état.
Une mesure contient id, timestamp (acquisition), received_at (réception), site_id (site enregistré à l’acquisition), temperature_c, battery_v et temperature_valid.
Les identifiants sont des chaînes opaques. Les dates renvoyées sont en UTC ; les températures en °C et les tensions en volts, sous forme de nombres JSON. null signifie indisponible. La tension n’est pas un pourcentage de pile. Les relevés historiques peuvent ne contenir aucune tension.
temperature_valid indique une température non nulle entre -40 et 80 °C inclus. L’historique conserve aussi les mesures invalides ; la dernière mesure de la sonde est la plus récente valide par date d’acquisition. latest_reading peut être nul, et une ancienne température ne prouve pas que la sonde communique encore : vérifiez last_seen_at et status.
Comparez timestamp et received_at pour connaître le délai entre la mesure et sa réception. Une réception inconnue reste nulle. L’historique décrit les relevés de cet appareil pour ce client, pas nécessairement une présence continue dans le même réfrigérateur. Utilisez le site de chaque relevé ; un remplacement ou une réaffectation peut changer l’identifiant de sonde.
Exemple de réponse
{
"data": [
{
"id": "42",
"name": "Cold room",
"site_id": null,
"status": "ok",
"last_seen_at": "2026-11-02T10:01:00.000000Z",
"disabled": false,
"alerts_enabled": true,
"min_temperature_c": 0.0,
"max_temperature_c": 8.0,
"latest_reading": {
"id": "90715",
"timestamp": "2026-11-02T10:00:00.000000Z",
"received_at": "2026-11-02T10:01:00.000000Z",
"site_id": null,
"temperature_c": 3.6,
"battery_v": 3.1,
"temperature_valid": true
}
}
],
"meta": {
"api_version": "v1",
"generated_at": "2026-11-02T10:02:00.000000Z"
},
"pagination": {
"next_cursor": null,
"has_more": false
}
}Historique et données tardives
Pour afficher l’état du parc, une requête sur /sensors par minute suffit généralement. Pour importer l’historique, parcourez toutes les pages de fenêtres fixes de la durée de votre choix et enregistrez les relevés en mettant à jour les identifiants déjà connus.
Après une coupure, une sonde peut envoyer des mesures anciennes. Une retransmission peut aussi enrichir une mesure existante. La pagination ne fige pas la base : une insertion tardive peut se situer avant votre curseur. Ni la date de mesure ni un identifiant croissant ne garantissent une synchronisation complète.
Relisez régulièrement une période de recouvrement, par exemple les dernières 48 heures, et réconciliez les périodes plus anciennes après une récupération. Un recouvrement fixe ne garantit pas de capter toutes les données tardives. Si le curseur expire, recommencez la fenêtre et dédupliquez par identifiant. Les exports sont limités aux données effectivement conservées.
Exemples d’import paginé
Exemples avec une fenêtre passée fixe. Enregistrez chaque mesure par son id. Relancez l’import pour réconcilier les données tardives.
Définissez aussi MARINADE_SENSOR_ID avec l’identifiant obtenu dans la liste des sondes. Adaptez les dates from et to à la période souhaitée.
Python (bibliothèque standard)
import json
import os
import urllib.parse
import urllib.request
base = os.environ["MARINADE_API_BASE_URL"].rstrip("/")
key = os.environ["MARINADE_API_KEY"]
sensor_id = urllib.parse.quote(os.environ["MARINADE_SENSOR_ID"], safe="")
params = {"from": "2026-11-01T00:00:00Z", "to": "2026-11-02T00:00:00Z", "limit": 100}
while True:
url = f"{base}/sensors/{sensor_id}/readings?{urllib.parse.urlencode(params)}"
request = urllib.request.Request(url, headers={"Authorization": f"Bearer {key}"})
with urllib.request.urlopen(request, timeout=15) as response:
page = json.load(response)
for reading in page["data"]:
print(json.dumps(reading)) # Upsert into your database by reading ID.
if not page["pagination"]["has_more"]:
break
params["cursor"] = page["pagination"]["next_cursor"]
Node.js
const base = process.env.MARINADE_API_BASE_URL.replace(/\/$/, "")
const key = process.env.MARINADE_API_KEY
const sensorId = encodeURIComponent(process.env.MARINADE_SENSOR_ID)
const params = new URLSearchParams({ from: "2026-11-01T00:00:00Z", to: "2026-11-02T00:00:00Z", limit: "100" })
while (true) {
const response = await fetch(`${base}/sensors/${sensorId}/readings?${params}`, {
headers: { Authorization: `Bearer ${key}` }, signal: AbortSignal.timeout(15000)
})
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`)
const page = await response.json()
for (const reading of page.data) console.log(reading) // Upsert by reading ID.
if (!page.pagination.has_more) break
params.set("cursor", page.pagination.next_cursor)
}
Erreurs, limites et assistance
60 requêtes par minute par clé et 300 par compte, toutes clés confondues. Les en-têtes RateLimit-Limit, RateLimit-Remaining et RateLimit-Reset décrivent le quota de la clé ; le quota du compte peut bloquer plus tôt. RateLimit-Reset est une date Unix en secondes.
400 invalid_parameter: paramètre ou curseur invalide ;cursor_expired: recommencez la fenêtre.401 unauthorized: clé absente, invalide, révoquée ou créateur non autorisé.403 customer_access_denied: accès au compte indisponible ou installation incomplète.404 not_found: ressource indisponible ou introuvable.405 method_not_allowed: utilisez GET ou HEAD.429 rate_limit_exceeded: attendez le nombre de secondes deRetry-After.503 temporarily_unavailableou autre5xx: réessayez avec un délai croissant et une petite variation aléatoire.
{"error":{"code":"invalid_parameter","message":"Unknown status","request_id":"example-id"}}Ne réessayez pas indéfiniment une erreur 401. Pour toute demande d’assistance, communiquez l’en-tête X-Request-Id, jamais votre clé. Les exemples lèvent une erreur HTTP ; ajoutez une stratégie de reprise adaptée à votre logiciel.
Version v1 : première version de l’API clients. Les changements incompatibles nécessiteront une nouvelle version et une annonce avant retrait. Aucun webhook ni accès en écriture n’est disponible.