GraphQL schema
Schema declared in etc/schema.graphqls. Resolvers under Model/Resolver/.
ProductInterface extensions
Every product type (simple, configurable, virtual, downloadable, bundle, grouped) gains four fields:
interface ProductInterface {
preorder_enabled: Boolean
preorder_eligible: Boolean
preorder_available_from: String # ISO 8601
preorder_message: String # placeholder-substituted
}
preorder_eligible differs from preorder_enabled in that it accounts for current stock state, the global config switch, the "Allow when in stock" flag, and customer overrides. Use preorder_eligible for "should I show the pre-order UI?" and preorder_enabled for "is this product configured for pre-order at all?"
Resolvers: Model/Resolver/Product/PreorderEligibleResolver and PreorderMessageResolver.
Queries
byte8PreorderEligibility(sku: String!)
Detailed eligibility check for a specific SKU — including child SKUs of configurables.
query GetPreorderEligibility($sku: String!) {
byte8PreorderEligibility(sku: $sku) {
eligible
deposit_amount
deposit_type # PERCENT | FIXED | FULL
balance_due
available_from
message
max_qty
}
}
Returns eligible: false (and skips the rest) when the product can't be pre-ordered for any reason. Resolves customer / per-product / global override hierarchy server-side.
Resolver: Model/Resolver/PreorderEligibilityResolver.
customer.preorders
Customer-scoped — requires a customer Bearer token.
query MyPreorders {
customer {
preorders {
total_count
items {
increment_id
order_increment_id
status
deposit_paid
balance_due
currency
available_from
created_at
product {
name
url_key
sku
image { url label }
}
actions {
can_cancel
can_pay_balance
pay_balance_url
}
}
}
}
}
Resolver: Model/Resolver/CustomerPreordersResolver.
Mutations
byte8CompletePreorder(preorder_id: Int!)
Triggers manual completion — equivalent to the customer clicking "Pay balance" on the My Pre-orders page. Returns the URL to redirect them to (the completion checkout).
mutation CompletePreorder($id: Int!) {
byte8CompletePreorder(preorder_id: $id) {
success
redirect_url
completion_order_increment_id
}
}
Resolver: Model/Resolver/CompletePreorderResolver.
byte8CancelPreorder(preorder_id: Int!)
Customer-initiated cancellation, subject to status / authority checks. Reverts MSI reservation, refunds deposit if configured.
mutation CancelPreorder($id: Int!) {
byte8CancelPreorder(preorder_id: $id) {
success
refund_amount
}
}
Authentication
| Operation | Auth |
|---|---|
ProductInterface fields | None — public, cacheable |
byte8PreorderEligibility | None — public, but per-customer overrides need a token to apply |
customer.preorders | Customer token required |
byte8CompletePreorder | Customer token required, must own the pre-order |
byte8CancelPreorder | Customer token required, must own the pre-order |
Caching
ProductInterface extensions follow Magento's standard product cache tags — pre-order eligibility is invalidated when the product is saved, when stock changes for that SKU, or when global config flips.
byte8PreorderEligibility is uncached by default — it's hit on PDP loads and needs to be live with stock state. If your traffic warrants caching, add a small cache layer in your storefront (Apollo client, Next.js fetch cache) keyed on SKU + stock-id.
customer.preorders is uncacheable — different per customer, changes on every action.
REST equivalents
For non-GraphQL clients, REST endpoints are declared in etc/webapi.xml:
GET /V1/byte8-preorder/eligibility/:sku— public eligibility checkGET /V1/customers/me/preorders— customer-scoped listingPOST /V1/byte8-preorder/:id/complete— trigger completionPOST /V1/byte8-preorder/:id/cancel— cancel
Same resolvers behind both surfaces.
Related
- VelaFront / headless integration — higher-level walkthrough.
- Customer account — what the equivalent Luma UI looks like.