Rebel Food API

Food image analysis, NOVA classification, barcode lookup & eating scores

About

Rebel Food API powers the Rebel scanner and mobile apps. It uses OpenAI Vision to identify foods, classify them on the NOVA scale (1–4), detect barcodes, and generate Maria's short opinion. Scans are persisted in Supabase; barcodes are enriched from Open Food Facts.

Typical flow

  1. POST /analyze — send a photo, get AI results
  2. POST /analyze/save — persist image + results (returns scanId)
  3. Optional follow-up: re-call /analyze with followUpAnswer, then /analyze/save with scanId
  4. Optional barcode: POST /barcode with scanId to enrich from Open Food Facts

Authentication

All API endpoints require an app key via X-API-Key (or legacy Authorization: ApiKey <key> / Authorization: Bearer <key>). Separate keys exist for web and mobile clients.

For logged-in users, also send the Supabase access token as Authorization: Bearer <jwt>. The API validates the JWT and derives userId server-side — a userId in the body without a matching token returns 401; a mismatch returns 403. Anonymous usage works with anonymousId only (no JWT).

To obtain an API key, email vincent.battaglia@gmail.com with your use case.

Endpoints

POST /analyze

Analyze a food image with GPT-4o Vision. Returns NOVA category, explanation, optional follow-up question, barcode hint, nutrients, and Maria's opinion.

Content-Type: multipart/form-data

Parameters
  • image (File, required)
  • language? (string, en | fr — default: en)
  • followUpAnswer? (string)
  • previousContext? (string, required with followUpAnswer)
Response
  • novaCategory (1–4 | null)
  • category (string)
  • explanation (string)
  • needsFollowUp (boolean)
  • followUpQuestion? (string | null)
  • barcode? (string | null)
  • nutrients (string[])
  • mariaOpinion? ({ message, emotion })
  • rawResponse (string)
POST /analyze/save

Upload the image to Supabase Storage and persist the scan. Creates a new row or updates an existing one after a follow-up answer.

Content-Type: multipart/form-data

Parameters
  • image (File, required)
  • scanData (JSON string, required)
  • scanId? (string, for follow-up updates)
  • anonymousId? (string, anonymous users)
  • userId? (string, ignored — derived from JWT when logged in)
  • source? (string, web | ios | android — default: web)
Response
  • success (boolean)
  • scanId (string)

scanData fields: novaCategory, category, explanation, needsFollowUp, followUpQuestion, rawResponse, followUpAnswer?, nutrients, mariaOpinion?

Logged-in users must send Authorization: Bearer <supabase_jwt>.

Daily scan limits apply per user_id or anonymous_id when configured.

POST /barcode

Look up a barcode in Open Food Facts (cached locally). Updates the scan with product data and may override the final NOVA score.

Content-Type: application/json

Parameters
  • barcode (string, required)
  • scanId (string, required)
  • language? (string, en | fr)
  • anonymousId? (string, for anonymous scan ownership)
Response
  • found (boolean)
  • product ({ barcode, product_name, brands, ingredients_text, nutriments, … })
  • novaChanged (boolean)
  • aiNovaCategory (number | null)
  • finalNovaCategory (number | null)
  • mariaOpinion? ({ message, emotion })

Scans owned by a user require Authorization: Bearer <supabase_jwt>.

POST /barcode/decode

Decode a barcode from a photo using AI vision.

Content-Type: multipart/form-data

Parameters
  • image (File, required)
Response
  • barcode? (string)
  • error? (string)
POST /scans/maria-opinion

Update Maria's opinion on an existing scan (message + emotion).

Content-Type: application/json

Parameters
  • scanId (string, required)
  • mariaOpinion ({ message, emotion }, required)
  • anonymousId? (string)
Response
  • success (boolean)

emotion: pleased | confident | joyful | disappointed | annoyed | bored | surprised

POST /score

Compute a daily, weekly, or monthly eating score (0–100) from stored scans via AI.

Content-Type: application/json

Parameters
  • userId? (string, derived from JWT when logged in)
  • anonymousId? (string)
  • periodType? (daily | weekly | monthly — default: daily)
  • date? (string, ISO date)
  • language? (string, en | fr)
Response
  • periodType, periodKey, scansCount
  • score (number | null)
  • ultraProcessedPoints, nutritionalQualityPoints, proteinsPoints, fibersPoints, varietyPoints
  • ultraProcessedPercent? (number | null)
  • summary, details
POST /score/save

Save or update a computed score for a given period.

Content-Type: application/json

Parameters
  • userId? (string, derived from JWT when logged in)
  • anonymousId? (string)
  • periodType (daily | weekly | monthly, required)
  • periodKey (string, required)
  • scansCount? (number)
  • score? (number | null)
  • ultraProcessedPoints?, nutritionalQualityPoints?, proteinsPoints?, fibersPoints?, varietyPoints? (number | null)
  • ultraProcessedPercent? (number | null)
  • summary?, details? (string | null)
Response
  • success (boolean)

Status

API operational