Skip to content

Instantly share code, notes, and snippets.

@Arefu
Created March 15, 2026 21:08
Show Gist options
  • Select an option

  • Save Arefu/b94ea1942c7fa898c2e473a75c5c67cf to your computer and use it in GitHub Desktop.

Select an option

Save Arefu/b94ea1942c7fa898c2e473a75c5c67cf to your computer and use it in GitHub Desktop.
Foodstuffs North Island API
openapi: 3.0.4
info:
title: "Foodstuffs' Pak 'n Save & New World Android App API"
description: "This API is not officially supported by Foodstuffs Ltd. It has been created by reverse engineering the Foodstuffs Pak 'n Save & New World Android App. Use at your own risk."
version: "4.32.0"
servers:
- url: "https://api-prod.prod.fsniwaikato.kiwi/prod"
- url: "https://api-preprod.test.fsniwaikato.kiwi"
- url: "https://api-qa.test.fsniwaikato.kiwi"
paths:
/mobile/user/login/guest:
post:
summary: "Fetches access_token for guest user"
description: "Requires no authentication. Used to get an access_token for guest users, along with a refresh_token."
operationId: "loginGuestUser"
tags:
- Authentication.
requestBody:
required: false
description: "If this is omitted, a Token for New World will be returned instead. If you use an invalid value, the `banner` value in the Token will be what you specified."
content:
application/json:
schema:
type: object
properties:
banner:
type: string
enum:
- PNS
- MNW
responses:
"200":
description: "Successful authentication."
content:
application/json:
schema:
type: object
properties:
access_token:
type: string
token_type:
type: string
refresh_token:
type: string
expires_in:
type: integer
scope:
type: string
required:
- access_token
- token_type
- refresh_token
- expires_in
- scope
/mobile/v1/users/login/refreshtoken:
"post":
summary: "Refreshes an access_token using a refresh_token"
description: "Requires no authentication. Used to refresh an expiring access_token using a valid refresh_token."
operationId: "refreshAccessToken"
tags:
- Authentication.
parameters:
- name: User-Agent
in: header
required: true
schema:
type: string
description: "Must be set to `PAKnSAVEApp/4.32.0 (App Version)`."
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
refresh_token:
type: string
responses:
"200":
description: "Successful token refresh."
content:
application/json:
schema:
type: object
properties:
accessToken:
type: string
refreshToken:
type: string
required:
- accessToken
- refreshToken
"401":
description: "Unauthorized - Invalid or expired refresh_token."
content:
application/json:
schema:
type: object
properties:
fields:
type: object
nullable: true
status:
type: integer
example: 401
message:
type: string
example: "Refresh token expired or invalid"
code:
type: string
example: "NOT_SUPPORTED"
/mobile/v1/upgrade:
post:
summary: "Checks for app upgrade availability"
description: "Returns information about whether a newer version of the app is available."
operationId: "checkAppUpgrade"
tags:
- Misc
requestBody:
description: Version and Banner from the current App.
content:
application/json:
schema:
type: object
properties:
version:
type: string
banner:
type: string
responses:
"200":
description: It will tell you if your app version is to low to continue using the app.
content:
application/json:
schema:
type: object
properties:
latest:
type: string
action:
type: string
message:
type: string
/mobile/v1/error:
get:
summary: "Fetches error titles and their descriptions"
description: "Returns a list of error titles used by the App along with their descriptions."
operationId: "getErrorCodes"
tags:
- Misc
responses:
"200":
description: "Successful retrieval of error codes."
content:
application/json:
schema:
type: object
additionalProperties:
type: object
properties:
title:
type: string
text:
type: string
required:
- title
- text
/mobile//store/physical:
get:
summary: "Fetches physical store information"
description: "Returns a list of physical stores with their details such as name, address, and opening hours."
operationId: "getPhysicalStores"
tags:
- Store
parameters:
- name: access_token
in: header
required: true
schema:
type: string
description: "Requires either a guest user token obtained via `loginGuestUser` or an authenticated user’s access token.."
responses:
"200":
description: A JSON array of all Stores for the `banner` in your `access_token`.
content:
application/json:
schema:
type: array
items:
type: object
properties:
id:
type: string
format: uuid
name:
type: string
banner:
type: string
address:
type: string
clickAndCollect:
type: boolean
delivery:
type: boolean
latitude:
type: number
format: float
longitude:
type: number
format: float
openingHours:
type: array
items:
type: object
properties:
day:
type: string
dayShort:
type: string
open:
type: string
close:
type: string
phone:
type: string
localPhone:
type: string
linkDetails:
type: object
properties:
liquorLicenseUrl:
type: string
format: uri
physicalStoreCode:
type: string
region:
type: string
salesOrgId:
type: string
onboardingMode:
type: boolean
defaultCollectType:
type: string
enum: [CONCIERGE, COUNTER, LOCKER]
expressTimeslots:
type: boolean
expressProductLimit:
type: integer
onlineActive:
type: boolean
physicalActive:
type: boolean
servicesAndFacilities:
type: array
items:
type: string
physicalAddress:
type: object
properties:
streetName:
type: string
houseID:
type: string
cityName:
type: string
additionalCityName:
type: string
regionName:
type: string
districtName:
type: string
postalCode:
type: string
countryCode:
type: string
countryName:
type: string
deliverySubscriptionProperties:
type: object
properties:
available:
type: boolean
minimumOrderAmountInCents:
type: integer
"401":
description: "Unauthorized - Invalid or missing access token."
content:
application/json:
schema:
type: object
properties:
code:
type: string
message:
type: string
required:
- error
- error_description
/mobile/cart:
get:
summary: "Fetches the current user's shopping cart"
description: "Returns the shopping cart details for the authenticated user, it also indicates if items are unavailable at the store associated with the `authorization` Token."
operationId: "getShoppingCart"
tags:
- Cart
parameters:
- name: authorization
description: "Bearer {access_token} - Requires an authenticated user's access token."
in: header
required: true
schema:
type: string
responses:
"200":
description: "Successful retrieval of the shopping cart."
content:
application/json:
schema:
type: object
properties:
store:
type: object
properties:
storeAddress:
type: string
storeName:
type: string
storeId:
type: string
format: uuid
storeRegion:
type: string
products:
type: array
items:
type: object
properties:
productId:
type: string
quantity:
type: integer
sale_type:
type: string
price:
type: integer
name:
type: string
brand:
type: string
originStatement:
type: string
isCatered:
type: boolean
isTobacco:
type: boolean
isLiquor:
type: boolean
unavailableProducts:
type: array
items:
type: object
subtotal:
type: integer
serviceFee:
type: integer
bagFee:
type: integer
bagless:
type: boolean
promoCodeDiscount:
type: integer
deliveryPassDiscount:
type: integer
paymentDetails:
type: object
whenLastPriced:
type: string
format: date-time
clubMember:
type: boolean
/mobile/ecomm-products/{banner}/{storeId}/specials?sortOrder={sortOrder}:
post:
summary: "Fetches Products that are on sale for a specific store"
description: "Returns a list of product specials available at the specified store."
operationId: "getStoreDeals"
tags:
- Store
parameters:
- name: banner
in: path
required: true
schema:
type: string
- name: storeId
in: path
required: true
schema:
type: string
format: uuid
- name: sortOrder
in: query
required: true
schema:
type: string
- name: authorization
required: true
in: header
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: array
items:
type: object
properties:
title:
type: string
items:
type: array
items:
type: string
responses:
"200":
description: If you have specified a filter in the request body this list will only have those items.
content:
application/json:
schema:
type: object
properties:
tobaccoFiltered:
type: boolean
totalHits:
type: integer
hitsPerPage:
type: integer
numberOfPages:
type: integer
page:
type: integer
products:
type: array
items:
type: object
properties:
productId:
type: string
brand:
type: string
name:
type: string
units:
type: string
categories:
type: array
items:
type: string
price:
type: integer
unitPrice:
type: string
productImageUrls:
type: object
properties:
"100":
type: string
format: uri
"200":
type: string
format: uri
"400":
type: string
format: uri
"500":
type: string
format: uri
required: ["100", "200", "400", "500"]
decalCode:
type: string
decalImageUrl:
type: string
format: uri
availableInStore:
type: boolean
availableInOnline:
type: boolean
tobaccoFlag:
type: boolean
liquorFlag:
type: boolean
saleType:
type: string
algoliaAnalytics:
type: object
properties:
searchQueryID:
type: string
searchPosition:
type: integer
required: ["searchQueryID", "searchPosition"]
boughtBefore:
type: boolean
badgeSmallUrl:
type: string
format: uri
nullable: true
badgeMediumUrl:
type: string
format: uri
nullable: true
badgeLargeUrl:
type: string
format: uri
nullable: true
required:
- productId
- brand
- name
- units
- categories
- price
- unitPrice
- productImageUrls
- decalCode
- decalImageUrl
- availableInStore
- availableInOnline
- tobaccoFlag
- liquorFlag
- saleType
- algoliaAnalytics
- boughtBefore
filters:
type: object
properties:
Deals:
type: object
additionalProperties: false
"Dietary & lifestyle":
type: object
additionalProperties:
type: integer
Categories:
type: object
additionalProperties:
type: integer
Brands:
type: object
additionalProperties:
type: integer
required:
- Deals
- "Dietary & lifestyle"
- Categories
- Brands
required:
- tobaccoFiltered
- totalHits
- hitsPerPage
- numberOfPages
- page
- products
- filters
/mobile/v1/products/category?storeId={storeId}&banner={banner}&region={region}:
"get":
summary: "Fetches product categories for a specific store"
description: "Returns a list of product categories available at the specified store, and their nested order."
operationId: "getProductCategories"
tags:
- Store
parameters:
- name: storeId
in: query
required: true
schema:
type: string
format: uuid
- name: banner
in: query
required: true
schema:
type: string
- name: region
in: query
required: true
schema:
type: string
responses:
"200":
description: A list of grocery categories
content:
application/json:
schema:
type: array
items:
type: object
properties:
name:
type: string
code:
type: string
nullable: true
appContent:
type: object
nullable: true
properties:
panel:
type: object
properties:
title: { type: string }
imageUrl: { type: string }
subTitle: { type: string }
product:
type: object
properties:
title: { type: string }
subTitle: { type: string }
campaignUrl: { type: string }
badgeLargeUrl: { type: string }
badgeSmallUrl: { type: string }
badgeMediumUrl: { type: string }
children:
type: array
items:
type: object
properties:
name:
type: string
code:
type: string
nullable: true
appContent:
type: object
nullable: true
properties:
panel:
type: object
properties:
title: { type: string }
imageUrl: { type: string }
subTitle: { type: string }
product:
type: object
properties:
title: { type: string }
subTitle: { type: string }
campaignUrl: { type: string }
badgeLargeUrl: { type: string }
badgeSmallUrl: { type: string }
badgeMediumUrl: { type: string }
children:
type: array
items:
type: object
properties:
name:
type: string
code:
type: string
nullable: true
/mobile/ecomm-products/{banner}/{storeId}/category?cat0={cat0Name}&cat1={cat1Name}&cat2={cat2Name}&sortOrder={sortOrder}:
"get":
summary: "Fetches Products for a specific category in a specific store"
description: "Returns a list of products available in the specified categories at the specified store, you can use any combination of `cat`, if they have products it will return them."
operationId: "getProductsByCategory"
tags:
- Store
parameters:
- name: banner
in: path
required: true
schema:
type: string
- name: storeId
in: path
required: true
schema:
type: string
format: uuid
- name: cat0Name
in: query
required: false
schema:
type: string
- name: cat1Name
in: query
required: false
schema:
type: string
- name: cat2Name
in: query
required: false
schema:
type: string
- name: sortOrder
in: query
required: true
schema:
type: string
- name: authorization
required: true
in: header
schema:
type: string
format: bearer
responses:
"200":
description: A list of grocery categories
content:
application/json:
schema:
type: array
items:
type: object
properties:
name:
type: string
code:
type: string
nullable: true
appContent:
type: object
nullable: true
properties:
panel:
type: object
properties:
title: { type: string }
imageUrl: { type: string }
subTitle: { type: string }
product:
type: object
properties:
title: { type: string }
subTitle: { type: string }
campaignUrl: { type: string }
badgeLargeUrl: { type: string }
badgeSmallUrl: { type: string }
badgeMediumUrl: { type: string }
children:
type: array
description: >
Nested categories. Each child follows the same structure
as the parent (name, optional code, optional appContent,
and its own children).
items:
type: object
properties:
name:
type: string
code:
type: string
nullable: true
appContent:
type: object
nullable: true
children:
type: array
items:
type: object
properties:
name:
type: string
code:
type: string
nullable: true
#TODO: Purchase and what's returned
/mobile/previousPurchases:
"get":
summary: "Fetches the user's previous purchases"
description: "Returns a list of previous purchases made by the authenticated user. If you're a guest, this list would be assumed to be empty."
operationId: "getPreviousPurchases"
tags:
- "Cart"
parameters:
- "name": "authorization"
"description": "Bearer {access_token} - Requires an authenticated user's access token."
"in": "header"
"required": true
"schema":
"type": "string"
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
excludeCart:
type: boolean
description: Exclude cart details from the response
maximumResults:
type: integer
description: Maximum number of results to return
source:
type: string
enum: [ALL, SPECIALS, CATEGORY]
description: Source of products to fetch
required:
- excludeCart
- maximumResults
- source
responses:
"200":
description: "Successful retrieval of previous purchases."
content:
application/json:
schema:
type: object
properties:
products:
type: array
required:
- products
/mobile/ecomm-products/{banner}/{storeId}/search?q={searchItem}&tobacco={searchingTobacco}&sortOrder={sortOrder}}&disableAdsOverride{disableAdsOverride}:
"post":
summary: "Searches for Products in a specific store"
description: "Returns a list of products matching the search query at the specified store."
operationId: "searchProductsInStore"
tags:
- Store
parameters:
- name: banner
in: path
required: true
schema:
type: string
- name: storeId
in: path
required: true
schema:
type: string
format: uuid
- name: searchItem
in: query
required: true
schema:
type: string
- name: searchingTobacco
in: query
required: false
schema:
type: boolean
- name: sortOrder
in: query
required: false
schema:
type: string
- name: disableAdsOverride
in: query
required: false
schema:
type: boolean
- name: authorization
required: true
in: header
schema:
type: string
format: bearer
requestBody:
content:
application/json:
schema:
type: array
items:
type: object
example: []
responses:
"200":
description: A paginated list of products
content:
application/json:
schema:
type: object
properties:
tobaccoFiltered:
type: boolean
totalHits:
type: integer
hitsPerPage:
type: integer
numberOfPages:
type: integer
page:
type: integer
products:
type: array
items:
type: object
properties:
productId:
type: string
brand:
type: string
name:
type: string
units:
type: string
categories:
type: array
items:
type: string
price:
type: integer
unitPrice:
type: string
productImageUrls:
type: object
properties:
"100": { type: string, format: uri }
"200": { type: string, format: uri }
"400": { type: string, format: uri }
"500": { type: string, format: uri }
availableInStore:
type: boolean
availableInOnline:
type: boolean
tobaccoFlag:
type: boolean
liquorFlag:
type: boolean
saleType:
type: string
algoliaAnalytics:
type: object
properties:
searchQueryID: { type: string }
searchPosition: { type: integer }
boughtBefore:
type: boolean
required:
- tobaccoFiltered
- totalHits
- hitsPerPage
- numberOfPages
- page
- products
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment