API Documentation

Developer reference (The Lunch Box only). This page documents the recipe data integration that powers the site’s recipe search and detail pages. It is maintained by development.

Recipes are powered by HealthePro

The Lunch Box’s recipes are fed by the HealthePro recipe API/JSON. The recipe search page, recipe detail pages, and the downloadable nutrition/cost reports are all built from HealthePro data.

On the front end this is rendered by HealthePro-specific components:

Everything about a recipe — copy, image, category, attributes, allergens, ingredients, nutrition — is maintained in HealthePro; nothing is edited in Craft. See the “Recipes (HealthePro)” section on the “Site Specific” page for the editor workflow.

How the integration works

There is no middleware server or intermediate database. The site talks to HealthePro’s external REST API directly over HTTPS with a bearer token (the HEALTHE_PRO_API / HEALTHE_PRO_AUTH environment variables). The token is used server-side only and is never sent to the browser.

Two mechanisms are in play:

  1. Cached recipe feed (listing/search). An hourly job on the TLB web server (svr-scripts/fortrabbit/healthe-pro/, run from the Fortrabbit cron queue — classic Fortrabbit’s smallest interval is one hour) walks every page of /recipes, fetches each recipe’s detail record, and publishes an optimized JSON feed at web/api/healthe-pro/healthe-pro-recipes-active.json — not a mirror of HealthePro, but each active recipe reduced to the handful of fields the search page needs to list, filter and sort (see “Search-page feed record” below) (plus -inactive and -error files, and a locally resized 500 px card thumbnail per active recipe). The feed carries a content-hash version token so browsers pick up a rebuilt feed immediately. The previous feed is kept as a gzip backup for 7 days. The recipe search page downloads the feed once and does all searching, filtering, sorting and paging in the browser.
  2. Live requests (detail, scaling, exports). Recipe detail pages, the Category filter list and “Scale Recipe” call HealthePro per request from Craft templates. PDF and XLSX exports are generated on the TLB server (mPDF / spreadsheet library) from the scaled data — HealthePro’s own print PDFs are not used.

Only recipes flagged active are published to the search page; inactive recipes’ detail URLs redirect (302) to the recipe index. Legacy OneSource recipe codes (e.g. DR008) 301-redirect to the matching HealthePro id via config/onesource_mappings.php.

Endpoint reference

Base URL: https://menuplan.healthepro.com/api/v1/external/organizations/{orgId} (the full base, including the organization id, is the HEALTHE_PRO_API env var). Auth: Authorization: <bearer token> header. HealthePro rate limit: 300 requests/minute.

EndpointUsed by
GET /recipes?order_by=name&page=Nfeed build (paginated; meta.last_page gives the page count); status monitor (catalog check)
GET /recipes/{id}feed build (per-recipe detail); recipe detail page
GET /recipes/{id}?scale=N“Scale Recipe”; PDF/XLSX export; status monitor (scaling check)
GET /recipe_categoriesCategory filter on the search page (restricted and ordered to a fixed list of 14 food categories)

Fields consumed from /recipes/{id}: id, name, active, image (signed CloudFront URL, expires in 1 hour — hence the locally cached thumbnails), category, category_id, attributes[].name, allergens[].name, food_process.name (HACCP), serving_size, serving_measure, yield, ingredients[] (id, name, quantity, quantity2), preparation_instructions (HTML), meal_components.*, nutrients.*.

Search-page feed record (reduced object per recipe): id, name, category, category_id, image, thumb (site-hosted thumbnail), allergens and attributes as comma-separated strings, active, status. The feed’s top-level filters object holds the de-duplicated allergen and attribute names that populate the two filter dropdowns.

Site-side endpoints (Craft templates / PHP): /action-healthepro-categories/, /action-recipe-ingredients-healthepro/?id=&servings=, /api/recipe-export.php (POST), /recipe-status/.

History: this integration replaced the OneSource/Horizon system (2025-11), which ran through a CAF-owned middleware server on Linode at recipe-api.thelunchbox.org. That server is decommissioned; the hostname now simply aliases to the status page below.

Service status & monitoring

There’s a simple, public status page that reports whether the HealthePro recipe service is up or down. It’s meant for an uptime monitor (Uptime Robot, or similar) so you get an alert the moment recipes stop working. It replaces the old recipe-api.thelunchbox.org/Status service.

Status URL: https://recipe-api.thelunchbox.org/ — the dedicated address for monitoring the recipe service. It points at the site’s own status page (https://www.thelunchbox.org/recipe-status/); either address works and returns the same thing.

Open it in a browser and you’ll get a small JSON response. The two things to look at are the HTTP status code the page returns and the "status" value in the response:

Under the hood it checks two things separately, shown in the response as catalog (can we load the recipe list?) and scaling (can we scale a real recipe?).

Setting up the monitor

  1. In Uptime Robot, add an HTTP(s) monitor pointing at https://recipe-api.thelunchbox.org/. The default behaviour — treat 200 as up, anything else as down — is all you need; a 503 will trigger the alert.
  2. Optional: add a second monitor at https://recipe-api.thelunchbox.org/?require=scaling. This one goes down specifically when ingredient scaling breaks, even if the recipe list is still working — the equivalent of the old IngredientsStatus check. Skip it if you only care about total outages.

If you’re updating an existing check, just swap its old recipe-api.thelunchbox.org/Status URL for https://recipe-api.thelunchbox.org/.

If it’s reporting down

Add ?debug=error to the URL — for example https://recipe-api.thelunchbox.org/?debug=error — and the page adds a short plain-English note about which check failed. No login is needed to view the status page, and it never shows any HealthePro password or key.