All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Because the Picnic API is unofficial and changes without notice, entries here also record shape changes that this library absorbs on your behalf.
Unreleased
Removed
- Breaking.
Picnic.order/3,Picnic.order!/3,Picnic.Resources.OrdersandPicnic.Schema.Orderare gone. They called an endpoint that does not exist in Picnic's API and never returned anything but a 404. Orders are reached two other ways: a placed order comes back under"orders"fromPicnic.delivery/3, and the cart is itself the open order, which is whyPicnic.get_cart/2returns an order-shaped document. For an order's checkout status,Picnic.request/4reaches/cart/checkout/order/{order_id}/statuswithout needing library support.
Changed
- Breaking.
Picnic.search/3now returns a flat list of products, most relevant first, instead of category groups holding their matches under"items". Picnic moved product search to a display-page endpoint that nests products at arbitrary depth among banners and headers; the presentation is dropped and only the products are returned. Fetch the untouched page withPicnic.request(client, :get, "/pages/search-page-results", params: [search_term: term])if you need the rest. - Breaking.
Picnic.Schema.SearchResultdescribes a product rather than a result group.:itemsand:linksare gone;:display_price,:price,:image_id,:unit_quantityand:decoratorstake their place. Prices are integer cents.
Fixed
Picnic.deliveries/2returned%Picnic.Error{category: :http, status: 404}for every call. The delivery list moved to a separate summary endpoint on the same API version; the verb, the status-filter body and the response shape are unchanged, so only the path differs. Note that the list has always returned delivery summaries — fields the full record carries can benil— andPicnic.delivery/3is how you get the complete record. This is now documented rather than implied.Product search returned
%Picnic.Error{category: :http, status: 404}for every query. The endpoint it used no longer exists; search now goes to the page-results endpoint on the same API version. No configuration change is needed.The two-factor handshake could not be completed as documented.
login/3returns an error rather than a client on that path, so the client callers still held carried no token and bothPicnic.Auth.generate_2fa/2andverify_2fa/2were sent unauthenticated, failing with 401. The error now carries the provisional client and both functions accept the login result directly, so no manualPicnic.Client.put_token/2step is needed.A two-factor challenge returned with a non-success HTTP status discarded the provisional token from the
x-picnic-authheader, leaving the handshake unrecoverable. The challenge is now detected before the status is classified, so both paths produce the same resumable error.The default base URL pointed at a host that does not resolve, so every client built without an explicit
:base_urlfailed with%Picnic.Error{category: :network, reason: :nxdomain}before reaching the API. Countries now resolve to their real storefronts understorefront-prod.<country>.picnicinternational.com.:nland:depreviously resolved to the same host, making:countrya no-op for URL building; they are now distinct. Clients that passed:base_urlexplicitly are unaffected.
Added
Picnic.Page.selling_units/1flattens Picnic's display-page payloads into a plain list of products, skipping tiles it does not recognise and degrading to[]with a drift warning on an unfamiliar page.Picnic.login/4takes options:send_2fa: truerequests the second-factor code as part of logging in, with:channel(default"SMS") choosing how it is delivered.Picnic.HTTP.response/4returns the response for any HTTP status, withstatus_error/1now public for callers that classify it themselves.Picnic.Clientinspects compactly as#Picnic.Client<…>with the auth token redacted, instead of dumping the wholeReqrequest template.:fras a supported:countryvalue alongside:nland:de.
0.1.0 - 2026-08-03
Initial release.
Added
Picnic.Client— the client as a plain struct, not a process.new/1resolves configuration once and stores aReq.Requesttemplate; there is nothing to start or supervise.Picnic.request/4— generic passthrough to any endpoint, including ones this library does not model. A first-class part of the public API.Picnic.Config— country, API version, base URL, client id, and default headers as data. An API version bump is a configuration change, not a code change.Picnic.Auth— login,hash_password/1(MD5, as Picnic's scheme requires), token capture from thex-picnic-authheader, and an explicit two-factor flow viagenerate_2fa/2andverify_2fa/2.Picnic.Decode— lenient decoding. Plain maps by default; opt-inPicnic.Schema.*structs that ignore unknown fields, leave missing fieldsnil, and keep the untouched payload under:raw.Picnic.Error— a categorised error struct (:network | :auth | :rate_limit | :http | :decode | :schema) so callers can match on classes of failure.- Resources for search, user, cart, orders, deliveries, and recipes, each with
a bang variant on the
Picnicfacade. Picnic.Session— optional GenServer that holds the client and re-authenticates once on an:autherror, with a pluggablePicnic.Session.TokenStoreand an in-memory default.Telemetry:
[:picnic, :request, :start | :stop | :exception]and[:picnic, :schema, :drift].- Retries for transient failures only — network errors, 5xx, and 429 with
backoff. Never for
:author other 4xx.
Notes
- The
reqdependency is declared as~> 0.7, the range the test suite runs against.