IntegrationsStorefront GraphQL API

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.

Endpoint & auth

https://apis.storentia.com/storefront/graphql

Every 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.

Scalars & pagination

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:

shell
input PaginationInput {  page: Int  limit: Int} type PageInfo {  total: Int!  hasNextPage: Boolean!  totalPages: Int!}

Products

Products own variants, options/option-values, media, and collection membership. ProductListResponse and InventoryListResponse both page through PageInfo.

type Product
id, storeIdUUID!
title, descriptionString!
statusACTIVE | DRAFT | ARCHIVED
originalPrice, sellingPriceFloat!
skuString
stockInt!
media, mediaIds[Media!]!, [UUID!]!
collections[Collection!]!
options, variants[ProductOption!]!, [ProductVariant!]!
Query
product(id: UUID!): Product@auth
listProducts(storeId, status, pagination): ProductListResponse!@auth
listInventory(storeId, pagination): InventoryListResponse!@auth
Mutation
createProduct(input: CreateProductInput!): Product!@auth
updateProduct(id, input: UpdateProductInput!): Product!@auth
deleteProduct(id: UUID!): Boolean!@auth
createProductVariant / updateProductVariant / deleteProductVariant@auth
generateProductVariants(productId: UUID!): [ProductVariant!]!@auth
addProductOption / updateProductOption / deleteProductOption@auth
addProductOptionValue / deleteProductOptionValue@auth
shell
query {  listProducts(status: ACTIVE, pagination: { page: 1, limit: 20 }) {    data { id title sellingPrice stock variants { id title stock } }    pageInfo { total hasNextPage }  }}

Cart

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.

Query
cart: Cart!@auth
abandonedCarts(storeId!, pagination): AbandonedCartListResponse!@auth
Mutation
addToCart(input: AddToCartInput!): CartItem!@auth
updateCartItem(input: UpdateCartItemInput!): CartItem@auth
removeFromCart(cartItemId: ID!): Boolean!@auth
clearCart: Boolean!@auth

Collections

Collections nest via parentId / subCollections and hold products as a many-to-many relationship.

Query
collection(id: UUID!): Collection@auth
collections(storeId, parentId): [Collection!]!@auth
Mutation
createCollection / updateCollection / deleteCollection@auth
addProductsToCollection(collectionId!, productIds!): Boolean!@auth
removeProductsFromCollection(collectionId!, productIds!): Boolean!@auth

Orders

type Order
id, customerId, storeIdUUID!
statusPENDING…DELIVERED | CANCELLED | FAILED
paymentStatusPENDING | SUCCESS | FAILED | REFUNDED
totalAmount, 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.

type Checkout
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.

Query
order(id: UUID!): Order@auth
orders(storeId, pagination): [Order!]!@auth
customerOrders(customerId!, storeId, pagination): [Order!]!@auth
paymentCapability(storeId: UUID!): PaymentCapability!@auth
Mutation
createOrder(input: CreateOrderInput!): OrderResponse!@auth
confirmOrderPayment(input: ConfirmPaymentInput!): OrderResponse!@auth
syncOrderPayment(orderId: UUID!): OrderResponse!@auth
updateOrderStatus(orderId!, status!): OrderResponse!@auth
cancelOrder(orderId: UUID!): OrderResponse!@auth

Customers

Customer auth is passwordless email + code, mirroring the account OTP flow but scoped to a store's customers rather than store owners.

Mutation
sendAuthenticationEmail(input: SendAuthEmailInput!): EmailAuthResponse!
verifyAuthenticationEmail(input: VerifyAuthEmailInput!): AuthResponse!
logout: Boolean!@auth
Query
me: Customer@auth
customerById(id: UUID!): Customer@auth
customerByEmail(email: String!): Customer@auth
customers(storeId, pagination): [Customer!]!@auth
customersByStore(pagination): [Customer!]!@auth(roles: [ADMIN])

Store

The public storefront-facing subset of a store's config — branding, contact info, and CORS settings for embedding the API in a browser app.

Query
store(id: UUID!): Store@auth
Mutation
updateStore(id!, input: UpdateStoreInput!): Store!@auth(roles: [USER, ADMIN])

Blog posts & pages

Two lightweight CMS types, each independently paginated.

Query
blogPost(id: UUID!): BlogPost@auth
listBlogPosts(storeId, pagination): BlogPostListResponse!@auth
page(id: UUID!): Page@auth
listPages(storeId, pagination): PageListResponse!@auth
Mutation
createBlogPost / updateBlogPost / deleteBlogPost@auth
createPage / updatePage / deletePage@auth

Media & folders

File uploads use the Upload scalar (multipart), organized into nestable folders per store.

Query
media(id: UUID!): Media@auth
folders(storeId, parentId): [Folder!]!@auth
mediaInFolder(storeId, folderId): [Media!]!@auth
Mutation
uploadMedia(storeId!, file: Upload!, folderId): Media!@auth
createFolder / moveMedia / moveFolder / renameFolder / renameMedia@auth
deleteMedia(id) / deleteFolder(id): Boolean!@auth

Discount codes

Percentage or fixed-amount codes with optional min/max purchase gates, usage limits (total and per-customer), and a date window.

Query
discountCode(id: UUID!): DiscountCode@auth
discountCodeByCode(storeId!, code!): DiscountCode@auth
listDiscountCodes(storeId!, pagination): DiscountCodeListResponse!@auth
validateDiscountCode(storeId!, code!, cartTotal!): ValidateDiscountResponse@auth
Mutation
createDiscountCode / updateDiscountCode / deleteDiscountCode@auth

Newsletter

Subscribe/unsubscribe are public — no token required.

Mutation
subscribeToNewsletter(input!): SubscribeResponse!
unsubscribeFromNewsletter(storeId, email!): UnsubscribeResponse!
deleteNewsletterSubscriber(id!): Boolean!@auth(roles: [ADMIN, USER])
Query
isNewsletterSubscribed(storeId, email!): Boolean!
newsLetterSubscribers(storeId, pagination): [NewsletterSubscriber!]!@auth(roles: [ADMIN, USER])

Contact messages

Contact form submission is public; reading/managing messages is not.

Mutation
createContact(input!): ContactResponse!
updateContactStatus(id!, status!): ContactMessage!@auth
deleteContact(id!): Boolean!@auth
Query
contacts(storeId, pagination): [ContactMessage!]!@auth(roles: [ADMIN, USER])
contactById(id!): ContactMessage@auth(roles: [ADMIN, USER])

Link sets

Named, ordered navigation menus (LinkSet) made of Link nodes that can nest under aparentLinkId and point at a URL or an internal page.

Query
linkSet(id: UUID!): LinkSet
linkSetsByStore(storeId!): [LinkSet!]!
link(id: UUID!): Link
Mutation
createLinkSet / updateLinkSet / deleteLinkSet@auth
createLink / updateLink / deleteLink@auth

Full example

A product listing query, called with a store public token:

bash
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 } } }"  }'