Shop - BevvyTech/BrewskiDocs GitHub Wiki
Endpoints under the /shop prefix expose management tools for the storefront/homepage experience. All routes require an authenticated bearer token. Unless otherwise noted, only super-user/support teams (platform admins) may call them. Error responses follow the global { "message": "..." } convention.
ℹ️ In addition to the private routes below,
/public/team-promotionnow surfaces the currently promoted team (see Public Catalogue).
The shop navigation registry controls which UI elements appear in the public storefront header (and future sections such as footer or sidebars). Records are keyed by section (e.g. menu.top) and item (e.g. home, basket). Entries are auto-seeded when the list endpoint is called so operators always see the full set of known toggles.
- Auth: Super-user or support team membership.
-
Response 200:
{ "items": [ { "id": "2f35a0f4-26e3-4e3e-8d30-981862c4d9d3", "section": "menu.top", "item": "home", "enabled": true, "createdAt": "2025-07-25T10:30:00.000Z", "updatedAt": "2025-07-25T10:30:00.000Z" } ] } -
Errors
-
401unauthenticated. -
403caller lacks platform (super/support) access.
-
Toggle a navigation entry on or off.
- Auth: Super-user or support team membership.
-
Body:
{ "enabled": false } -
Response 200:
{ "item": <object> }in the same shape as the list entry. -
Errors
-
400invalid payload. -
401unauthenticated. -
403caller lacks platform (super/support) access. -
404entry missing.
-
Force every beer owned by the team to be visible on the public catalogue.
-
Auth: Team owner or admin of
:teamId. - Body: none.
-
Response 200:
{ "updated": 17 }updatedis the number of beers toggled toshowPublic=true. -
Errors
-
401unauthenticated. -
403caller is not an owner/admin for the target team. -
404team missing.
-
Search teams when scheduling homepage promotions.
- Auth: Super-user or support team membership.
-
Query params
-
q(string, required, ≥2 chars) – search term. -
limit(1–25, default 10) – maximum matches.
-
-
Response 200:
{ "results": [ { "id": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7", "name": "Lantern Brewery", "logoUrl": "https://assets.brewskiapp.com/logos/d43c046a.png", "publicSlug": "lantern-brewery" } ] }
The upsell catalogue stores accessory products that can be promoted across the public shop. All routes below require super-user or support team membership. Coupler metadata uses the keg_coupler_type enum with values: d, s, g, a, u, m, keykeg, grundy, other.
List upsell records.
- Auth: Super-user or support team membership.
-
Query params
-
includeInactive(optional boolean) – include records whereisActive=false.
-
-
Response 200:
{ "upsells": [ { "id": "1a2b3c4d-1111-2222-3333-444455556666", "slug": "s-type-coupler", "name": "S-Type Keg Coupler", "description": "Stainless S-type coupler supplied with check valve.", "priceMinor": 12900, "currencySymbol": "£", "price": "£129.00", "couplerType": "s", "displayOrder": 10, "images": [ { "id": "img-01", "url": "https://assets.brewskiapp.com/upsells/1a2b3c4d/img-01-main.webp", "iconUrl": "https://assets.brewskiapp.com/upsells/1a2b3c4d/img-01-icon.webp", "isDefault": true } ] } ] }-
priceis provided when bothpriceMinorandcurrencySymbolare present; otherwise it isnull. -
imagesare ordered as stored; only one image carriesisDefault: true.
-
Fetch a single upsell. Returns the same payload shape as the list entry. Responds with 404 when the record is missing.
Create a new upsell.
- Auth: Super-user or support team membership.
-
Body (JSON):
-
name(string, required, ≤160 chars) -
slug(optional string; alphanumeric + hyphen) -
description(optional string, ≤5000 chars) -
priceMinor(optional integer, stored in minor currency units) -
currencySymbol(optional string, ≤8 chars) -
couplerType(optional enum fromkeg_coupler_type) -
isActive(optional boolean, defaults totrue) -
displayOrder(optional integer, defaults to0)
-
-
Response 201:
{ "upsell": <object> } -
Errors:
400invalid payload.
Update an upsell. Accepts any subset of the fields listed above (all optional).
- Auth: Super-user or support team membership.
-
Response 200:
{ "upsell": <object> } -
Errors:
400invalid payload,404upsell missing.
Delete an upsell and remove any stored imagery.
- Auth: Super-user or support team membership.
- Response 204: No content.
-
Errors:
404upsell missing.
Upload an image for an upsell. The service stores a 500 × 500 primary asset and a 100 × 100 icon.
- Auth: Super-user or support team membership.
-
Body:
multipart/form-data-
image(required JPEG/PNG/WEBP ≤5 MB) -
setDefault(optional flag; accepts"true","1", or"on")
-
-
Response 201:
{ "upsell": <object> }(with the refreshedimagesarray). -
Errors:
400missing file,413image too large,415unsupported content type,404upsell missing.
Mark an uploaded image as the default.
- Auth: Super-user or support team membership.
-
Response 200:
{ "upsell": <object> } -
Errors:
404upsell or image missing.
Remove an upsell image and delete the stored assets.
- Auth: Super-user or support team membership.
-
Response 200:
{ "upsell": <object> }(remaining images; if the default is deleted the first remaining image becomes default). -
Errors:
404upsell or image missing.
List all scheduled homepage promotions.
- Auth: Super-user or support team membership.
-
Response 200:
{ "promotions": [ { "id": "c97c...", "teamId": "d43c...", "startsAt": "2025-04-01T08:00:00.000Z", "imageUrl": "https://assets.brewskiapp.com/promotions/lantern.png", "imageKey": "promotion-1738079482.jpg", "imageTitle": "Lantern rooftop pour", "imageDescription": "Hazy saison poured at sunset on the taproom roof.", "mainTitle": "Featured brewery", "mainDescription": "Lantern Brewery leads our spring showcase...", "textNeedsBackground": true, "textBackground": "dark", "textColor": "white", "isDefault": false, "createdAt": "2025-02-28T15:22:17.123Z", "updatedAt": "2025-02-28T15:22:17.123Z", "team": { "id": "d43c...", "name": "Lantern Brewery", "logoUrl": "https://assets.brewskiapp.com/logos/d43c.png", "publicSlug": "lantern-brewery" }, "createdBy": { "id": "9c3b...", "name": "Sophie Brewer" } } ], "fallbackTeam": null }-
fallbackTeamechoes the oldest team (id, name, slug, logo) when no promotions exist so the UI can highlight the default selection. -
isDefaultmarks the platform fallback promotion; only one record may be default at any time. -
textBackgroundreturns one ofdark,darkdeep,light, orlightdeepwhen a background overlay is required (dark/light= 15% alpha,darkdeep/lightdeep= 85% alpha).
-
Create a new promotion.
- Auth: Super-user or support team membership.
-
Body:
multipart/form-datawith fields:-
teamId(UUID, required) -
startsAt(ISO string orYYYY-MM-DDTHH:mm, required) -
image(JPEG/PNG/WEBP file ≤5 MB, required) -
mainTitle(optional string ≤160 chars – send an empty string to clear an existing value) -
mainDescription(optional string – empty string clears the value) -
imageTitle(optional string ≤160 chars – empty string clears) -
imageDescription(optional string – empty string clears) -
textNeedsBackground(optional boolean; defaults tofalse) -
textBackground(optional string; acceptsdark,darkdeep,light, orlightdeepwhentextNeedsBackgroundis enabled.dark/lightmap to 15% overlays,darkdeep/lightdeepto 85%.) -
textColor(optional string; acceptsblack,white, orrainbow)
-
-
Response 201:
{ "promotion": <object> }(same shape as list endpoint). - When the table has no default promotion, the first created record is automatically marked as default.
-
Errors:
400missing fields,413image too large,415unsupported media type,404team not found.
Update an existing promotion.
- Auth: Super-user or support team membership.
-
Body options:
-
multipart/form-data(allows replacing the image and metadata). - JSON object containing any subset of
teamId,startsAt,mainTitle,mainDescription,imageTitle,imageDescription,textNeedsBackground,textBackground,textColor(provide""to null a text field).
-
-
Response 200:
{ "promotion": <object> }(updated record). -
Errors:
400no fields supplied,404promotion or team missing.
Promote a scheduled item to be the platform default (demoting any existing default).
- Auth: Super-user or support team membership.
-
Response 200:
{ "promotion": { "...": "..." }, "demotedPromotionIds": ["a12f..."] }-
promotionmatches the list payload withisDefault: true. -
demotedPromotionIdscontains any promotions that were previously marked as default and are now cleared.
-
-
Errors:
404when the promotion is missing.
Remove a scheduled promotion (also deletes the stored image asset).
- Auth: Super-user or support team membership.
- Response 204: No content.
-
Errors:
400when attempting to delete the default promotion,404promotion missing.
Fetch the promotion that should currently appear on the homepage. Primarily used by admin tooling.
- Auth: Super-user or support team membership.
-
Response 200:
{ "promotion": { "id": "c97c...", "teamId": "d43c...", "startsAt": "2025-04-01T08:00:00.000Z", "imageUrl": "https://assets.brewskiapp.com/promotions/lantern.png", "imageTitle": "Lantern rooftop pour", "imageDescription": "Hazy saison poured at sunset on the taproom roof.", "mainTitle": "Featured brewery", "mainDescription": "Lantern Brewery leads our spring showcase...", "textNeedsBackground": true, "textBackground": "dark", "textColor": "white", "isDefault": false, "team": { ... } } }- If no promotion is active or scheduled, the default promotion is returned (if one exists); otherwise
promotionisnull.
- If no promotion is active or scheduled, the default promotion is returned (if one exists); otherwise
List all hero records that power the top banner carousel on the public shop homepage.
- Auth: Super-user or support team membership.
-
Response 200:
{ "heroes": [ { "id": "0f6e1fb2-0fa7-4e07-b994-0a28cf6c26d0", "category": "Marketplace highlight", "title": "Discover independent breweries", "message": "Fresh releases, direct from the source. Explore what’s pouring today.", "showButton": true, "buttonText": "Browse beers", "buttonLink": "/shop", "theme": "dark", "imageUrl": "https://assets.brewskiapp.com/platform/hero-1738345600.jpg", "imageKey": "hero-1738345600.jpg", "imageWidth": 1280, "imageHeight": 720, "displaySeconds": 8, "position": 0, "createdAt": "2025-06-02T10:05:44.123Z", "updatedAt": "2025-06-02T10:05:44.123Z" } ] }- Results are ordered by
position(ascending) and thencreatedAt. Use these fields when rendering a carousel. -
themeis eitherdarkorlightand informs the storefront which contrasting text colour to pick. -
displaySecondscontrols how long the hero should remain on-screen before the next slide fades in (defaults to 8 seconds if not supplied). -
imageUrlpoints to a JPEG resized to a maximum height of 770 px; the original upload is discarded once transformed.
- Results are ordered by
Create a new hero slide.
- Auth: Super-user or support team membership.
-
Body:
multipart/form-data-
category(string ≤160 chars, required) -
title(string ≤200 chars, required) -
message(string ≤5000 chars, required) -
showButton(true/false, optional; defaults tofalse) -
displaySeconds(integer 3–120, optional; defaults to8) -
buttonText(string ≤160 chars, required whenshowButton=true) -
buttonLink(string ≤2048 chars, required whenshowButton=true, must behttp(s)://…or start with/) -
theme(darkorlight, optional; defaults todark) -
image(JPEG/PNG/WEBP file ≤8 MB, required)
-
-
Response 201:
{ "hero": <object> }(fields as described above). -
Behaviour
- New slides are appended to the end of the carousel (
positionis set to the next highest value). - When
displaySecondsis omitted the slide inherits the platform default (currently 8 s). - Uploaded images are rotated when necessary, resized to a 770 px height ceiling, and converted to progressive JPEG before storage.
- New slides are appended to the end of the carousel (
-
Errors:
-
400missing required fields or invalid button link format. -
413image exceeds 8 MB. -
415unsupported media type.
-
Update an existing hero slide.
- Auth: Super-user or support team membership.
-
Path params:
id(hero UUID). -
Body: Same
multipart/form-datacontract as thePOSTendpoint; omitimageto keep the current artwork. -
Response 200:
{ "hero": <object> }(updated record). -
Errors:
-
400missing required fields or invalid button link format. -
404hero not found. -
413image exceeds 8 MB. -
415unsupported media type.
-
Move a hero slide up or down within the carousel.
- Auth: Super-user or support team membership.
-
Path params:
id(hero UUID). -
Body:
{ "direction": "up" } // or "down" -
Response 200:
{ "heroes": [ { "...": "..." } ] }- The returned array is the updated carousel order (same shape as
GET /shop/homepage-hero).
- The returned array is the updated carousel order (same shape as
Remove a hero slide and resequence the remaining carousel positions.
- Auth: Super-user or support team membership.
-
Path params:
id(hero UUID). -
Response 200:
{ "heroes": [ /* refreshed list matching GET /shop/homepage-hero */ ] }- The response reflects the new ordering after the slide is removed and positions are compacted (
0…n-1).
- The response reflects the new ordering after the slide is removed and positions are compacted (
-
Behaviour:
- Deletes the stored hero image asset after the record is removed.
- Remaining slides keep their existing
displaySeconds; onlypositionis resequenced.
-
Errors:
-
404hero not found.
-
List the two homepage featured group slots. Each entry references a brewery team and one of its shop-visible brewery groups; the storefront consumes these records to promote collaborative buying programmes on the public homepage.
- Auth: Super-user or support team membership.
-
Response 200:
{ "placements": [ { "id": "f011b2e4-1234-4a6f-9b7e-0c1d2e3f4a5b", "position": 1, "team": { "id": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7", "name": "Lantern Brewery", "logoUrl": "https://assets.brewskiapp.com/logos/d43c046a.png", "publicSlug": "lantern-brewery" }, "group": { "id": "8cb5b1e0-4321-4af0-9d3a-0e9f7b24c6aa", "name": "Northern Alliance", "slug": "northern-alliance", "shopVisible": true, "status": "active", "imageUrl": null }, "createdAt": "2025-07-28T09:00:00.000Z", "updatedAt": "2025-07-28T09:00:00.000Z" } ] } -
Errors
-
401unauthenticated. -
403caller lacks platform (super/support) access.
-
Create or replace a featured group slot.
- Auth: Super-user or support team membership.
-
Path params
-
position(1or2) — slot index, counting from the top of the services column.
-
-
Body:
{ "teamId": "d43c046a-10a1-4f52-bd0a-9bf16f828ab7", "groupId": "8cb5b1e0-4321-4af0-9d3a-0e9f7b24c6aa" }-
teamIdmust reference an existing brewery. -
groupIdmust belong to that brewery viabrewery_group_memberswith statusactiveorinvited, and the group must haveshop_visible=true.
-
-
Response 200:
{ "placement": <object> }with the same fields asGET /shop/homepage-featured-groups. -
Errors
-
400invalid payload (missing IDs, or the selected group is not linked to the team / not shop-visible). -
401unauthenticated. -
403caller lacks platform (super/support) access. -
409the supplied group is already assigned to the other slot.
-
Clear a featured group slot.
- Auth: Super-user or support team membership.
-
Path params
-
position(1or2).
-
- Response 204: No content (slot cleared).
-
Errors
-
401unauthenticated. -
403caller lacks platform (super/support) access.
-
List the brewery groups that a given team currently belongs to (filtered to shopVisible groups). Used by the homepage editor to populate the second dropdown after selecting a brewery.
- Auth: Super-user or support team membership.
-
Path params
-
teamId— target brewery UUID.
-
-
Response 200:
{ "groups": [ { "id": "8cb5b1e0-4321-4af0-9d3a-0e9f7b24c6aa", "name": "Northern Alliance", "slug": "northern-alliance", "shopVisible": true, "status": "active", "imageUrl": null, "membershipStatus": "active" } ] } -
Errors
-
401unauthenticated. -
403caller lacks platform (super/support) access.
-