A zero dependency node module for Bring! shopping lists entirely written in TypeScript.
The developers of this module are in no way endorsed by or affiliated with Bring! Labs AG, or any associated subsidiaries, logos or trademarks.
npm install bring-shopping --production
npm run build
npm testBRING_EMAIL="you@example.com" BRING_PASSWORD="secret" npm run smokeTo allow template create/delete during smoke tests:
BRING_SMOKE_ALLOW_WRITE=1 npm run smokeThis repo includes an OpenClaw/ClawHub skill at skills/bring-shopping.
If your OpenClaw setup can load skills from a Git repo, point it at this repository and the skills/bring-shopping folder.
The skill scripts will try to load the updated API from:
BRING_NODE_API_PATH, or../../node-bring-api/build/bring.js(relative to the skill), or- the installed
bring-shoppingpackage.
const bringApi = require(`bring-shopping`);
main();
async function main () {
// provide user and email to login
const bring = new bringApi({mail: `example@example.com`, password: `secret`});
// login to get your uuid and Bearer token
try {
await bring.login();
console.log(`Successfully logged in as ${bring.name}`);
} catch (e) {
console.error(`Error on Login: ${e.message}`);
}
// get all lists and their listUuid
const lists = await bring.loadLists();
// get items of a list by its list uuid
const items = await bring.getItems('9b3ba561-02ad-4744-a737-c43k7e5b93ec');
// get translations
const translations = await bring.loadTranslations('de-DE');
} | Method | Description |
|---|---|
login() |
Authenticate with email and password |
retrieveNewAccessToken(refreshToken?) |
Refresh the access token |
| Method | Description |
|---|---|
loadLists() |
Get all shopping lists |
getItems(listUuid) |
Get all items from a list |
getItemsDetails(listUuid) |
Get detailed item information |
getAllUsersFromList(listUuid) |
Get all users sharing a list |
getListActivity(listUuid) |
Get activity timeline of a list |
| Method | Description |
|---|---|
saveItem(listUuid, itemName, specification) |
Add an item to a list |
removeItem(listUuid, itemName) |
Remove an item from a list |
moveToRecentList(listUuid, itemName) |
Mark item as purchased |
updateItem(listUuid, itemName, specification?, itemUuid?) |
Update an existing item |
completeItem(listUuid, itemName, specification?, itemUuid?) |
Mark item as completed |
batchUpdateList(listUuid, items, operation?) |
Batch update multiple items |
| Method | Description |
|---|---|
saveItemImage(itemUuid, image) |
Attach an image to an item |
removeItemImage(itemUuid) |
Remove an image from an item |
| Method | Description |
|---|---|
getUserAccount() |
Get current user account information |
getUserSettings() |
Get user settings |
getAllUserSettings() |
Alias for getting user settings and list settings |
reloadUserListSettings() |
Reload and cache list settings |
reloadArticleTranslations() |
Reload translations for required locales |
setListArticleLanguage(listUuid, language) |
Set the article language for a list |
mapUserLanguageToLocale(userLocale) |
Map user language/country to a supported locale |
doesUserExist(email?) |
Validate email and check if user exists |
| Method | Description |
|---|---|
getInspirations(filter?) |
Get recipe inspirations |
getInspirationFilters() |
Get available inspiration filters |
parseRecipe(url) |
Parse a recipe URL into a template payload |
addRecipeToList(listUuid, recipe, options?) |
Add a recipe with all ingredients to a list |
removeRecipeMarker(listUuid, recipeName) |
Remove a recipe marker from a list |
getRecipeMarkers(listUuid) |
Get all recipe markers on a list |
| Method | Description |
|---|---|
createTemplate(template, type?) |
Create a template/recipe in Bring |
deleteTemplate(templateUuid) |
Delete a template/recipe |
| Method | Description |
|---|---|
notify(listUuid, type, itemName?, ...) |
Send notification to list members |
Notification types: GOING_SHOPPING, CHANGED_LIST, SHOPPING_DONE, URGENT_MESSAGE, LIST_ACTIVITY_STREAM_REACTION
| Method | Description |
|---|---|
loadTranslations(locale) |
Load translations (e.g., 'de-DE') |
loadCatalog(locale) |
Load item catalog |
| Method | Description |
|---|---|
getPendingInvitations() |
Get pending list invitations |
// Add multiple items at once
await bring.batchUpdateList(listUuid, [
{ itemId: 'Milk', spec: '1 liter' },
{ itemId: 'Bread', spec: 'whole grain' },
{ itemId: 'Eggs', spec: '6 pieces' }
], 'TO_PURCHASE');// Notify others you're going shopping
await bring.notify(listUuid, 'GOING_SHOPPING');
// Send an urgent message
await bring.notify(listUuid, 'URGENT_MESSAGE', 'Don\'t forget the milk!');
// Notify shopping is done
await bring.notify(listUuid, 'SHOPPING_DONE');const activity = await bring.getListActivity(listUuid);
activity.timeline.forEach(entry => {
console.log(`${entry.type}: ${entry.content.items?.map(i => i.name).join(', ')}`);
});const account = await bring.getUserAccount();
console.log(`Logged in as: ${account.email} (${account.userLocale.language})`);// Add a recipe with marker and tagged items
await bring.addRecipeToList(listUuid, {
name: 'Lasagne',
items: [
{ name: 'Hackfleisch', amount: '500g' },
{ name: 'Passierte Tomaten', amount: '400ml' },
{ name: 'Lasagneplatten', amount: '1 Packung' },
{ name: 'Milch', amount: '200ml' }
]
});
// This adds to your list:
// - "=== LASAGNE ===" (marker to identify the recipe)
// - "Hackfleisch" with spec "Lasagne - 500g"
// - "Passierte Tomaten" with spec "Lasagne - 400ml"
// - etc.
// If you add another recipe that also needs milk:
await bring.addRecipeToList(listUuid, {
name: 'Pancakes',
items: [
{ name: 'Milch', amount: '300ml' },
{ name: 'Eier', amount: '3 Stück' }
]
});
// "Milch" now shows: "Lasagne - 200ml, Pancakes - 300ml"
// Customize the behavior:
await bring.addRecipeToList(listUuid, recipe, {
addMarker: false, // Don't add "=== RECIPE ===" marker
tagItems: false, // Don't add recipe name to spec
mergeExisting: false, // Overwrite existing specs instead of merging
markerFormat: '🍝 {name}' // Custom marker format
});
// Get all recipe markers on a list
const markers = await bring.getRecipeMarkers(listUuid);
// ['LASAGNE', 'PANCAKES']
// Remove a recipe marker
await bring.removeRecipeMarker(listUuid, 'Lasagne');- Added automatic token refresh with
retrieveNewAccessToken()- tokens are refreshed automatically on 401 or expiry - Added
getUserAccount()method to get user account information - Added
batchUpdateList()for efficient bulk item operations - Added
updateItem()andcompleteItem()convenience methods - Added
getListActivity()to retrieve list activity timeline - Added
getInspirations()andgetInspirationFilters()for recipe features - Added
setListArticleLanguage()to configure list language - Added
notify()method for sending notifications to list members - Added
doesUserExist()to validate email and check if user exists - Added
parseRecipe()to parse recipe URLs into template payloads - Added
createTemplate()anddeleteTemplate()for template/recipe management - Added
reloadUserListSettings()andreloadArticleTranslations()for caching - Added
mapUserLanguageToLocale()helper for locale mapping - Added new TypeScript interfaces for all new response types
- Internal refactoring: centralized request handling with
requestJson()andrequestText()
- (@foxriver76) also throw on http errors
- (@foxriver76) ported to native
fetchmodule (BREAKING: Requires Node.js 18 or above)
- (foxriver76) updated types
- (foxriver76) fixed
removeItemImageas headers were missing
- (Aliyss) added methods to link an image to an item (PR #221)
- (foxriver76) fixed typos in types (thanks to @Squawnchy)
- (foxriver76) restructure to typescript
- (foxriver76) fixed issue where error was used instead of the mssage on
getPendingInvitations
- (mdhom) added
getItemsDetailsmethod - (foxriver76) now reject with real errors instead of strings
- (foxriver76) on new call of login overwrite bearer header to allow reauth
- (foxriver76) More information on rejection of getItems
- (foxriver76) minor fix
- (foxriver76) new functionalities -> getTranslations, getCatalog and getPendingInvitations
- (foxriver76) use API version v2
- (foxriver76) minor code optimization, nothing functional
- (foxriver76) fix links in package
- (foxriver76) offical release