GraphQL API¶
Vectis exposes a single GraphQL endpoint at /graphql powered by Strawberry (code-first, type-safe). All queries and mutations are composed from module resolvers via multiple inheritance in vectis/core/graphql.py.
Endpoint¶
| Environment | URL |
|---|---|
| Development | http://localhost:8000/graphql |
| Via BFF proxy | http://localhost:5174/api/graphql (storefront) or http://localhost:5173/api/graphql (admin) |
The Strawberry GraphQL IDE is available at the development URL in the browser.
Authentication¶
All requests go through the BFF layer. The SvelteKit server attaches headers:
| Header | Source | Purpose |
|---|---|---|
Authorization | Bearer {JWT} from Redis session | Authenticated user identity |
X-Session-ID | vectis_cart_session cookie | Guest cart tracking |
X-Channel-ID | Resolved by middleware | Channel context (optional) |
X-Locale | vectis_locale cookie | Content translation language |
X-Currency | vectis_currency cookie | Price display currency |
Key Queries¶
| Query | Description |
|---|---|
channelInfo | Channel configuration: commerce mode, supported languages/currencies |
me | Current authenticated user |
products(limit, offset) | Product catalog with variants |
product(id) | Single product with full variant/pricing data |
resolvePrice(variantId, quantity) | Resolved price for a variant respecting hierarchy + currency |
myCart | Current user/session cart with enriched lines |
order(id) / orders(...) | Order lookup with lines, tax, shipping, fraud status |
accountCommerceSummary(accountId, currency) | B2B account rollup: order count, revenue total, average order value, last order time. Counts and sums use orders whose status is not Cancelled or Refunded (same currency label as currency; amounts are from stored order grand_total). |
accounts(...) | Paginated B2B accounts with filters: search (company, legal name, owner email), status, creditHoldStatus, customerGroupId, taxExempt, currency, riskLevel (use __unset__ for accounts with no risk level), createdFrom / createdTo, updatedFrom / updatedTo, sortBy, sortDir, limit, offset. Returns updatedAt, customerGroup, taxId, limits, etc. |
customerGroups | All customer groups (name, slug) for admin filters and display |
customerGroup(id) | Single customer group by ID (for edit form) |
accountAdminNotes(accountId, limit, offset, includeArchived) | Staff-only notes on a B2B account. Returns { notes, total }. Requires account.update. |
quotes(accountId, status, limit, offset) | Quote list; returns { quotes, total } (not a bare array) |
exchangeRates(baseCurrency) | Active exchange rates |
discountRules(enabledOnly) | Promotion rules |
savedPaymentMethods(accountId, locationId) | Saved cards for an account, optionally scoped by location |
paymentMethods(channelId) | Configured payment gateways |
paymentTransactions(orderId) | Transaction ledger for an order |
auditLog(entityType, entityId, limit) | Audit trail entries (includes createdAt); when authenticated: requires audit.view. Ordered by created_at descending. |
notificationTemplates | Registered email templates: code, name, module, subjectTemplate, bodyTemplate, enabled, variables (JSON) |
notificationLogs(limit) | Recent delivery attempts (template code, recipient, channel, status) |
warehouses(search, activeOnly) | Warehouse list with optional search/filter; requires inventory.view |
stockLevels(warehouseId, productId, search, sortBy, sortDir, limit, offset) | Paginated stock levels enriched with SKU/product/variant names; requires inventory.view |
stockLevelsForProduct(productId, warehouseId) | All variant stock levels for a product (bulk editor); requires inventory.view |
stockAdjustmentLog(variantId, warehouseId, limit, offset) | Paginated audit trail with user email; requires inventory.view |
savedReports | Saved report definitions (SavedReport): name, reportType, JSON config, visibility (Decided #82) |
storefrontSearchConfig | Typesense host, collection, scoped browser key, plus autocomplete flags and limits (autocompleteEnabled, autocompleteMinChars, autocompleteDebounceMs, autocompleteMaxResults) loaded from search.% settings (Decided #105) |
Key Mutations¶
| Mutation | Description |
|---|---|
login(email, password) | Authenticate and receive access token |
registerCustomer(...) | B2C customer registration |
addToCart(input) | Add variant to cart with price resolution |
applyCoupon(code) | Apply coupon code to cart |
removeCoupon(code) | Remove coupon from cart |
checkout(input) | Full checkout: promotions, shipping, tax, payment, order creation |
updateOrderStatus(orderId, status) | Transition order state (triggers auto void/refund on cancel) |
modifyOrder(orderId, ...) | Modify an authorized order: add/remove/update lines with reauthorization |
adminCreateOrder(...) | Staff: create order directly from line items with saved card charging |
adminReleaseFraudHold(orderId, transactionId) | Staff: release a transaction held by gateway fraud filters |
adminCaptureTransaction(transactionId, amount) | Staff: capture an authorized transaction |
adminVoidTransaction(transactionId) | Staff: void an authorized transaction |
adminRefundTransaction(transactionId, amount, lastFour) | Staff: refund a captured transaction |
adminChargeSavedCard(savedPaymentMethodId, amount, ...) | Staff: charge a customer's saved card |
addPaymentMethod(...) | Save a new card from Accept.js opaque data |
deletePaymentMethod(id) | Delete a saved card |
setDefaultPaymentMethod(id) | Set a saved card as default |
createPaymentMethod(...) | Admin: configure a payment gateway (requires settings.edit when authenticated) |
updatePaymentMethod(id, ...) | Admin: update gateway configuration (requires settings.edit when authenticated) |
setExchangeRate(input) | Admin: set currency exchange rate |
setAccountPrice(input) | Admin: set account-level price override |
createCustomerGroup(input) | Admin: create customer group. Requires settings.edit. |
updateCustomerGroup(id, input) | Admin: update customer group name/slug. Requires settings.edit. |
deleteCustomerGroup(id) | Admin: delete customer group (blocked if accounts/customers reference it). Requires settings.edit. |
updateAccount(accountId, input) | Admin: update account fields including customerGroupId. |
addAccountAdminNote(accountId, body, attachments?) | Admin: add staff-only note to account with optional image attachments (JSON array of {url, filename, content_type, size}). Requires account.update. |
updateAccountAdminNote(noteId, body?, attachments?) | Admin: edit a note's body and/or attachments. Authors can edit within 24 hours; super admins (* permission) can edit any note at any time. Returns AccountNoteType with updatedAt and attachments. Requires account.update. |
archiveAccountAdminNote(noteId) | Admin: archive a note. Requires account.update. |
toggleChannelExtension(...) | Admin: enable/disable extension per channel |
createProduct(input) | When authenticated: requires product.create permission |
updateProduct(input) | When authenticated: requires product.update permission |
createVariant(input) | When authenticated: requires product.create permission |
createTaxCategory / createTaxRate / updateTaxRate / deleteTaxRate | When authenticated: requires settings.edit |
createPage / updatePageBlocks | When authenticated: requires settings.edit |
createSearchSynonym | When authenticated: requires settings.edit |
registerNotificationTemplate / updateNotificationTemplate / sendNotification | When authenticated: requires settings.edit |
updateNotificationPreference | When authenticated: requires settings.edit; upserts NotificationPreference for a user and category |
notificationPreferences(userId) | When authenticated: requires settings.edit; lists preferences for the given user |
subscribeToRestock(variantId, email) | Restock alerts (Decided #84): signed-in uses userId from session; guests must pass email |
unsubscribeFromRestock(variantId) | Remove restock subscription for the signed-in user (guest email subscriptions require sign-in to manage via API) |
createSalesRep / assignSalesRep | When authenticated: requires settings.edit |
issueStoreCredit(amount, reason, accountId?, customerId?, notes?, locationId?) | Issue store credit to a B2B account or B2C customer. Optional locationId scopes to a specific location (B2B only). When authenticated: requires order.update. Records created_by from the authenticated user. |
cancelStoreCredit(transactionId, reason) | Cancel an active credit transaction. Creates a reversal and decrements the balance. Requires order.update. |
redeem_gift_card(code, accountId?, customerId?, userId?, amount?) | Redeem a gift card into store credit. Supports B2B (accountId) and B2C (customerId). Derives from session context if neither is provided. |
createTag(input: CreateTagInput) | Create a tag. Requires settings.edit. Input accepts name, color, and optional applicableEntityTypes: [String] to scope the tag. |
updateTag(id, input: UpdateTagInput) | Update tag name, color, or applicableEntityTypes. Requires settings.edit. Re-derives slug on name change; rejects duplicate slugs. |
deleteTag(id) | Delete tag and all its assignments. Requires settings.edit. |
assignTag(input: {tagId, entityType, entityId}) | Assign a tag to an entity. Requires {entity}.update permission for the given entityType (see Tagging section in conventions.md). assigned_by set from session. Enforces tag scoping: if applicableEntityTypes is set on the tag, the entityType must be in the list. |
unassignTag(tagId, entityType, entityId) | Remove tag from entity. Same permission gate as assignTag. |
TagType Fields¶
| Field | Type | Description |
|---|---|---|
id | ID | Tag identifier |
name | String | Tag display name |
slug | String | URL-safe slug auto-derived from name |
color | String? | Hex color for display |
applicableEntityTypes | [String]? | Entity types this tag can be assigned to. null means all types. |
Tag Queries¶
| Query | Args | Returns | Description |
|---|---|---|---|
tags | — | [TagType] | All tags (for admin Tags page) |
tagsForEntity | entityType, entityId | [TagType] | Tags assigned to a specific entity |
tagsForEntityType | entityType: String! | [TagType] | Tags applicable to a given entity type (for tag picker dropdowns). Returns tags where applicableEntityTypes is null OR contains the given type. |
Tags on Entity Types¶
All five entity types expose a tags: [TagType] field:
| Type | tags resolved via |
|---|---|
ProductType | Batch-loaded on products() list; single-load on product() detail |
OrderType | Batch-loaded on orders() list; single-load on order() detail |
AccountType | Batch-loaded on accounts() list; single-load on account() detail |
LocationType | Batch-loaded on account() detail (nested locations) |
EmployeeType | Batch-loaded on account() detail (nested employees) |
Tag Filtering on List Queries¶
| Query | New arg | Behavior |
|---|---|---|
products(tagIds: [Int!]) | tagIds | Filter products tagged with ANY of the provided tag IDs |
orders(tagIds: [Int!]) | tagIds | Filter orders tagged with ANY of the provided tag IDs |
accounts(tagIds: [Int!]) | tagIds | Filter accounts tagged with ANY of the provided tag IDs |
AI Content Generation¶
| Query / Mutation | Description |
|---|---|
aiProviders | List installed AI providers with health status |
aiPromptTemplates | List all prompt templates (stored DB rows merged with built-in defaults). Returns isBuiltin flag |
generateAiContent(useCase, context, providerName?, maxTokens?, temperature?) | Generate content using a prompt template. Falls back to built-in defaults when no stored template exists |
upsertAiPromptTemplate(useCase, template) | Create or update a prompt template |
deleteAiPromptTemplate(useCase) | Delete a stored template. For built-in use cases, reverts to the default prompt |
Inventory (Decided #113)¶
| Mutation | Description |
|---|---|
createWarehouse(input) | Create a new warehouse. Requires inventory.manage. |
updateWarehouse(warehouseId, input) | Update warehouse fields. Requires inventory.manage. |
deactivateWarehouse(warehouseId) | Soft-deactivate a warehouse. Requires inventory.manage. |
adjustStock(variantId, warehouseId, newQuantity, reason, notes) | Set stock to an absolute quantity, writes audit log. Requires inventory.manage. |
bulkAdjustStock(input) | Batch stock updates for multiple variants. Input includes items: [{variantId, warehouseId, newQuantity}], reason, notes. Requires inventory.manage. |
importInventory(items, notes) | Import stock from CSV data. Items: [{sku, warehouseCode, quantity}]. Matches by SKU + warehouse code. Requires inventory.manage. |
Reporting (Decided #82)¶
| Mutation | Description |
|---|---|
createSavedReport | Persist a named report with reportType and JSON config (e.g. date_from, date_to). |
runReport(reportId) | Load the saved report, execute ReportExecutionService, return rows as JSON. |
exportReport(reportId, format) | Same execution; returns CSV as a string when format is csv (other formats rejected until implemented). |
createScheduledReport | Create a ScheduledReport row (schedule string, recipients, format) linked to a SavedReport. |
updateScheduledReport | Update schedule and/or enabled on a scheduled report (id argument). |
Supported reportType values: sales_summary, product_performance, customer_orders, inventory_levels, tax_collected. Order-based reports honor config.date_from and config.date_to when present.
Payment gateway configuration mutations¶
createPaymentMethod, updatePaymentMethod, and deletePaymentMethod when removing a configured gateway (PaymentMethod row) require settings.edit when the caller is authenticated. (Customer saved-card deletion may use the same field name depending on schema composition.)
Fulfillment and shipping mutations¶
Shipping configuration mutations (createShippingZone, updateShippingZone, deleteShippingZone, setZoneMethods, provider/method/box create–update–delete) require the settings.edit permission when the caller is authenticated. Shipment lifecycle mutations (createShipment, shipShipment, deliverShipment) require order.update. The shippingRatesForCheckout query is intentionally unguarded so the storefront can resolve rates without staff permissions.
Store Credit API¶
Queries¶
| Query | Args | Returns | Description |
|---|---|---|---|
storeCreditBalance | accountId?, customerId?, locationId? | StoreCreditBalanceType | Balance and transactions for a specific ledger |
storeCreditSummary | accountId?, customerId? | StoreCreditSummaryType | All ledgers for an account or customer |
myStoreCredit | (none — uses session) | StoreCreditBalanceType | Caller's own credit (storefront use). Uses account_id or customer_id from session context. |
customers | search?, limit?, offset? | [CustomerType] | List B2C customers (admin use). Search by name or email. |
Types¶
StoreCreditBalanceType:accountId,customerId,locationId,locationName,balance(MoneyType),transactions([CreditTransactionType])StoreCreditSummaryType:accountId,customerId,ledgers([StoreCreditBalanceType])CreditTransactionType:id,amount(MoneyType),type,status,reason,referenceType,referenceId,notes,createdAt,createdByName
Checkout Integration¶
The CheckoutInput accepts storeCreditAmount (String, optional). At checkout the system:
- Caps the requested amount at min(requested, balance, grandTotal)
- Debits store credit before calling the payment gateway
- Charges the remainder to the gateway (or skips it if fully covered)
- On gateway failure, compensates by refunding the debited credit back
The OrderType exposes storeCreditApplied (MoneyType) showing how much credit was used.
Checkout Mutation Input¶
The checkout mutation accepts a comprehensive input:
mutation Checkout($input: CheckoutInput!) {
checkout(input: $input) {
id
orderNumber
status
currency
paymentStatus
fraudStatus
subtotal { amount currency }
discountTotal { amount currency }
taxTotal { amount currency }
shippingTotal { amount currency }
grandTotal { amount currency }
lines {
sku
productName
quantity
lineTotal { amount currency }
taxTotal { amount currency }
discountAmount { amount currency }
}
}
}
Input fields:
| Field | Type | Description |
|---|---|---|
email | String | Required for guest checkout |
poNumber | String | B2B purchase order reference |
acceptPolicy | Boolean | Terms acceptance |
shippingAddress | AddressInput | Ship-to address |
billingAddress | AddressInput | Bill-to address |
shippingCarrier | String | Selected carrier code |
shippingMethodCode | String | Selected shipping method |
paymentMethod | String | Payment gateway code |
paymentData | JSON | Gateway-specific payload |
couponCodes | [String] | Additional coupon codes to apply at checkout |
savedPaymentMethodId | Int | Use a saved card (CIM profile) |
opaqueDataDescriptor | String | Accept.js token descriptor |
opaqueDataValue | String | Accept.js token value |
saveCard | Boolean | Save the card for future use after checkout |
storeCreditAmount | String | Amount of store credit to apply (Decimal as string). Capped at min(requested, balance, grandTotal). |
Order Type¶
The OrderType includes payment and fraud fields:
| Field | Type | Description |
|---|---|---|
paymentStatus | String | authorized, captured, held_for_review, voided, refunded, failed |
paymentCardBrand | String | Card network slug from the latest successful charge/authorization (visa, mastercard, amex, …) when the gateway stored it; null if unknown |
fraudStatus | String | null, held_for_review, released, declined |
fraudDetails | JSON | FDS filter names and details when flagged |
storeCreditApplied | MoneyType | Amount of store credit used on this order (split payment). 0 if none. |
Money Type¶
All monetary values use the MoneyType:
type MoneyType {
amount: String! # Decimal as string for precision
currency: String! # ISO 4217 code
}
Admin Cart API (Decided Cart Mgmt #2)¶
Admin cart operations require cart.view (queries) or cart.edit (mutations) permission.
Queries¶
| Query | Args | Returns | Description |
|---|---|---|---|
adminCarts | channelId, accountId, search, hasLines, sortBy, sortDir, limit, offset | AdminCartListResult | Paginated cart dashboard |
adminCart | id | AdminCartType | Full cart detail with enriched lines |
cartSnapshots | cartId, limit | [CartSnapshotType] | Snapshot timeline |
cartValidation | cartId | [CartValidationIssueType] | Non-blocking error checking |
cartAuditLog | cartId, limit | [CartAuditEntryType] | Audit trail for this cart |
Mutations¶
| Mutation | Args | Description |
|---|---|---|
adminCreateCart | channelId, accountId?, locationId?, employeeId?, customerId?, currency? | Create a new empty cart |
adminAddCartLine | cartId, variantId, quantity, managedPrice? | Add item with optional managed price |
adminUpdateCartLine | cartId, lineId, quantity | Change quantity |
adminRemoveCartLine | cartId, lineId | Remove item |
adminSetManagedPrice | cartId, lineId, price? | Set/clear managed price override |
adminApplyCoupon | cartId, code | Apply coupon code |
adminRemoveCoupon | cartId, code | Remove coupon code |
adminSetCartAddresses | cartId, shippingAddressId?, billingAddressId? | Update addresses |
adminSetCartNotes | cartId, notes | Set admin notes |
adminSetLineNotes | cartId, lineId, notes | Set per-line admin notes |
adminLockCart | cartId | Acquire editing lock (Decided #92) |
adminUnlockCart | cartId | Release editing lock |
adminRestoreCartSnapshot | cartId, snapshotId | Restore cart to snapshot state |
All mutations return AdminCartType with the updated cart state.
Registration (Decided #114)¶
Queries¶
| Query | Args | Returns | Purpose |
|---|---|---|---|
registrationForms | activeOnly?, channelId? | [RegistrationFormType] | List registration forms with steps/sections/fields |
registrationForm | id?, slug? | RegistrationFormType | Get form by ID or slug (or default) |
registrationRules | activeOnly? | [RegistrationRuleType] | List geo-conditional rules |
documentTypes | activeOnly? | [DocumentTypeType] | List document types |
agreementTemplates | activeOnly?, formId? | [AgreementTemplateType] | List agreement templates |
invitationCodes | activeOnly? | [InvitationCodeType] | List invitation codes |
registrationSubmissions | status?, formId?, limit, offset | SubmissionListResult | Paginated submissions |
registrationSubmission | id | RegistrationSubmissionType | Submission detail with docs/sigs |
evaluateRegistrationRules | country?, state?, county?, city?, postalCode?, formId? | RulesEvaluationType | Evaluate rules for a geo location |
accountDocuments | accountId | [DocumentUploadType] | Documents for a specific account |
Mutations¶
| Mutation | Args | Description |
|---|---|---|
createRegistrationForm | input: CreateRegistrationFormInput | Create a new registration form |
updateRegistrationForm | id, input: UpdateRegistrationFormInput | Update form metadata |
deleteRegistrationForm | id | Delete form and all nested structure |
saveFormStructure | formId, steps: [FormStructureStepInput] | Replace full step/section/field tree |
createRegistrationRule | input: CreateRuleInput | Create geo-conditional rule |
updateRegistrationRule | id, input: UpdateRuleInput | Update rule |
deleteRegistrationRule | id | Delete rule |
createDocumentType | input: CreateDocumentTypeInput | Create document type |
updateDocumentType | id, input: UpdateDocumentTypeInput | Update document type |
deleteDocumentType | id | Delete document type |
createAgreementTemplate | input: CreateAgreementTemplateInput | Create agreement |
updateAgreementTemplate | id, input: UpdateAgreementTemplateInput | Update agreement |
deleteAgreementTemplate | id | Delete agreement |
createInvitationCode | input: CreateInvitationCodeInput | Create invitation code |
updateInvitationCode | id, input: UpdateInvitationCodeInput | Update code |
deleteInvitationCode | id | Delete code |
reviewDocument | input: ReviewDocumentInput | Approve/reject uploaded document |
submitRegistration | input: SubmitRegistrationInput | Public: submit registration form |
reviewRegistration | input: ReviewSubmissionInput | Admin: approve/deny/request-docs |
Banner & Slider (Decided #116, #117)¶
Queries¶
| Query | Arguments | Description |
|---|---|---|
banners | activeOnly: Boolean | List all banners |
banner | id: ID! | Get single banner |
bannersByPlacement | placementType: String!, entitySlug: String | Get banners for a placement |
sliders | activeOnly: Boolean | List all sliders |
slider | id: ID! | Get single slider with slides |
Mutations¶
| Mutation | Arguments | Description |
|---|---|---|
createBanner | input: BannerInput! | Create banner |
updateBanner | id: ID!, input: BannerInput! | Update banner |
deleteBanner | id: ID! | Delete banner |
createSlider | input: SliderInput! | Create slider |
updateSlider | id: ID!, input: SliderInput! | Update slider |
deleteSlider | id: ID! | Delete slider and all slides |
addSlide | sliderId: ID!, input: SlideInput! | Add slide to slider |
updateSlide | id: ID!, input: SlideInput! | Update slide |
removeSlide | id: ID! | Remove slide |
reorderSlides | sliderId: ID!, slideIds: [ID!]! | Reorder slides |
Tracking & Attribution (Decided #118)¶
Queries¶
| Query | Arguments | Description |
|---|---|---|
trackingAnalytics | entityType, entityId?, dateFrom?, dateTo? | Aggregate analytics |
trackingEvents | entityType?, entityId?, dateFrom?, dateTo?, limit, offset | Paginated event log |
Mutations¶
| Mutation | Arguments | Description |
|---|---|---|
trackEvent | eventType, entityType, entityId, pageUrl?, referrerUrl? | Record tracking event (public) |
Email Campaigns (Decided #115)¶
Queries¶
| Query | Arguments | Description |
|---|---|---|
emailCampaigns | statusFilter?: String | List campaigns |
emailCampaign | id: ID! | Get single campaign |
Mutations¶
| Mutation | Arguments | Description |
|---|---|---|
createEmailCampaign | input: EmailCampaignInput! | Create campaign |
updateEmailCampaign | id: ID!, input: EmailCampaignInput! | Update campaign |
sendEmailCampaign | id: ID! | Send campaign |
cancelEmailCampaign | id: ID! | Cancel campaign |
deleteEmailCampaign | id: ID! | Delete campaign |
Auto-Generated Reference¶
See Reference for the full auto-generated schema documentation including all types, inputs, queries, and mutations.