REST API Documentation
Complete reference for integrating with the BrewMark API. All endpoints return JSON. Base URL: https://brewmark.io
Sessions & tokens
BrewMark supports two authentication methods. Web clients use HTTP-only cookie sessions set automatically after login. Mobile clients (iOS app) receive a JWT token in the login response body, which must be sent as a Bearer token in the Authorization header.
Authorization: Bearer <jwt-token> Content-Type: application/json
/api/auth/register3/min per IPCreate a new user account with email and password. Returns a JWT token for mobile clients or sets a session cookie for web clients.
emailstringrequiredUser email addresspasswordstringrequiredAccount passwordsuccessbooleanWhether registration succeededuserobject{ id, email, createdAt }tokenstringJWT session token (mobile clients only)POST /api/auth/register
Content-Type: application/json
{
"email": "user@example.com",
"password": "securepassword123"
}
→ 200 OK
{
"success": true,
"user": { "id": 1, "email": "user@example.com" },
"token": "eyJhbGciOiJIUzI1NiJ9..."
}/api/auth/login5/min per IPAuthenticate with email and password. Returns a JWT token for mobile clients. Web clients receive an HTTP-only session cookie.
emailstringrequiredUser email addresspasswordstringrequiredAccount passwordsuccessbooleanWhether login succeededuserobject{ id, email, createdAt }tokenstringJWT session token (mobile clients only)POST /api/auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "securepassword123"
}
→ 200 OK
{
"success": true,
"user": { "id": 1, "email": "user@example.com" },
"token": "eyJhbGciOiJIUzI1NiJ9..."
}/api/auth/magic-link5/min per IPRequest a passwordless magic link sent to the user's email. Always returns success to prevent email enumeration.
emailstringrequiredEmail address to send the magic link tosuccessbooleanAlways true (prevents email enumeration)messagestringConfirmation message/api/auth/magic-link/verify10/min per IPVerify a magic link token and create a session. The token is valid for 60 minutes and single-use.
tokenstringrequiredMagic link token from the emailsuccessbooleanWhether verification succeededuserobject{ id, email }/api/auth/apple5/min per IPSign in or register with Apple. For mobile clients, include the Apple identity token. Returns a JWT for subsequent authenticated requests.
identityTokenstringrequiredApple Sign In identity token (JWT from ASAuthorizationAppleIDCredential)emailstringUser email (provided on first sign-in only)successbooleanWhether authentication succeededuserobject{ id, email, name }tokenstringJWT session token (mobile clients only)POST /api/auth/apple
Content-Type: application/json
{
"identityToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
}
→ 200 OK
{
"success": true,
"user": { "id": "clx123...", "email": "user@icloud.com" },
"token": "eyJhbGciOiJIUzI1NiJ9..."
}/api/auth/meRequiredGet the currently authenticated user's profile and preferences. Use this to check if the session is valid and to load the user's taste profile.
userobject{ id, email, name, profile: { acidityLevel, bodyLevel, sweetnessLevel, ... } }GET /api/auth/me
Authorization: Bearer <token>
→ 200 OK
{
"user": {
"id": "clx123...",
"email": "user@icloud.com",
"name": "Jeff",
"profile": {
"acidityLevel": 3,
"bodyLevel": 4,
"sweetnessLevel": 3,
"bitternessLevel": 2,
"flavorComplexityLevel": 4,
"aftertasteLevel": 3
}
}
}/api/auth/logoutRequiredEnd the current session. Clears the session cookie for web clients. Mobile clients should discard the stored JWT.
successbooleanAlways true on successPersonalized recipes
The core product endpoints. These adapt brew recipes to the user's equipment and taste preferences using grind index normalization, taste vector matching, and Wilson score ranking.
/api/adaptRecipeOptionalCalculate an adapted brew recipe for a specific coffee, grinder, and machine combination. Optionally adjusts for taste preferences (strength, taste direction, grind nudge).
machineIdnumberrequiredBrewer/machine IDgrinderIdnumberrequiredGrinder IDcoffeeProfileIdnumberrequiredCoffee profile IDuserIdstringUser ID (for taste profile matching)strengthnumberStrength preference 1-5 (default: 3)tastenumberTaste direction -2 to +2 (negative=brighter, positive=richer)grindNudgeStepsnumberManual grind adjustment in steps (-5 to +5)baseRecipeobjectOriginal recipe before adaptationadaptedRecipeobject{ doseGrams, waterGrams, ratio, grindSetting, grindIndex, waterTempF, brewTimeSeconds }recommendedVariantobjectBest-matching variant for user's taste profileotherVariantsarrayAlternative recipe variants ranked by relevancePOST /api/adaptRecipe
Content-Type: application/json
{
"machineId": 1,
"grinderId": 5,
"coffeeProfileId": 12,
"strength": 4,
"taste": 1
}
→ 200 OK
{
"baseRecipe": {
"method": "V60",
"doseGrams": 18,
"waterGrams": 300,
"ratio": 16.67
},
"adaptedRecipe": {
"doseGrams": 20,
"waterGrams": 300,
"ratio": 15,
"grindSetting": "14 clicks",
"grindIndex": 42,
"waterTempF": 205,
"brewTimeSeconds": 210
},
"recommendedVariant": { ... },
"otherVariants": [ ... ]
}/api/get-recipeOptionalGet an adapted starting recipe for a specific coffee + equipment combo. Returns the best starting point with ranked community variants.
coffeeIdnumberrequiredCoffee profile IDgrinderIdnumberrequiredGrinder IDmachineIdnumberrequiredMachine/brewer IDfilterIdnumberFilter ID (optional)volumeGramsnumberrequiredTarget water volume in gramsstartingPointobject|nullAdapted base recipe for the equipment comborecommendationobject|nullBest-matching community recipe for user tastevariantsarrayRanked community recipe variantscoffeeobjectCoffee profile detailsequipmentobjectEquipment details used for adaptationPOST /api/get-recipe
Content-Type: application/json
{
"coffeeId": 12,
"grinderId": 5,
"machineId": 1,
"volumeGrams": 300
}
→ 200 OK
{
"startingPoint": {
"doseGrams": 18,
"waterGrams": 300,
"ratio": 16.67,
"grindSetting": "14 clicks",
"grindIndex": 42,
"waterTempF": 205,
"brewTimeSeconds": 210
},
"recommendation": { ... },
"variants": [ ... ]
}/api/recipesOptionalList and search brew recipes. Returns paginated results with recipe details. Authenticated users get taste match scores.
qstringSearch by recipe name or coffeegrinderIdnumberFilter by grindermachineIdnumberFilter by machine/brewerroastLevelstringFilter: Light, Medium, DarklimitnumberResults per page (default: 20)cursorstringPagination cursordataarrayArray of recipe objectstotalnumberTotal matching recipescursorstring|nullNext page cursorhasMorebooleanWhether more results exist/api/recipes/{id}Get a single recipe with full details including coffee, grinder, machine, filter, and creator info. Supports ETag caching.
idnumberRecipe IDcoffeeobjectCoffee profile with roaster detailsgrinderobjectGrinder usedmachineobjectMachine/brewer usedcreatedByobjectRecipe author (display name only)/api/recipes/{id}/commentsList all comments on a recipe.
commentsarrayArray of comment objects with author info/api/recipes/{id}/commentsRequired10/min per IPAdd a comment to a recipe. Sends an email notification to the recipe author.
bodystringrequiredComment text (1-2000 characters)commentobjectThe created comment object/api/recipes/{id}/commentsRequiredDelete a comment. Only the comment author or recipe owner can delete.
commentIdnumberrequiredComment ID to delete/api/browse/recipesBrowse recipes with filtering and offset-based pagination. Supports sorting by newest, highest rated, or most brewed.
qstringSearch querybrewMethodstringFilter by brew methodroasterstringFilter by roaster nameroastLevelstringFilter by roast levelsortstringnewest, highest_rated, or most_brewedpagenumberPage number (default: 1)limitnumberResults per page (default: 20)dataarrayArray of recipe objectstotalnumberTotal matching recipestotalPagesnumberTotal pages availablepagenumberCurrent page number/api/recommendationsRequiredGet personalized recipe recommendations based on the user's 6D taste vector. Returns recipes ranked by taste match score.
limitnumberMax results (default: 5, max: 20)recommendationsarrayRecipes ranked by taste match scorehasProfilebooleanWhether the user has a taste profile setprofileSummaryobject{ strengthPreference, roastPreference }Coffee catalog
Search and browse the coffee database. Each coffee has a roast level, origin, flavor notes, and taste profile used for recipe matching.
/api/coffeesSearch and list coffees across all roasters. Supports fuzzy search by coffee name, roaster name, or origin.
qstringSearch query (fuzzy matched against name, roaster, origin)roasterstringFilter by roaster slugroastLevelstringFilter: Light, Medium-Light, Medium, Medium-Dark, DarksortstringSort by: name, newest, popular (default: name)limitnumberResults per page (default: 20)cursorstringPagination cursordataarrayArray of coffee profile objectscursorstring|nullNext page cursorhasMorebooleanWhether more results existGET /api/coffees?q=ethiopian&roastLevel=Light&limit=5
→ 200 OK
{
"data": [
{
"id": 12,
"name": "Yirgacheffe Natural",
"roasterName": "Onyx Coffee Lab",
"roasterSlug": "onyx-coffee-lab",
"roastLevel": "Light",
"origin": "Ethiopia",
"flavorProfile": "Blueberry, Jasmine, Honey",
"acidityLevel": 4,
"bodyLevel": 2,
"processingMethod": "Natural"
}
],
"cursor": "eyJpZCI6MTJ9",
"hasMore": true
}/api/coffeesRequiredSubmit a new coffee to the catalog. Requires authentication. The coffee is associated with a roaster by name (auto-matched or created).
namestringrequiredCoffee nameroasterNamestringrequiredRoaster name (matched to existing or creates placeholder)roastLevelstringrequiredLight, Medium-Light, Medium, Medium-Dark, or DarkaciditynumberAcidity level 1-5bodynumberBody level 1-5flavorProfilestringComma-separated flavor notesoriginstringCountry or region of originprocessingMethodstringWashed, Natural, Honey, etc.descriptionstringFree-text descriptioncoffeeobjectThe created coffee profile object/api/coffees/{id}Get a single coffee profile with full details including roaster, origin, flavor notes, and community recipe count. Supports ETag caching.
coffeeobjectFull coffee profile with submittedBy user and _count.communityRecipesGET /api/coffees/12
→ 200 OK
{
"coffee": {
"id": 12,
"name": "Yirgacheffe Natural",
"roasterName": "Onyx Coffee Lab",
"roastLevel": "Light",
"origin": "Ethiopia",
"processingMethod": "Natural",
"flavorProfile": "Blueberry, Jasmine, Honey",
"acidityLevel": 4,
"bodyLevel": 2,
"_count": { "communityRecipes": 8 }
}
}/api/coffees/{id}/statsGet brewing statistics for a specific coffee. Includes recipe count, vote totals, top grinders/machines, and ratio/grind ranges.
coffeeIdnumberCoffee profile IDrecipeCountnumberNumber of community recipesvotesobject{ total, upvotes, downvotes }topGrindersarrayMost-used grinders with avg grind indextopMachinesarrayMost-used machines/brewersratioRangeobject|null{ min, max, avg } water:coffee ratiogrindRangeobject|null{ min, max, avg } grind index (0-100)/api/coffees/suggestionsGet coffee name suggestions for autocomplete. Returns up to 5 fuzzy-matched coffees.
namestringCoffee name to suggest forroasterstringRoaster name to suggest forsuggestionsarrayArray of matching coffee profilesGrinders, machines & filters
Equipment endpoints return the full catalog of supported grinders, brewers, and filters. Grinder data includes the grind index mapping used for recipe adaptation.
/api/grindersList all supported grinders. Each grinder includes its brand, model, grind step count, and grind index range for recipe adaptation.
brandstringFilter by brand namegrindersarrayAll grinder objects with id, brand, model, minGrindIndex, maxGrindIndex, clicksPerFullRangebyBrandobjectGrinders grouped by brand namebrandsarrayList of unique brand namesGET /api/grinders?brand=Comandante
→ 200 OK
{
"grinders": [
{
"id": 5,
"brand": "Comandante",
"model": "C40 MK4",
"minGrindIndex": 0,
"maxGrindIndex": 100,
"clicksPerFullRange": 40
}
],
"brands": ["Comandante"]
}/api/machinesList all supported brewing machines and manual brewers (V60, AeroPress, Chemex, espresso machines, etc.).
brandstringFilter by brandbrewMethodstringFilter by brew method typemachinesarrayAll machine/brewer objects with id, brand, model, brewMethod, defaultWaterTempFbyBrandobjectMachines grouped by brandbrandsarrayList of unique brand names/api/filtersList all filter types (paper, metal, cloth) with their attributes.
filtersarrayArray of { id, name, type, description }/api/brew-methodsList all available brew methods (V60, AeroPress, Chemex, French Press, Espresso, etc.).
dataarrayArray of brew method objectsRoaster profiles
Browse and search roaster profiles. Each roaster has a public page with their coffee catalog and community recipes.
/api/roastersList all roasters with search and pagination.
searchstringSearch by roaster namepagenumberPage number (default: 1)limitnumberResults per page (default: 20)roastersarrayArray of roaster objectspaginationobject{ page, limit, total, totalPages }GET /api/roasters?search=onyx&limit=5
→ 200 OK
{
"roasters": [
{
"id": 1,
"name": "Onyx Coffee Lab",
"slug": "onyx-coffee-lab",
"location": "Rogers, AR",
"description": "Specialty roaster...",
"logoUrl": "https://...",
"isVerified": true,
"_count": { "coffees": 24 }
}
],
"pagination": { "page": 1, "limit": 5, "total": 1, "totalPages": 1 }
}/api/roasters/{slug}Get a single roaster profile by slug. Includes coffee count and community recipe count. Supports ETag caching.
idnumberRoaster IDnamestringRoaster nameslugstringURL-safe slugtaglinestringRoaster taglinestylestringRoasting stylewebsitestringRoaster website URL_countobject{ coffees: number }recipeCountnumberTotal community recipes for this roaster/api/roasters/{slug}/coffeesList a roaster's coffee catalog with optional roast level filtering. Cursor-based pagination sorted alphabetically.
roastLevelstringFilter by roast levelcursorstringPagination cursorlimitnumberResults per page (default: 20)dataarrayCoffee profiles with recipe countscursorstring|nullNext page cursorhasMorebooleanWhether more results exist/api/roasters/{slug}/recipesList community-submitted recipes for all of a roaster's coffees. Cursor-based pagination.
cursorstringPagination cursorlimitnumberResults per page (default: 20)dataarrayCommunity recipe objects with coffee, grinder, and brewer detailscursorstring|nullNext page cursorhasMorebooleanWhether more results existUser-submitted recipes & voting
Community recipes are submitted by users and ranked using Wilson score confidence intervals based on upvotes and downvotes.
/api/community-recipesOptionalList community recipes with optional filters. Authenticated users get taste match scores. Cursor-based pagination.
coffeeIdnumberFilter by coffeegrinderIdnumberFilter by grindermachineIdnumberFilter by machinecreatedByMebooleanOnly show user's own recipeslimitnumberResults per page (default: 20)cursorstringPagination cursordataarrayCommunity recipe objects with taste match scorestotalnumberTotal matching recipescursorstring|nullNext page cursorhasMorebooleanWhether more results exist/api/community-recipesRequiredSubmit a community brew recipe. Requires authentication. The recipe is public and can be voted on by other users.
coffeeProfileIdnumberrequiredCoffee this recipe is forgrinderIdnumberrequiredGrinder usedmachineIdnumberrequiredMachine/brewer usedgrindSettingstringrequiredGrind setting (e.g. "14 clicks")doseGramsnumberrequiredCoffee dose in gramswaterGramsnumberrequiredWater amount in gramsrationumberrequiredWater-to-coffee ratiofilterIdnumberFilter used (optional)brewTimeSecondsnumberTotal brew time in secondswaterTempFnumberWater temperature in FahrenheitnotesstringBrewing notes or instructionstasteProfileobject{ acidity, body, sweetness, bitterness } ratings 1-5/api/community-recipes/{id}/voteRequired30/min per IPVote on a community recipe. Each user can have one vote per recipe. Voting again changes the vote.
vote"UP" | "DOWN"requiredVote directionvotestringThe recorded vote (UP or DOWN)messagestringConfirmation message/api/community-recipes/{id}/voteOptionalGet the current user's vote on a specific community recipe. Returns null if the user hasn't voted.
vote"UP" | "DOWN" | nullCurrent user's vote, or null/api/community-recipes/{id}/brew-logOptionalGet aggregated brew ratings for a community recipe. If authenticated, also returns the user's own rating.
avgnumberAverage rating (1-5)countnumberTotal ratings countuserRatingobject{ rating, notes } — current user's rating (auth only)/api/community-recipes/{id}/brew-logRequired30/min per IPLog a brew and rate a community recipe. Creates a brew log entry and upserts the rating. Notifies the recipe author.
ratingnumberrequiredRating 1-5notesstringBrew notesdoseGramsnumberCoffee dose usedgrindSettingstringGrind setting usedbrewLogobject{ id } — created brew logratingobject{ avg, count } — updated aggregate ratingTaste profile & preferences
The user's taste profile is a 6-dimensional vector used for recipe matching. Each dimension is an integer from 1 to 5.
/api/user/profileRequiredGet the current user's taste profile and preferences.
profileobject{ acidityLevel, bodyLevel, sweetnessLevel, bitternessLevel, flavorComplexityLevel, aftertasteLevel } — each 1-5/api/user/profileRequiredUpdate the user's taste profile. Send only the fields you want to change.
acidityLevelnumber1 (low) to 5 (high)bodyLevelnumber1 (light) to 5 (full)sweetnessLevelnumber1 (dry) to 5 (sweet)bitternessLevelnumber1 (low) to 5 (high)flavorComplexityLevelnumber1 (simple) to 5 (complex)aftertasteLevelnumber1 (short) to 5 (lingering)profileobjectUpdated taste profile object/api/user/profile/onboardingRequiredComplete the taste profile onboarding flow. Sets all taste dimensions at once.
acidityLevelnumberrequired1-5bodyLevelnumberrequired1-5sweetnessLevelnumberrequired1-5bitternessLevelnumberrequired1-5flavorComplexityLevelnumberrequired1-5aftertasteLevelnumberrequired1-5/api/user/saved-recipesRequiredGet the user's bookmarked community recipes.
savedRecipesarrayArray of saved recipe objects with full recipe details/api/user/saved-recipesRequiredBookmark a community recipe.
communityRecipeIdnumberrequiredCommunity recipe ID to save/api/user/saved-recipesRequiredRemove a bookmarked community recipe.
communityRecipeIdnumberrequiredCommunity recipe ID to unsave/api/users/taste-profileRequiredGet the user's 6-dimensional taste vector used for recipe matching. Returns defaults (all 0.5) if no profile set.
profileobject{ strengthPreference, acidityTolerance, roastPreference, bodyVsClarity, bitternessSensitivity, explorerType } — all 0-1 scaleisDefaultbooleanWhether using default values (no profile set yet)/api/users/taste-profileRequiredSet or update the user's taste profile. Accepts partial updates — only send fields you want to change.
strengthPreferencenumber0 (light) to 1 (strong)acidityTolerancenumber0 (low acid) to 1 (high acid)roastPreferencenumber0 (light roast) to 1 (dark roast)bodyVsClaritynumber0 (clarity) to 1 (body)bitternessSensitivitynumber0 (tolerant) to 1 (sensitive)explorerTypenumber0 (consistent) to 1 (adventurous)profileobjectUpdated taste profileisDefaultbooleanAlways false after setting/api/user/notification-preferencesRequiredGet the user's email notification settings.
preferencesobject{ notifyRecipeComments, notifyRecipeFavorites, notifyRecipeBrews } — all boolean/api/user/notification-preferencesRequiredUpdate notification preferences. Send only the fields you want to change.
notifyRecipeCommentsbooleanEmail when someone comments on your recipenotifyRecipeFavoritesbooleanEmail when someone saves your recipenotifyRecipeBrewsbooleanEmail when someone brews your recipe/api/users/{id}Get a public user profile with brewing statistics. Email is partially masked for privacy.
userobject{ id, displayName, memberSince }statsobject{ brewCount, savedCount, commentCount }brewLogsarrayRecent brew logs (max 20)savedRecipesarrayRecently saved recipes (max 20)commentsarrayRecent comments (max 20)Brew session tracking
Log and track individual brew sessions. Includes timing, equipment, grind settings, and extraction feedback for improving future recipes.
/api/brew-logsRequiredList the user's brew logs. Supports filtering and cursor-based pagination.
coffeeProfileIdnumberFilter by coffeeratingnumberFilter by minimum rating (1-5)savedAsRecipebooleanOnly logs saved as personal recipeslimitnumberResults per page (default: 20)cursorstringPagination cursordataarrayArray of brew log objectstotalnumberTotal matching logscursorstring|nullNext page cursorhasMorebooleanWhether more results exist/api/brew-logsRequiredLog a new brew session. Captures the full context of the brew for tracking and recipe improvement.
methodstringrequiredBrew method (V60, AeroPress, etc.)coffeeGramsnumberrequiredCoffee dose in gramswaterGramsnumberrequiredWater amount in gramsgrindSettingstringrequiredGrind setting usedcoffeeProfileIdnumberCoffee profile IDgrinderIdnumberGrinder IDmachineIdnumberMachine/brewer IDwaterTempFnumberWater temperature (Fahrenheit)brewTimeSecondsnumberTotal brew time in secondsdaysOffRoastnumberDays since roast dateoutcomestringBrew outcome: good, over-extracted, under-extracted, etc.notesstringFree-text notesextractionFeedbackobject{ sour, bitter, watery, harsh, balanced } boolean indicatorsbrewLogobjectThe created brew log/api/brew-logs/{id}RequiredGet a single brew log with full details including grinder info. Only accessible by the brew log owner.
brewLogobjectFull brew log with grinder details/api/brew-logs/{id}RequiredUpdate a brew log. Use this to add ratings, taste notes, or outcome feedback after the brew.
outcomestringUpdated outcome assessmentratingnumberRating 1-5notesstringUpdated notestasteProfileobject{ acidity, body, sweetness, bitterness } ratingsextractionFeedbackobjectUpdated extraction feedback/api/brew-logs/{id}RequiredDelete a brew log.
/api/brew-logs/lastRequiredGet the user's most recent brew log. Useful for pre-filling the brew form with last-used settings.
/api/brew-logs/weekly-statsRequiredGet the user's weekly brewing statistics with trends, insights, and brew-by-day breakdown.
statsobject{ totalBrews, totalCoffeeGrams, averageRating, streak, brewsToday, uniqueCoffees, allTimeBrews }bestBrewobject|nullHighest-rated brew this weekinsightsarrayUp to 2 insights (grind_sweet_spot, ratio_preference, streak_milestone, etc.)brewsByDayarray{ label, date, brews[] } grouped by daylastWeekobjectPrevious week stats for trend comparison/api/brew-logs/{id}/compareRequiredCompare a brew log with the user's previous brew. Shows deltas for ratio, grind, water, and brew time.
comparisonobject{ currentBrew, previousBrew, deltas: { ratio, grind, water, brewTime } }/api/brew-logs/{id}/save-as-recipeRequiredMark a brew log as a saved personal recipe. No request body needed.
brewLogobjectUpdated brew log with savedAsRecipe: trueSaved equipment & coffee library
Users can save their owned equipment and favorite coffees for quick access during recipe adaptation.
/api/user/grindersRequiredGet the user's saved grinders collection.
grindersarrayUser grinder objects with grinder details and isDefault flag/api/user/grindersRequiredAdd a grinder to the user's collection. Upserts if already present.
grinderIdnumberrequiredGrinder ID from the catalogisDefaultbooleanSet as the default grindernicknamestringCustom nickname for this grinder/api/user/grindersRequiredRemove a grinder from the user's collection.
grinderIdnumberrequiredGrinder ID to remove/api/user/grinders/calibrateRequiredSubmit grind calibration data. Map your grinder settings to the universal grind index (0-100) for accurate recipe adaptation.
grinderIdnumberrequiredGrinder ID to calibratereferencesarrayrequired[{ grindIndex: 0-100, setting: number, feedback: "TOO_FINE"|"JUST_RIGHT"|"TOO_COARSE" }]calibrationobjectSaved calibration data/api/user/machinesRequiredGet the user's saved machines/brewers collection.
machinesarrayUser machine objects with details and isDefault flag/api/user/machinesRequiredAdd a machine/brewer to the user's collection.
machineIdnumberrequiredMachine ID from the catalogisDefaultbooleanSet as the default machinenicknamestringCustom nickname/api/user/machinesRequiredRemove a machine from the user's collection.
machineIdnumberrequiredMachine ID to remove/api/user/filtersRequiredGet the user's saved filter preferences.
filtersarrayUser filter objects with isDefault flag/api/user/filtersRequiredAdd a filter to the user's saved preferences.
filterIdnumberrequiredFilter ID from the catalogisDefaultbooleanSet as default filter/api/user/filtersRequiredRemove a filter from the user's saved preferences.
filterIdnumberrequiredFilter ID to remove/api/user/coffee-libraryRequiredGet the user's coffee library with brew stats (total brews and last brewed date per coffee).
libraryarrayLibrary entries with coffee details, personalNotes, rating, brew stats/api/user/coffee-libraryRequiredAdd a coffee to the user's library.
coffeeProfileIdnumberrequiredCoffee profile IDpersonalNotesstringPersonal tasting notesratingnumberRating 1-5/api/user/coffee-libraryRequiredRemove a coffee from the user's library.
coffeeProfileIdnumberrequiredCoffee profile ID to remove/api/user/recipesRequiredGet the user's personal saved recipes with pagination.
limitnumberResults per page (default: 50, max: 100)pagenumberPage number (default: 1)recipesarrayUser recipe objects/api/user/recipesRequiredCreate a personal recipe.
methodstringrequiredBrew methodgrindSettingnumberrequiredGrind setting usedrationumberrequiredWater:coffee ratiodoseGramsnumberrequiredCoffee dose in gramswaterGramsnumberrequiredWater amount in gramsgrinderIdnumberGrinder IDcoffeeProfileIdnumberCoffee profile IDnamestringRecipe namenotesstringBrewing notes/api/user/recipes/{id}RequiredUpdate a personal recipe. Send only fields to change.
namestringRecipe namenotesstringUpdated notesratingnumberRating 1-5grindSettingnumberGrind settingrationumberUpdated ratio/api/user/recipes/{id}RequiredDelete a personal recipe.
Recipe collections
Users can organize saved community recipes into named collections for quick access by brew method, coffee type, or any personal taxonomy.
/api/collectionsRequiredList all of the user's recipe collections with item counts.
collectionsarrayCollections with _count.items/api/collectionsRequiredCreate a new recipe collection.
namestringrequiredCollection namedescriptionstringCollection descriptioncollectionobjectCreated collection object/api/collections/{id}RequiredGet a collection with all its items. Includes full recipe details (coffee, grinder, machine).
collectionobjectCollection with nested items array and full recipe details/api/collections/{id}RequiredUpdate a collection's name or description.
namestringUpdated namedescriptionstringUpdated description/api/collections/{id}RequiredDelete a collection and all its item associations.
/api/collections/{id}/itemsRequiredAdd a recipe to a collection. Returns 409 if already present.
recipeIdnumberrequiredCommunity recipe ID to additemobjectCreated collection item with recipe details/api/collections/{id}/itemsRequiredRemove a recipe from a collection.
recipeIdnumberrequiredRecipe ID to removeDiscovery endpoints
High-level discovery endpoints that combine system recommendations with community recipes, adapted for the user's equipment.
/api/discoverGet recipe suggestions for a specific grinder and optional filters. Returns one system recommendation plus up to 20 community recipes, all adapted to the user's grinder.
grinderIdnumberrequiredGrinder ID (used for grind adaptation)roaststringFilter by roast level: LIGHT, MEDIUM, DARKcoffeeIdnumberFilter by specific coffeemachineIdnumberFilter by machine/brewerbrewStrengthstringLIGHT, MEDIUM, or STRONGrecipesarrayMixed array: 1 system recommendation (id: 0) + community recipestotalnumberTotal results returnedgrinderobject{ id, brand, name } — the grinder used for adaptationGET /api/discover?grinderId=5&roast=LIGHT&limit=10
→ 200 OK
{
"recipes": [
{
"id": 0,
"type": "recommended",
"grindSetting": "22 clicks",
"grindIndex": 55,
"ratio": 16.67,
"doseGrams": 18,
"waterGrams": 300,
"isAdapted": true
},
{
"id": 42,
"type": "community",
"grindSetting": "20 clicks",
"ratio": 15.5,
"upvotes": 12,
"avgRating": 4.3,
"coffee": { "name": "Yirgacheffe", "roasterName": "Onyx" }
}
],
"total": 11,
"grinder": { "id": 5, "brand": "Comandante", "name": "C40 MK4" }
}/api/healthSystem health check. Returns database status, migration info, and latency metrics. No authentication required.
statusstring"healthy", "degraded" (>500ms latency), or "unhealthy"timestampstringISO 8601 timestampchecksobject{ database: { status, latencyMs }, migrations: { status, applied } }Embeddable recipe data
These endpoints power the embed widget and are fully CORS-enabled. See the Embed Widget Documentation for the JavaScript widget integration guide.
All embed endpoints are cached for 5 minutes at the edge with a 10-minute stale-while-revalidate window.
/api/embed/{roasterSlug}/{coffeeSlug}Fetch all brew recipes for a specific coffee. Returns roaster info, coffee details, and an array of recipes across brew methods.
roasterobject{ name, slug }coffeeobject{ name, slug, roastLevel, flavorNotes[] }recipesarray{ method, doseGrams, waterGrams, ratio, waterTempF, grindSize, brewTimeSeconds, notes, isDefault }/api/embed/track-clickTrack when a user clicks through from an embedded recipe to BrewMark.
shareIdstringrequiredEmbed identifier (roaster-slug/coffee-slug or share ID)/api/embed/track-method-switchTrack when a user switches brew methods within an embedded recipe card.
shareIdstringrequiredEmbed identifiermethodstringrequiredThe brew method selectedRate limits
POST /api/auth/register3 requests/minPOST /api/auth/login5 requests/minPOST /api/auth/apple5 requests/minPOST /api/auth/magic-link5 requests/minGET /api/auth/magic-link/verify10 requests/minPOST /api/recipes/{id}/comments10 requests/minPOST /api/community-recipes/{id}/vote30 requests/minPOST /api/community-recipes/{id}/brew-log30 requests/minPOST /api/subscribe3 requests/minPOST /api/contact3 requests/minPOST /api/demo-requests3 requests/minPOST /api/tools/grind-feedback10 requests/minAll other endpointsNo hard limit (fair use)When rate-limited, the API returns 429 Too Many Requests with a Retry-After header indicating seconds to wait before retrying.
Error responses
All error responses return a consistent JSON structure with an error field and the appropriate HTTP status code.
{
"error": "Human-readable error message"
}
// Common status codes:
// 400 — Bad Request (invalid parameters)
// 401 — Unauthorized (missing or expired token)
// 404 — Not Found
// 429 — Too Many Requests (rate limited)
// 500 — Internal Server ErrorWhen a JWT expires or is invalid, the API returns 401. Mobile clients should prompt re-authentication via Apple Sign In when receiving this status. Sessions are valid for 7 days.
Ready to integrate?
Start building with the BrewMark API. Check the embed widget docs for the easiest integration path, or use the REST API directly.