Intégration du contrôle qualité
Intégrez un système QA / laboratoire externe avec Roasthubs via des webhooks QC et des lectures legacy /api/.
Les valeurs id_tag des lots sont générées comme {3 premières lettres du nom de zone}-{séquence} (ex. green → GRE-4, roast → ROA-12).
Flux
Parameter recorded on lot
│
▼
Target evaluated ──► lot_target_pass | lot_target_fail
│
▼ (all targets evaluated)
Overall QC ────────► lot_qc_pass | lot_qc_fail
│
▼
Webhook POST → GET /api/lots/:id + GET /api/lots_targets?lot_id=:id
- Abonnez-vous aux quatre événements QC sous System → Webhooks.
- À la livraison, utilisez
resource_idcomme id du lot (confirmez avecdata.lot_idlorsqu'il est présent). - Chargez le détail lot / QC avec les appels
/api/ci-dessous (pas de GET lot QC sur v2 aujourd'hui). - Dédupliquez sur l'
iddu webhook ; renvoyez 2xx rapidement.
Enveloppe webhook
POST JSON, nouvelles tentatives avec backoff exponentiel. id est la clé d'idempotence.
{
"id": 18402,
"type_id": "lot_qc_fail",
"user_id": 4,
"resource_id": 4,
"roasthubs_zone_id": 1,
"data": {}
}
| Champ | Signification |
|---|---|
id | Id d'événement (déduplication) |
type_id | Nom d'événement ci-dessous |
resource_id | Id du lot pour les GET de suivi |
roasthubs_zone_id | Zone du lot |
data | Charge utile de l'événement |
Événements QC
lot_target_pass / lot_target_fail
Émis lorsqu'une cible sur un lot est évaluée (après POST/PUT /api/lots_parameters, ou approbation booléenne).
data est la ligne lots_targets mise à jour :
{
"id": 101,
"lot_id": 4,
"target_id": 7,
"target_name": "Moisture",
"parameter_id": 3,
"parameter_name": "Moisture",
"lower_bound": 10,
"upper_bound": 12.5,
"boolean_target": null,
"status": "fail",
"value": null,
"last_evaluated": "2026-09-03T10:15:02.441Z",
"created_at": "2026-09-01T08:05:11.102Z"
}
Utilisez "pass" ou "fail" dans status selon l'événement.
L'approbation booléenne en masse peut plutôt envoyer :
{ "status": "pass", "target_id": 101 }
Dans ce chemin, resource_id peut être l'id de la cible — préférez data.lot_id / un GET /api/lots_targets de suivi en cas de doute.
lot_qc_pass / lot_qc_fail
Émis lorsque chaque cible du lot a été évaluée. Échec si une cible a échoué (status → qc_failed).
data est le lot incluant lots_targets :
{
"id": 4,
"id_tag": "GRE-4",
"number": "BL-2401",
"recipe_id": 10,
"roasthubs_zone_id": 1,
"status": "qc_failed",
"datetime_qc": "2026-09-03T10:20:14.882Z",
"qc_failed": true,
"qc_note": null,
"lots_targets": [
{
"id": 101,
"lot_id": 4,
"target_id": 7,
"target_name": "Moisture",
"parameter_id": 3,
"parameter_name": "Moisture",
"lower_bound": 10,
"upper_bound": 12.5,
"status": "fail",
"last_evaluated": "2026-09-03T10:15:02.441Z"
},
{
"id": 102,
"lot_id": 4,
"target_id": 8,
"target_name": "Water activity",
"parameter_id": 5,
"parameter_name": "Water activity",
"lower_bound": 0.4,
"upper_bound": 0.6,
"status": "pass",
"last_evaluated": "2026-09-03T10:18:44.019Z"
}
]
}
En cas de réussite, attendez-vous à "status": "available_for_processing" (ou un statut non-échec inchangé) et toutes les cibles "pass".
APIs lot et QC (/api/)
Session authentifiée requise. Utilisez resource_id du webhook comme :id / lot_id.
| Méthode | Endpoint | Objectif |
|---|---|---|
GET | /api/lots/:id | Lot + recette + certificats + paramètres mesurés |
GET | /api/lots_targets?lot_id=:id | Matrice de cibles faisant autorité |
GET | /api/parameters | Catalogue de paramètres (mis en cache) |
GET | /api/targets | Catalogue de specs (mis en cache ; ?product_id= optionnel) |
GET | /api/targets/:id | Spec unique |
GET | /api/parameters_types | Type de paramètre / regroupement par zone |
GET | /api/recipes/:id | Recette complète si l'embed du lot ne suffit pas |
GET | /api/lots?rhz=&page=&limit= | Liste / sync (optionnel) |
Réécriture (optionnelle ; peut réémettre des webhooks QC) :
| Méthode | Endpoint |
|---|---|
POST | /api/lots_parameters |
PUT | /api/lots_parameters/:id |
POST | /api/lots/:id/approveallbooleantargets |
GET /api/lots/:id
GET /api/lots/:id
Exemple de réponse :
{
"data": {
"id": 4,
"id_tag": "GRE-4",
"number": "BL-2401",
"supplier_id": 3,
"roasthubs_zone_id": 1,
"unit_id": null,
"user_id": 4,
"recipe_id": 10,
"target_plant_node_id": 12,
"status": "qc_failed",
"datetime_qc": "2026-09-03T10:20:14.882Z",
"qc_failed": true,
"qc_note": null,
"erp_id": null,
"erp_url": null,
"production_id": null,
"is_processed_by_plc": false,
"is_in_warehouse": true,
"is_available_for_processing": false,
"needs_cleaning": false,
"remaining_weight_kg": 847.5,
"expected_weight_kg_input": 1000,
"actual_weight_kg_input": 998.2,
"expected_weight_kg_output": 1000,
"actual_weight_kg_output": 998.2,
"note": null,
"eudr_dds_number": null,
"created_at": "2026-09-01T08:00:03.114Z",
"updated_at": "2026-09-03T10:20:14.882Z",
"recipe": {
"id": 10,
"name": "Brazil Cerrado NY2",
"source_roasthubs_zone_id": 1,
"note": null
},
"target_plant_node": { "name": "Silo A3" },
"user": { "first_name": "Ada" },
"certificates": [{ "id": 1, "name": "Organic" }],
"parameters": [
{
"id": 3,
"name": "Moisture",
"data_type": "number",
"parameters_type_id": 1,
"unit_id": 1,
"lot_parameter": {
"id": 9001,
"value": "13.1",
"note": null,
"user_id": 4,
"created_at": "2026-09-03T10:14:55.220Z"
}
},
{
"id": 5,
"name": "Water activity",
"data_type": "number",
"parameters_type_id": 1,
"unit_id": 2,
"lot_parameter": {
"id": 9002,
"value": "0.52",
"note": null,
"user_id": 4,
"created_at": "2026-09-03T10:18:40.091Z"
}
}
]
}
}
Joignez parameters[].lot_parameter.value aux cibles sur parameter_id.
GET /api/lots_targets?lot_id=:id
GET /api/lots_targets?lot_id=:id
Exemple de réponse :
{
"data": [
{
"id": 101,
"lot_id": 4,
"target_id": 7,
"target_name": "Moisture",
"parameter_id": 3,
"parameter_name": "Moisture",
"lower_bound": 10,
"upper_bound": 12.5,
"boolean_target": null,
"status": "fail",
"value": null,
"last_evaluated": "2026-09-03T10:15:02.441Z",
"created_at": "2026-09-01T08:05:11.102Z"
},
{
"id": 102,
"lot_id": 4,
"target_id": 8,
"target_name": "Water activity",
"parameter_id": 5,
"parameter_name": "Water activity",
"lower_bound": 0.4,
"upper_bound": 0.6,
"boolean_target": null,
"status": "pass",
"value": null,
"last_evaluated": "2026-09-03T10:18:44.019Z",
"created_at": "2026-09-01T08:05:11.220Z"
}
]
}
status: not_evaluated | pass | fail.
GET /api/parameters
GET /api/parameters
Exemple de réponse :
{
"data": [
{
"id": 3,
"name": "Moisture",
"data_type": "number",
"parameters_type_id": 1,
"unit_id": 1,
"regex": null,
"regex_message": null,
"parameter_type": {
"id": 1,
"name": "Green intake",
"roasthubs_zone_id": 1
},
"lot_parameter": null
}
]
}
Aussi : GET /api/parameters_types.
GET /api/targets / GET /api/targets/:id
GET /api/targets
GET /api/targets?product_id=
GET /api/targets/:id
Exemple de réponse de liste :
{
"data": [
{
"id": 7,
"parameter_id": 3,
"lower_bound": 10,
"upper_bound": 12.5,
"applies_to_all_lots": false,
"removed": false,
"recipe_id": 10,
"plant_node_id": null,
"recipes_groups_id": null,
"product_id": null,
"value": null,
"parameters": {
"id": 3,
"name": "Moisture",
"data_type": "number",
"parameters_types": {
"id": 1,
"name": "Green intake",
"roasthubs_zone_id": 1
}
}
}
]
}
GET /api/recipes/:id (optionnel)
GET /api/recipes/:id
Renvoie { "data": { …recipe…, "lines": [ … ] } } — utilisez lorsque vous avez besoin des composants / champs ERP au-delà de l'embed du lot.
GET /api/lots (liste / sync optionnelle)
GET /api/lots?rhz=&page=&limit=&status=
Exemple de réponse :
{
"meta": { "count": 12 },
"data": [
{
"id": 4,
"id_tag": "GRE-4",
"number": "BL-2401",
"status": "qc_failed",
"recipe": { "id": 10, "name": "Brazil Cerrado NY2" },
"targets_qty": 2,
"targets_qty_pass": 1,
"targets_qty_fail": 1
}
],
"parameters": []
}
Avec rhz défini, parameters peut inclure des lignes lots_parameters pour la page.
Écrire des mesures (optionnel)
POST /api/lots_parameters
Content-Type: application/json
{
"lot_id": <lot_id>,
"parameter_id": <parameter_id>,
"value": "<measured_value>",
"note": "<optional_note>",
"user_id": <user_id>
}
PUT /api/lots_parameters/:id
Content-Type: application/json
{
"value": "<measured_value>",
"note": "<optional_note>"
}
POST /api/lots/:id/approveallbooleantargets
Ceux-ci réévaluent les cibles et peuvent émettre lot_target_* / lot_qc_* — gérez vos propres événements via l'id du webhook.
Authentification
- Webhooks : System → Webhooks
- API : session authentifiée / accès API de l'organisation
- Swagger v2 (non-QC) :
{baseUrl}/api/v2/docs
Voir aussi
docs/webhook-events.mddocs/v2-api.md