Integrations›Storefront GraphQL API
The storefront API is a single GraphQL endpoint covering products, carts, orders, collections, customers, content, media, discounts, and more — everything a store needs, one store at a time. Both official SDKs are thin typed wrappers over this schema; call it directly if you need a shape the SDK doesn't expose yet.
https://apis.storentia.com/storefront/graphqlEvery field that touches store data requires auth (marked @auth in the schema below). Send either a store-scoped public token (read-mostly, safe client-side) or an OAuth app access token (full read/write) as a Bearer token — see Authentication. Fields tagged @auth(roles: ["ADMIN"])additionally require the caller's role to match.
Two custom scalars run through the whole schema: UUID and JSON (freeform metadata blobs). List endpoints that can grow large take a PaginationInput and return a PageInfo:
input PaginationInput { page: Int limit: Int} type PageInfo { total: Int! hasNextPage: Boolean! totalPages: Int!}Products own variants, options/option-values, media, and collection membership. ProductListResponse and InventoryListResponse both page through PageInfo.
id, storeIdUUID!title, descriptionString!statusACTIVE | DRAFT | ARCHIVEDoriginalPrice, sellingPriceFloat!skuStringstockInt!media, mediaIds[Media!]!, [UUID!]!collections[Collection!]!options, variants[ProductOption!]!, [ProductVariant!]!product(id: UUID!): Product@authlistProducts(storeId, status, pagination): ProductListResponse!@authlistInventory(storeId, pagination): InventoryListResponse!@authcreateProduct(input: CreateProductInput!): Product!@authupdateProduct(id, input: UpdateProductInput!): Product!@authdeleteProduct(id: UUID!): Boolean!@authcreateProductVariant / updateProductVariant / deleteProductVariant@authgenerateProductVariants(productId: UUID!): [ProductVariant!]!@authaddProductOption / updateProductOption / deleteProductOption@authaddProductOptionValue / deleteProductOptionValue@authquery { listProducts(status: ACTIVE, pagination: { page: 1, limit: 20 }) { data { id title sellingPrice stock variants { id title stock } } pageInfo { total hasNextPage } }}One cart per authenticated customer — cart always resolves the caller's own cart, there is no cart id argument. Requires a customer session, not a store-level token.
cart: Cart!@authabandonedCarts(storeId!, pagination): AbandonedCartListResponse!@authaddToCart(input: AddToCartInput!): CartItem!@authupdateCartItem(input: UpdateCartItemInput!): CartItem@authremoveFromCart(cartItemId: ID!): Boolean!@authclearCart: Boolean!@authCollections nest via parentId / subCollections and hold products as a many-to-many relationship.
collection(id: UUID!): Collection@authcollections(storeId, parentId): [Collection!]!@authcreateCollection / updateCollection / deleteCollection@authaddProductsToCollection(collectionId!, productIds!): Boolean!@authremoveProductsFromCollection(collectionId!, productIds!): Boolean!@authid, customerId, storeIdUUID!statusPENDING…DELIVERED | CANCELLED | FAILEDpaymentStatusPENDING | SUCCESS | FAILED | REFUNDEDtotalAmount, currencyFloat!, String!items[OrderItem!]!createOrder both places the order and opens a gateway checkout in one call — the response carries a Checkout alongside the order, with everything the browser needs to open the merchant's payment widget. None of it is a secret; the merchant's gateway key never leaves the platform.
provider, appIdString!gatewayOrderId, publicKeyString!amountMinorInt!currency, modeString!Once the browser has run the gateway widget, hand its callback to confirmOrderPayment— the server re-verifies the signature against the merchant's own secret before an order is ever marked paid; nothing from the browser is trusted directly. Not every gateway's browser SDK hands back a signed callback to confirm, though — Cashfree's doesn't. For those, use syncOrderPayment instead: it settles the order by asking the gateway directly for its status, with no client-supplied fields at all.
order(id: UUID!): Order@authorders(storeId, pagination): [Order!]!@authcustomerOrders(customerId!, storeId, pagination): [Order!]!@authpaymentCapability(storeId: UUID!): PaymentCapability!@authcreateOrder(input: CreateOrderInput!): OrderResponse!@authconfirmOrderPayment(input: ConfirmPaymentInput!): OrderResponse!@authsyncOrderPayment(orderId: UUID!): OrderResponse!@authupdateOrderStatus(orderId!, status!): OrderResponse!@authcancelOrder(orderId: UUID!): OrderResponse!@authCustomer auth is passwordless email + code, mirroring the account OTP flow but scoped to a store's customers rather than store owners.
sendAuthenticationEmail(input: SendAuthEmailInput!): EmailAuthResponse!verifyAuthenticationEmail(input: VerifyAuthEmailInput!): AuthResponse!logout: Boolean!@authme: Customer@authcustomerById(id: UUID!): Customer@authcustomerByEmail(email: String!): Customer@authcustomers(storeId, pagination): [Customer!]!@authcustomersByStore(pagination): [Customer!]!@auth(roles: [ADMIN])The public storefront-facing subset of a store's config — branding, contact info, and CORS settings for embedding the API in a browser app.
store(id: UUID!): Store@authupdateStore(id!, input: UpdateStoreInput!): Store!@auth(roles: [USER, ADMIN])Two lightweight CMS types, each independently paginated.
blogPost(id: UUID!): BlogPost@authlistBlogPosts(storeId, pagination): BlogPostListResponse!@authpage(id: UUID!): Page@authlistPages(storeId, pagination): PageListResponse!@authcreateBlogPost / updateBlogPost / deleteBlogPost@authcreatePage / updatePage / deletePage@authFile uploads use the Upload scalar (multipart), organized into nestable folders per store.
media(id: UUID!): Media@authfolders(storeId, parentId): [Folder!]!@authmediaInFolder(storeId, folderId): [Media!]!@authuploadMedia(storeId!, file: Upload!, folderId): Media!@authcreateFolder / moveMedia / moveFolder / renameFolder / renameMedia@authdeleteMedia(id) / deleteFolder(id): Boolean!@authPercentage or fixed-amount codes with optional min/max purchase gates, usage limits (total and per-customer), and a date window.
discountCode(id: UUID!): DiscountCode@authdiscountCodeByCode(storeId!, code!): DiscountCode@authlistDiscountCodes(storeId!, pagination): DiscountCodeListResponse!@authvalidateDiscountCode(storeId!, code!, cartTotal!): ValidateDiscountResponse@authcreateDiscountCode / updateDiscountCode / deleteDiscountCode@authSubscribe/unsubscribe are public — no token required.
subscribeToNewsletter(input!): SubscribeResponse!unsubscribeFromNewsletter(storeId, email!): UnsubscribeResponse!deleteNewsletterSubscriber(id!): Boolean!@auth(roles: [ADMIN, USER])isNewsletterSubscribed(storeId, email!): Boolean!newsLetterSubscribers(storeId, pagination): [NewsletterSubscriber!]!@auth(roles: [ADMIN, USER])Contact form submission is public; reading/managing messages is not.
createContact(input!): ContactResponse!updateContactStatus(id!, status!): ContactMessage!@authdeleteContact(id!): Boolean!@authcontacts(storeId, pagination): [ContactMessage!]!@auth(roles: [ADMIN, USER])contactById(id!): ContactMessage@auth(roles: [ADMIN, USER])Named, ordered navigation menus (LinkSet) made of Link nodes that can nest under aparentLinkId and point at a URL or an internal page.
linkSet(id: UUID!): LinkSetlinkSetsByStore(storeId!): [LinkSet!]!link(id: UUID!): LinkcreateLinkSet / updateLinkSet / deleteLinkSet@authcreateLink / updateLink / deleteLink@authA product listing query, called with a store public token:
curl https://apis.storentia.com/storefront/graphql \ -H "Authorization: Bearer spk_live_9f8e7d6c5b4a3928" \ -H "Content-Type: application/json" \ -d '{ "query": "query { listProducts(status: ACTIVE, pagination: { page: 1, limit: 5 }) { data { id title sellingPrice } pageInfo { total } } }" }'