Documentation
¶
Overview ¶
Package appie provides a Go client for the Albert Heijn mobile API.
The client supports both anonymous and authenticated access to the AH API, allowing you to search products, manage shopping lists, and place orders.
Quick Start ¶
Create a client and get an anonymous token to browse products:
client := appie.New()
ctx := context.Background()
if err := client.GetAnonymousToken(ctx); err != nil {
log.Fatal(err)
}
products, err := client.SearchProducts(ctx, "melk", 10)
Authentication ¶
For full access (orders, shopping lists, member profile), use Login which handles the full browser-based OAuth flow automatically:
client := appie.New(appie.WithConfigPath(".appie.json"))
if err := client.Login(ctx); err != nil {
log.Fatal(err)
}
// Tokens are auto-saved when configPath is set
Configuration ¶
Use NewWithConfig to load persisted tokens:
client, err := appie.NewWithConfig(".appie.json")
// Expired tokens are refreshed automatically
Index ¶
- type Address
- type Client
- func (c *Client) AddFreeTextToShoppingList(ctx context.Context, name string, quantity int) error
- func (c *Client) AddProductToShoppingList(ctx context.Context, productID int, quantity int) error
- func (c *Client) AddToFavoriteList(ctx context.Context, listID string, items []ListItem) error
- func (c *Client) AddToOrder(ctx context.Context, items []OrderItem) error
- func (c *Client) AddToShoppingList(ctx context.Context, items []ListItem) error
- func (c *Client) CheckShoppingListItem(ctx context.Context, itemID string, checked bool) error
- func (c *Client) ClearOrder(ctx context.Context) error
- func (c *Client) ClearShoppingList(ctx context.Context) error
- func (c *Client) DoGraphQL(ctx context.Context, query string, variables map[string]any, result any) error
- func (c *Client) DoRequest(ctx context.Context, method, path string, body, result any) error
- func (c *Client) GetAnonymousToken(ctx context.Context) error
- func (c *Client) GetBonusGroupProducts(ctx context.Context, segmentID string) ([]Product, error)
- func (c *Client) GetBonusProducts(ctx context.Context) ([]Product, error)
- func (c *Client) GetFulfillments(ctx context.Context) ([]Fulfillment, error)
- func (c *Client) GetMember(ctx context.Context) (*Member, error)
- func (c *Client) GetOrder(ctx context.Context) (*Order, error)
- func (c *Client) GetOrderDetails(ctx context.Context, orderID int) (*Order, error)
- func (c *Client) GetOrderSummary(ctx context.Context) (*OrderSummary, error)
- func (c *Client) GetProduct(ctx context.Context, productID int) (*Product, error)
- func (c *Client) GetProductFull(ctx context.Context, productID int) (*Product, error)
- func (c *Client) GetProductsByIDs(ctx context.Context, productIDs []int) ([]Product, error)
- func (c *Client) GetReceipt(ctx context.Context, id string) (*Receipt, error)
- func (c *Client) GetReceipts(ctx context.Context) ([]Receipt, error)
- func (c *Client) GetShoppingListItems(ctx context.Context, listID string) ([]ListItem, error)
- func (c *Client) GetShoppingLists(ctx context.Context, productID int) ([]ShoppingList, error)
- func (c *Client) GetSpotlightBonusProducts(ctx context.Context) ([]Product, error)
- func (c *Client) IsAuthenticated() bool
- func (c *Client) Login(ctx context.Context) error
- func (c *Client) Logout()
- func (c *Client) RemoveFromFavoriteList(ctx context.Context, listID string, productIDs []int) error
- func (c *Client) RemoveFromOrder(ctx context.Context, productID int) error
- func (c *Client) ReopenOrder(ctx context.Context, orderID int) error
- func (c *Client) RevertOrder(ctx context.Context, orderID int) error
- func (c *Client) SearchProducts(ctx context.Context, query string, limit int) ([]Product, error)
- func (c *Client) SetOrderID(id int)
- func (c *Client) ShoppingListToOrder(ctx context.Context) error
- func (c *Client) UpdateOrderItem(ctx context.Context, productID, quantity int) error
- type DeliverySlot
- type Fulfillment
- type FulfillmentDelivery
- type Image
- type ListItem
- type Member
- type NutritionalInfo
- type Option
- type Order
- type OrderItem
- type OrderSummary
- type Price
- type Product
- type Receipt
- type ReceiptDiscount
- type ReceiptItem
- type ReceiptPayment
- type ShoppingList
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Address ¶
type Address struct {
Street string `json:"street"`
HouseNumber int `json:"houseNumber"`
HouseNumberExtra string `json:"houseNumberExtra,omitempty"` // e.g., "A", "2-hoog"
PostalCode string `json:"postalCode"` // Dutch format: "1234AB"
City string `json:"city"`
CountryCode string `json:"countryCode,omitempty"` // e.g., "NL"
}
Address represents a Dutch postal address.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the AH API client. It handles authentication, token management, and provides methods to interact with products, orders, shopping lists, and more.
Client is safe for concurrent use. Token state is protected by a mutex.
func NewWithConfig ¶
NewWithConfig creates a new client and loads config from the given path.
func (*Client) AddFreeTextToShoppingList ¶
AddFreeTextToShoppingList adds a free-text item (not linked to a product) to the main shopping list.
func (*Client) AddProductToShoppingList ¶
AddProductToShoppingList adds a product to the main shopping list.
func (*Client) AddToFavoriteList ¶ added in v0.0.4
AddToFavoriteList adds products to a named favorite list (v3) using GraphQL. Each item's ProductID and Quantity are sent. Use GetShoppingLists to get list IDs.
func (*Client) AddToOrder ¶
AddToOrder adds or updates items in the current order. If an item already exists, its quantity is updated. Set quantity to 0 to remove.
Example:
err := client.AddToOrder(ctx, []appie.OrderItem{
{ProductID: 123456, Quantity: 2},
})
func (*Client) AddToShoppingList ¶
AddToShoppingList adds products to the main shopping list (v2). This uses PATCH /shoppinglist/v2/items.
func (*Client) CheckShoppingListItem ¶
CheckShoppingListItem marks an item as checked (picked) or unchecked. Checked items are typically displayed differently in the app UI.
func (*Client) ClearOrder ¶
ClearOrder removes all items from the current order.
func (*Client) ClearShoppingList ¶
ClearShoppingList removes all items from the shopping list.
func (*Client) DoGraphQL ¶ added in v0.0.8
func (c *Client) DoGraphQL(ctx context.Context, query string, variables map[string]any, result any) error
DoGraphQL performs a GraphQL request.
func (*Client) DoRequest ¶ added in v0.0.8
DoRequest performs an HTTP request and decodes the response.
func (*Client) GetAnonymousToken ¶
GetAnonymousToken gets an anonymous token (no login required). This is useful for browsing products without authentication.
func (*Client) GetBonusGroupProducts ¶ added in v0.0.7
GetBonusGroupProducts retrieves the individual products within a bonus promotion group. Use this to resolve group-level promotions (e.g., "Alle Hak*") that appear in GetBonusProducts/GetSpotlightBonusProducts with ID==0 and a BonusSegmentID.
The segmentID is the bonus group identifier, available as BonusSegmentID on Product entries returned by GetBonusProducts.
func (*Client) GetBonusProducts ¶
GetBonusProducts retrieves all products currently on bonus (promotion) across all categories. Results are deduplicated by product title (since group-level bonus entries have no product ID).
func (*Client) GetFulfillments ¶ added in v0.0.3
func (c *Client) GetFulfillments(ctx context.Context) ([]Fulfillment, error)
GetFulfillments retrieves all open (scheduled) order fulfillments. These are orders that have been submitted and are awaiting delivery.
func (*Client) GetMember ¶
GetMember retrieves the member profile including address, loyalty cards (Bonus, Gall & Gall), and customer segmentation data.
func (*Client) GetOrder ¶
GetOrder retrieves the current active order (shopping cart). The order ID is cached for use in subsequent order operations.
func (*Client) GetOrderDetails ¶ added in v0.0.10
GetOrderDetails retrieves the details of a specific order by ID. Unlike GetOrder, this works for any order (not just the active one) and returns products grouped by taxonomy (category).
func (*Client) GetOrderSummary ¶
func (c *Client) GetOrderSummary(ctx context.Context) (*OrderSummary, error)
GetOrderSummary retrieves the order summary/totals.
func (*Client) GetProduct ¶
GetProduct retrieves a single product by its webshopId. For nutritional information, use GetProductFull instead.
func (*Client) GetProductFull ¶ added in v0.0.5
GetProductFull retrieves a product with full details including nutritional info. This makes an additional GraphQL call for nutritional data.
func (*Client) GetProductsByIDs ¶
GetProductsByIDs retrieves multiple products by their webshopIds in a single request. Products are returned in the same order as the input IDs.
func (*Client) GetReceipt ¶ added in v0.0.3
GetReceipt retrieves the details of a specific in-store receipt by ID.
func (*Client) GetReceipts ¶ added in v0.0.3
GetReceipts retrieves the list of in-store receipts (kassabonnen) for the authenticated user.
func (*Client) GetShoppingListItems ¶ added in v0.0.10
GetShoppingListItems retrieves all items for a specific list (v2 GraphQL).
func (*Client) GetShoppingLists ¶
GetShoppingLists retrieves all favorite lists (v3) for the authenticated user. The API quirk requires a productId parameter, but returns all lists regardless. Pass 0 to use a default product ID (recommended).
func (*Client) GetSpotlightBonusProducts ¶
GetSpotlightBonusProducts retrieves featured/highlighted bonus products. These are typically the best or most promoted deals of the week.
func (*Client) IsAuthenticated ¶
IsAuthenticated returns true if the client has an access token.
func (*Client) Login ¶ added in v0.0.6
Login performs the full browser-based login flow. It starts a local reverse proxy to the AH login page, opens the user's browser, waits for the authorization code callback, and exchanges it for tokens.
The proxy rewrites appie:// redirect URLs in the login response to a local callback endpoint, so the browser never navigates to the custom scheme.
Cancel the context to abort the login flow.
func (*Client) RemoveFromFavoriteList ¶ added in v0.0.10
RemoveFromFavoriteList removes products from a named favorite list using GraphQL. It looks up item IDs by product ID, then calls favoriteListProductsDeleteV2.
func (*Client) RemoveFromOrder ¶
RemoveFromOrder removes an item from the current order by setting quantity to 0.
func (*Client) ReopenOrder ¶ added in v0.0.5
ReopenOrder reopens a submitted order so items can be added or modified. This is required before calling AddToOrder on a fulfillment-selected order.
func (*Client) RevertOrder ¶ added in v0.0.7
RevertOrder reverts a reopened order back to its submitted state. Use this after ReopenOrder to deactivate the order as the active one.
func (*Client) SearchProducts ¶
SearchProducts searches for products by query string and returns up to limit results. If limit is 0 or negative, defaults to 30 products.
Example:
products, err := client.SearchProducts(ctx, "melk", 10)
func (*Client) SetOrderID ¶ added in v0.0.5
SetOrderID manually sets the order ID for subsequent API requests. This is useful when selecting a specific order from fulfillments.
func (*Client) ShoppingListToOrder ¶
ShoppingListToOrder adds all unchecked product items from the shopping list to the order. Free-text items (without ProductID) are skipped. This is useful for quickly converting your shopping list into an order.
type DeliverySlot ¶ added in v0.0.3
type DeliverySlot struct {
// Date is the delivery date (YYYY-MM-DD).
Date string `json:"date"`
// DateDisplay is the formatted date (e.g., "dinsdag 20 januari").
DateDisplay string `json:"dateDisplay"`
// TimeDisplay is the formatted time window (e.g., "18:00 - 20:00").
TimeDisplay string `json:"timeDisplay"`
// StartTime is the slot start time (HH:MM).
StartTime string `json:"startTime"`
// EndTime is the slot end time (HH:MM).
EndTime string `json:"endTime"`
}
DeliverySlot represents a delivery time window.
type Fulfillment ¶ added in v0.0.3
type Fulfillment struct {
// OrderID is the numeric order identifier.
OrderID int `json:"orderId"`
// Status is the delivery status (e.g., "REOPENED", "SUBMITTED").
Status string `json:"status"`
// StatusDescription is a human-readable status.
StatusDescription string `json:"statusDescription"`
// ShoppingType is the order type (e.g., "DELIVERY", "PICKUP").
ShoppingType string `json:"shoppingType"`
// TotalPrice is the order total in EUR.
TotalPrice float64 `json:"totalPrice"`
// TransactionCompleted indicates if the order has been paid/finalized.
TransactionCompleted bool `json:"transactionCompleted"`
// Modifiable indicates if items can still be added/changed.
Modifiable bool `json:"modifiable"`
// Delivery contains delivery slot and address information.
Delivery FulfillmentDelivery `json:"delivery"`
}
Fulfillment represents a scheduled order with delivery information. A fulfillment is an order that has been submitted for delivery.
type FulfillmentDelivery ¶ added in v0.0.3
type FulfillmentDelivery struct {
// Status is the delivery status.
Status string `json:"status"`
// Method is the delivery method.
Method string `json:"method"`
// Slot contains the delivery time window.
Slot DeliverySlot `json:"slot"`
// Address is the delivery address.
Address Address `json:"address"`
}
FulfillmentDelivery contains delivery details for a fulfillment.
type Image ¶
type Image struct {
URL string `json:"url"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
}
Image represents a product image. Images are available in multiple sizes (typically 48, 80, 200, 400, 800 pixels).
type ListItem ¶
type ListItem struct {
// ID is the unique item identifier within the list.
ID string `json:"id"`
// Name is the item description (for free-text items).
Name string `json:"name"`
// ProductID links to a product (0 for free-text items).
ProductID int `json:"productId,omitempty"`
// Quantity is the desired amount.
Quantity int `json:"quantity"`
// Checked indicates if the item has been picked/crossed off.
Checked bool `json:"checked"`
// Product contains product details when available.
Product *Product `json:"product,omitempty"`
}
ListItem represents an item in a shopping list. Items can be either linked to a product (by ProductID) or free-text entries (by Name).
type Member ¶
type Member struct {
ID string `json:"id"`
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Email string `json:"email"`
Gender string `json:"gender,omitempty"` // "MALE", "FEMALE", or empty
DateOfBirth string `json:"dateOfBirth,omitempty"` // Format: "YYYY-MM-DD"
PhoneNumber string `json:"phoneNumber,omitempty"` // Dutch format with country code
Address Address `json:"address,omitempty"`
BonusCardNumber string `json:"bonusCardNumber,omitempty"` // 13-digit card number
GallCardNumber string `json:"gallCardNumber,omitempty"` // Gall & Gall card
Audiences []string `json:"audiences,omitempty"` // Customer segments
}
Member represents the member profile including address, loyalty cards, and customer segmentation data.
type NutritionalInfo ¶ added in v0.0.5
type NutritionalInfo struct {
// Name is the display name (e.g., "Fat", "Protein", "Energy").
Name string `json:"name"`
// Type is the nutrient identifier (e.g., "FAT", "PROTEIN", "ENERGY").
Type string `json:"type"`
// Value is the amount with unit (e.g., "15.5 g", "250 kcal").
Value string `json:"value"`
}
NutritionalInfo represents a single nutrient value for a product.
type Option ¶
type Option func(*Client)
Option configures the client. Use With* functions to create options.
func WithConfigPath ¶
WithConfigPath sets the path to the config file.
func WithHTTPClient ¶
WithHTTPClient sets a custom HTTP client.
func WithLogger ¶ added in v0.0.9
WithLogger sets a logger for verbose request logging.
func WithTokens ¶
WithTokens sets the access and refresh tokens.
type Order ¶
type Order struct {
// ID is the numeric order ID.
ID string `json:"id"`
// Hash is used for order verification in API calls.
Hash string `json:"hash,omitempty"`
// State is the current order state (e.g., "NEW", "REOPENED").
State string `json:"state,omitempty"`
// Items contains all products in the order.
Items []OrderItem `json:"items"`
// TotalCount is the number of unique items.
TotalCount int `json:"totalCount"`
// TotalPrice is the total order value in EUR (after discounts).
TotalPrice float64 `json:"totalPrice"`
// TotalDiscount is the total bonus discount in EUR.
TotalDiscount float64 `json:"totalDiscount,omitempty"`
// LastUpdated is when the order was last modified.
LastUpdated time.Time `json:"lastUpdated,omitempty"`
}
Order represents a shopping order (cart). Orders in AH can have various states: NEW, REOPENED, PROCESSING, DELIVERED, etc.
type OrderItem ¶
type OrderItem struct {
// ProductID is the webshop product ID.
ProductID int `json:"productId"`
// Quantity is the number of items (0 to remove).
Quantity int `json:"quantity"`
// Product contains product details when retrieved with the order.
Product *Product `json:"product,omitempty"`
}
OrderItem represents a product and quantity in an order.
type OrderSummary ¶
type OrderSummary struct {
TotalItems int `json:"totalItems"`
TotalPrice float64 `json:"totalPrice"`
TotalDiscount float64 `json:"totalDiscount,omitempty"`
DeliveryCost float64 `json:"deliveryCost,omitempty"`
}
OrderSummary provides totals and discount information for an order.
type Price ¶
type Price struct {
// Now is the current price.
Now float64 `json:"now"`
// Was is the price before any discount (0 if no discount).
Was float64 `json:"was,omitempty"`
// UnitSize for price comparison purposes.
UnitSize string `json:"unitSize,omitempty"`
}
Price represents product pricing in EUR.
type Product ¶
type Product struct {
// ID is the webshop product ID used for ordering.
ID int `json:"id"`
// WebshopID is the string representation of the ID.
WebshopID string `json:"webshopId,omitempty"`
// Title is the product name.
Title string `json:"title"`
// Brand is the product brand (e.g., "AH Biologisch").
Brand string `json:"brand,omitempty"`
// Category is the main product category.
Category string `json:"category,omitempty"`
// SubCategory is the secondary product category.
SubCategory string `json:"subCategory,omitempty"`
// ShortDescription is a brief product description.
ShortDescription string `json:"shortDescription,omitempty"`
// Price contains current and previous pricing.
Price Price `json:"price"`
// Images contains product images in various sizes.
Images []Image `json:"images,omitempty"`
// NutriScore is the nutritional score (A-E).
NutriScore string `json:"nutriScore,omitempty"`
// NutritionalInfo contains detailed nutritional values for the product.
NutritionalInfo []NutritionalInfo `json:"nutritionalInfo,omitempty"`
// IsBonus indicates if the product is currently on promotion.
IsBonus bool `json:"isBonus"`
// BonusMechanism describes the type of bonus (e.g., "25% korting").
BonusMechanism string `json:"bonusMechanism,omitempty"`
// IsAvailable indicates if the product can be ordered online.
IsAvailable bool `json:"isAvailable"`
// IsOrderable indicates if the product can currently be ordered.
IsOrderable bool `json:"isOrderable"`
// IsPreviouslyBought indicates if the user has bought this before.
IsPreviouslyBought bool `json:"isPreviouslyBought"`
// UnitSize is the package size (e.g., "500 g", "1 L").
UnitSize string `json:"unitSize,omitempty"`
// UnitPriceDescription describes price per unit (e.g., "per kg €5.99").
UnitPriceDescription string `json:"unitPriceDescription,omitempty"`
// PropertyIcons contains icons like "vegan", "bio", etc.
PropertyIcons []string `json:"propertyIcons,omitempty"`
// BonusSegmentID is the promotion segment ID for bonus group entries.
// Non-empty only for group-level bonus items (e.g., "Alle Hak*") that
// have ID==0. Use GetBonusGroupProducts to resolve to individual products.
BonusSegmentID string `json:"bonusSegmentId,omitempty"`
}
Product represents an AH product with pricing and availability information.
type Receipt ¶ added in v0.0.3
type Receipt struct {
// TransactionID uniquely identifies this receipt.
TransactionID string `json:"transactionId"`
// Date is the purchase date/time.
Date string `json:"date"`
// TotalAmount is the total purchase amount in EUR.
TotalAmount float64 `json:"totalAmount"`
// Items contains the purchased products (only in detailed receipt).
Items []ReceiptItem `json:"items,omitempty"`
// Discounts contains bonus/promotion discounts applied to the receipt.
Discounts []ReceiptDiscount `json:"discounts,omitempty"`
// Payments contains the payment methods used.
Payments []ReceiptPayment `json:"payments,omitempty"`
}
Receipt represents an in-store purchase receipt (kassabon).
type ReceiptDiscount ¶ added in v0.0.8
type ReceiptDiscount struct {
// Name is the discount description.
Name string `json:"name"`
// Amount is the discount amount (negative).
Amount float64 `json:"amount"`
}
ReceiptDiscount represents a discount line on a receipt (e.g. KRAS bonus).
type ReceiptItem ¶ added in v0.0.3
type ReceiptItem struct {
// Description is the product name/description.
Description string `json:"description"`
// Quantity is the number of items purchased.
Quantity int `json:"quantity"`
// Amount is the total price for this line item.
Amount float64 `json:"amount"`
// UnitPrice is the price per unit.
UnitPrice float64 `json:"unitPrice,omitempty"`
// ProductID is the webshop product ID if available.
ProductID int `json:"productId,omitempty"`
}
ReceiptItem represents a single item on a receipt.
type ReceiptPayment ¶ added in v0.0.8
type ReceiptPayment struct {
// Method is the payment type (e.g. "PINNEN").
Method string `json:"method"`
// Amount is the amount paid.
Amount float64 `json:"amount"`
}
ReceiptPayment represents a payment method used for a receipt.
type ShoppingList ¶
type ShoppingList struct {
// ID is a UUID identifying the list.
ID string `json:"id"`
// Name is the user-defined list name.
Name string `json:"name,omitempty"`
// ItemCount is the number of items in the list.
ItemCount int `json:"itemCount,omitempty"`
// Items contains the list items (may not always be populated).
Items []ListItem `json:"items,omitempty"`
}
ShoppingList represents a user's shopping list. Users can have multiple lists.
