API Calculadora Mediterránea + EAT

Cálculo de calorías, EAT score, huella de carbono y MEDAS, y recomendaciones de compra. Autenticación: Authorization: Bearer <JWT del participante> o x-api-key para socios.

Base: /api/public · Especificación: openapi.json

post/v1/calculateCalcular sin guardar (scope: calculate)
get/v1/assessments/{id}/resultsResultados guardados (scope: results:read)
get/v1/assessments/{id}/recommendationsPrioridades y estado por grupo (scope: recommendations:read)
post/v1/shopping-listsCrear lista de compras (scope: shopping:write)
post/v1/purchases/analyzeAnalizar una compra (scope: purchases:analyze)
get/v1/catalog/productsCatálogo de productos (público, CORS abierto)
get/v1/openapi.jsonEsta especificación
Ver JSON completo
{
  "openapi": "3.1.0",
  "info": {
    "title": "API Calculadora Mediterránea + EAT",
    "version": "1.0.0",
    "description": "Cálculo de calorías, EAT score, huella de carbono y MEDAS, y recomendaciones de compra. Autenticación: Authorization: Bearer <JWT del participante> o x-api-key para socios."
  },
  "servers": [
    {
      "url": "/api/public"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "scheme": "bearer"
      },
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "x-api-key"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        }
      },
      "PurchaseLine": {
        "type": "object",
        "required": [
          "description",
          "quantity"
        ],
        "properties": {
          "sku": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "quantity": {
            "type": "number"
          },
          "unit_g": {
            "type": "number"
          },
          "price_crc": {
            "type": "number"
          }
        }
      }
    }
  },
  "security": [
    {
      "bearer": []
    },
    {
      "apiKey": []
    }
  ],
  "paths": {
    "/v1/calculate": {
      "post": {
        "summary": "Calcular sin guardar (scope: calculate)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "per_day"
                ],
                "properties": {
                  "per_day": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "number"
                    },
                    "description": "Veces por día por código de alimento"
                  },
                  "cd_azucar": {
                    "type": "number",
                    "description": "Cucharaditas de azúcar al día"
                  },
                  "fat_type_code": {
                    "type": [
                      "integer",
                      "null"
                    ],
                    "minimum": 0,
                    "maximum": 7
                  },
                  "medas": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "number"
                    },
                    "description": "Respuestas MEDAS 1–14"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Variables con nombres SAS"
          },
          "400": {
            "description": "Cuerpo inválido"
          },
          "401": {
            "description": "No autenticado"
          }
        }
      }
    },
    "/v1/assessments/{id}/results": {
      "get": {
        "summary": "Resultados guardados (scope: results:read)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Resultado"
          },
          "404": {
            "description": "Sin resultados"
          }
        }
      }
    },
    "/v1/assessments/{id}/recommendations": {
      "get": {
        "summary": "Prioridades y estado por grupo (scope: recommendations:read)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Prioridades"
          }
        }
      }
    },
    "/v1/shopping-lists": {
      "post": {
        "summary": "Crear lista de compras (scope: shopping:write)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "assessment_id",
                  "persons",
                  "days"
                ],
                "properties": {
                  "assessment_id": {
                    "type": "string",
                    "format": "uuid"
                  },
                  "persons": {
                    "type": "integer"
                  },
                  "days": {
                    "type": "integer"
                  },
                  "budget_crc": {
                    "type": [
                      "number",
                      "null"
                    ]
                  },
                  "retailer_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uuid"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "una_tienda",
                      "mejor_precio",
                      "tienda_preferida"
                    ],
                    "description": "una_tienda: cadena con menor total; mejor_precio: lo más barato por gramo en cualquier cadena; tienda_preferida: usa retailer_id"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Lista con productos (cadena y enlace), total por tienda, ahorro frente a la más cara y fecha de los precios"
          }
        }
      }
    },
    "/v1/purchases/analyze": {
      "post": {
        "summary": "Analizar una compra (scope: purchases:analyze)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "lines"
                ],
                "properties": {
                  "retailer_id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "lines": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/PurchaseLine"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Puntaje mediterráneo de la compra, reparto por grupo y sustituciones"
          }
        }
      }
    },
    "/v1/catalog/products": {
      "get": {
        "summary": "Catálogo de productos (público, CORS abierto)",
        "security": [],
        "parameters": [
          {
            "name": "rec_group",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "retailer_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Productos"
          }
        }
      }
    },
    "/v1/openapi.json": {
      "get": {
        "summary": "Esta especificación",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI 3.1"
          }
        }
      }
    }
  }
}