One endpoint. Every call is POST to /graphql with header Content-Type: application/json and a body
{ "query": "...", "variables": { ... } }. The Run buttons below hit the live staging Odoo - edit the body and re-run.
Auth calls need Authorization: Bearer <token> - paste a token here after logging in:
Card shape: what the list, hover quick view and drawer preview need. Small, sparse. Has has_variants for the smart "+".
Param
Type
Notes
slugrequired
String
e.g. hayabusa-t3-boxing-gloves
locale
String
de / fr / it / en (default de)
productDetail query
Full PDP body: attribute_axes with display_type + html_color per value, all variants, precomputed variant_matrix (combo_key sorted alphabetically by attribute_slug), images, badges. Each variant carries price_tiers, the volume prices from the shop pricelist, tier 1 being the unit price; the basket applies the same tiers by quantity.
Param
Type
Notes
slugrequired
String
same as productCard
locale
String
de / fr / it / en
productList query
Cursor-paginated list of cards. next_cursor is opaque; pass it back as cursor for the next page; null on the last page.
Param
Type
Notes
category_slug
String
optional filter
locale
String
de / fr / it / en
limit
Int
default 24
cursor
String
from a previous response's next_cursor
Images (Phase 2b)
image pipeline how it works
One binary is stored once. The BFF mints one public URL per locale, each ending in the
translated product name, so a FR crawler sees gants-de-boxe.webp and a DE crawler
sees boxhandschuhe.webp for the same stored file. imgproxy only ever receives a key,
a width and a format, never a language.
URL shape: /img/{base64url(s3_key)}/{width}x{dpr}/{localized-name}.{format}
Rendered pixels are width * dpr. The trailing name is decorative and exists for SEO:
the key is authoritative, so renaming a product never breaks an indexed URL.
Formats: webp, avif, jpg, png.
Responses carry cache-control: immutable.
Field on Image
Type
Notes
src
String
resolved for the requested locale
src_i18n
I18n
one URL per locale, same binary
srcset
[ImageRendition]
1x / 2x / 3x, build url 1x, url 2x, url 3x
alt / alt_i18n
String / I18n
localized alt text
width / height
Int
intrinsic pixels, reserve the box so CLS stays 0
is_primary
Boolean
use this one for og:image
variant_id
Int
null = template level, else the variant it belongs to
All four are the same stored binary. Only the transform and the filename differ.
productDetail images query
The image array on a product, with the localized URLs and the retina ladder. Needs Odoo up.
Cart
addToCart mutation
Adds a variant. First add (no cart_token) starts a guest cart = a draft sale.order in Odoo. Blocked if over stock.
Param
Type
Notes
variant_idrequired
Int
from products
qty
Float
default 1
cart_token
String
omit to start a new cart; pass to add to an existing one
cart query
Read a cart by its token.
Param
Type
Notes
cart_tokenrequired
String
from addToCart
Checkout (Phase 4)
checkout flow how it works
One page checkout in three steps, then a hosted payment page. Totals always come from Odoo, including Swiss VAT.
1. setCheckoutAddresses with billing and optionally a separate delivery address. A guest becomes a customer record at this point.
2. shippingMethods lists the carriers that apply to this cart with the cost each adds, then setShippingMethod picks one.
3. startPayment returns a redirect_url. Send the customer there. Payrexx brings them back to the return url (the return_url you passed, or the configured default) with ?cart=<token>&outcome=success|failed|cancel.
4. paymentStatus reads the real state from Payrexx and confirms the Odoo order once. Poll it on the return page. The Payrexx webhook calls the same logic, so the order is confirmed even if the customer closes the tab.
checkout can be called at any time. missing tells the UI what is still needed, ready_for_payment gates the pay button.
Staging runs in mock mode: redirect_url opens a stand-in page with Pay, Decline and Cancel buttons instead of Payrexx.
checkout query
Current state of the checkout for a cart. Use the cart_token from addToCart.
setCheckoutAddresses mutation
Billing is required and needs an email, the confirmation goes there. Shipping is optional, leave it out to deliver to the billing address.
Param
Type
Notes
cart_token
String
from addToCart
billing
AddressInput
name, email, street, zip, city, country_code, optional phone and street2
shipping
AddressInput
optional separate delivery address
shippingMethods query
Carriers that apply to this cart and country, with the cost each would add. Free-over thresholds and margins are already applied. Rule based carriers that match no rule are left out.
setShippingMethod mutation
Adds the delivery line to the order in Odoo. Calling it again replaces the previous choice.
startPayment mutation
Creates the Payrexx gateway for the order total and returns where to send the customer. Fails with CHECKOUT_NOT_READY if anything is still missing. return_url is optional: pass the page for the customer's locale and Payrexx brings them back there; leave it out for the configured default. Only listed origins are accepted. Open redirect_url in a new tab to pay on staging.
paymentStatus query
Reads the gateway state from Payrexx and confirms the Odoo order on the first successful call. Safe to poll. paid is true for confirmed, authorized and reserved.
Auth
login mutation
Portal (res.users) authentication. Returns a stateless JWT. Pass a guest cart token to merge it into the customer on login.
Param
Type
Notes
emailrequired
String
portal login
passwordrequired
String
wrong password → AUTH_INVALID_CREDENTIALS
guest_cart_token
String
optional - merges that cart
checkEmail query
Fluid Auth step 1 - enter email first. If it exists, show the password field (Login); if new, expand Registration.
Param
Type
Notes
emailrequired
String
returns { email, exists } - one fast Odoo lookup
register mutation
Fluid Auth - new email path: creates the portal customer and logs them straight in (returns a Session, same shape as login).
Param
Type
Notes
emailrequired
String
the new login
passwordrequired
String
namerequired
String
display name
guest_cart_token
String
optional - merges the guest cart into the new account
mergeGuestCart mutationneeds token
Explicit guest → customer merge. Requires the JWT in the box at the top (auto-filled after Login).
Failures return { "errors": [...] } with extensions.code from a fixed set and a translated extensions.user_message { de, fr, it, en }. Switch on code, never on the text.
code
when
CART_STOCK_INSUFFICIENT
ordering more than available stock
CART_VARIANT_UNAVAILABLE
variant archived / inactive
AUTH_INVALID_CREDENTIALS
wrong email or password
CART_NOT_FOUND
no open cart for that token
CHECKOUT_INVALID_ADDRESS
unknown country code or missing address field
CHECKOUT_SHIPPING_UNAVAILABLE
the carrier does not apply to this cart or country
CHECKOUT_NOT_READY
startPayment before addresses and shipping are set, see missing
CHECKOUT_INVALID_RETURN_URL
return_url is not http(s) or its origin is not on the allow list
PAYMENT_FAILED
Payrexx did not accept the gateway request. retryable: true
ODOO_ACCESS_DENIED
the integration user lacks rights on a model
ODOO_UNAVAILABLE
Odoo is down or unreachable. retryable: true, so show a retry rather than a hard error