""" Read-only access to a PrestaShop store: catalogue, prices, stock, categories and policies. Failures arrive as typed UserError values inside each payload, so a client can branch on a code rather than parse prose. Pagination follows the Relay connection pattern. Nothing here writes, so every operation is safe to retry. Additive changes — new types, fields and enum members — may land at any time. A field being retired is marked with the @deprecated directive, carrying the reason and its replacement, and keeps working for at least 180 days after that. Breaking changes get a new endpoint version rather than being applied in place. Responses carry API-Version, and a retired version would additionally carry Deprecation and Sunset headers (RFC 8594, RFC 9745). """ schema { query: Query } """ Includes the field when the argument is true. """ directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT """ Omits the field when the argument is true. """ directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT """ Marks a field or enum value as retired, with the reason and its replacement. It keeps working for at least 180 days after being marked. """ directive @deprecated(reason: String) on FIELD_DEFINITION | ENUM_VALUE """ Relative weight of a field, per the GraphQL Cost Specification. Scalars and enums are free; each composite costs 1 by default. Advisory: this shop publishes weights so a client can size a query before sending it, but it does not reject queries on a cost budget — the limits that are actually enforced are the page size below, a maximum selection depth of 12, and the request rate. """ directive @cost(weight: String!) on ARGUMENT_DEFINITION | ENUM | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | OBJECT | SCALAR """ How long a list can get, so a client can bound a query before sending it. Enforced: the products connection is sliced by `first` and capped at 100. Asking for more is not an error — the page is served at the cap and the response says so in userErrors. """ directive @listSize(assumedSize: Int, slicingArguments: [String!], sizedFields: [String!], requireOneSlicingArgument: Boolean = true) on FIELD_DEFINITION """ Requests allowed per client inside a time window. Enforced: this shop allows 600 requests per minute per client IP across its agent routes, and answers HTTP 429 with a Retry-After header once that is exceeded. Read from the shop's own configuration, so this number is the one actually applied. """ directive @rateLimit(max: Int!, window: String!) on FIELD_DEFINITION | OBJECT """ Marks an operation that can be repeated with no additional effect. Every field in this schema is one: there are no mutations — mutationType is null — so nothing here writes, and repeating any query returns the same answer and changes nothing. Retry freely, including after a timeout, and carry no client-supplied mutation id, because there is no write to deduplicate. The one operation on this store that creates something is REST — POST /v1/register — and it takes an Idempotency-Key header, echoed back as Idempotency-Replayed. A schema-local convention, not a specification directive. """ directive @idempotent on FIELD_DEFINITION | OBJECT scalar String scalar Int scalar Float scalar Boolean scalar ID """ Whether a product can be bought right now. """ enum Availability { """ Orderable at the time of the request. """ IN_STOCK """ Not orderable. Stock is live, so this can change between requests. """ OUT_OF_STOCK } """ Machine-readable reason a field could not be resolved. Stable across versions: new members may be added, existing ones are not renamed. """ enum UserErrorCode { """ The requested record does not exist, or is not visible to this shop. """ NOT_FOUND """ An argument was missing, malformed or out of range. """ INVALID_ARGUMENT """ The feature is switched off on this store. """ UNAVAILABLE """ Too many requests. Read the RateLimit-* response headers and back off. """ RATE_LIMITED """ The store failed to answer. Retrying is safe; everything here is read-only. """ INTERNAL } """ Everything this store publishes for agents. Read-only throughout: no query here changes anything, so retrying is always safe. """ type Query @idempotent @rateLimit(max: 600, window: "1m") { """ Who this store is, and the size of its catalogue. """ shop: Shop! @cost(weight: "1") """ One product in full. Returns a payload rather than a nullable Product so that "not found" arrives as a typed error instead of a bare null. """ product(id: ID!): ProductPayload! @cost(weight: "2") """ The catalogue, paginated the Relay way. Walk it with `pageInfo.endCursor` and `after` rather than incrementing offsets. """ products(first: Int, after: String, query: String, inStockOnly: Boolean): ProductConnection! @cost(weight: "10") @listSize(slicingArguments: ["first"], assumedSize: 100) """ The category tree with live product counts. """ categories: CategoryPayload! @cost(weight: "3") """ Shipping, returns, privacy and terms pages. """ policies: PolicyPayload! @cost(weight: "2") } """ The store itself. """ type Shop { """ Display name. """ name: String! """ Canonical base URL. """ url: String! """ ISO 639-1 code of the language this response is in. """ language: String! """ ISO 4217 code prices are quoted in. """ currency: String! """ Active products. """ productCount: Int! """ Categories holding at least one product. """ categoryCount: Int! """ Contract version, matching the API-Version response header. """ apiVersion: String! """ How this schema evolves and how retirement is announced. """ deprecationPolicy: String! } """ A single sellable product. """ type Product { """ Numeric identifier, stable across renames. """ id: ID! """ Display name in the shop language. """ name: String! """ Plain-text description, markup stripped. """ description: String """ Canonical product page. Append `.md` for a markdown rendering. """ url: String! """ The merchant's own SKU. """ reference: String """ GTIN, EAN or ISBN where the merchant recorded one. """ gtin: String """ Manufacturer name. """ brand: String """ Current price. Null when the shop hides prices from anonymous callers. """ price: Money """ Live stock state at the moment of this request. """ availability: Availability! """ Whether the product is orderable. """ inStock: Boolean! @deprecated(reason: "Use `availability`, which can express states a boolean cannot.") """ ISO 8601 timestamp of the last edit. Use it to skip what you already hold. """ updatedAt: String } """ An amount in a currency. The amount is a decimal string, never a float — binary floating point cannot represent prices exactly. """ type Money { """ Decimal string, e.g. "19.90". """ amount: String! """ ISO 4217 code. """ currency: String! } """ A catalogue category. """ type Category { """ Numeric identifier. """ id: ID! """ Display name. """ name: String! """ Canonical category page. """ url: String! """ Depth in the tree, the root being 1. """ depth: Int! """ Parent category, absent at the root. """ parentId: ID """ Active products directly in this category. """ productCount: Int! } """ A published policy page: delivery, returns, privacy, terms. """ type Policy { """ Numeric identifier. """ id: ID! """ Page title. """ title: String! """ Canonical page URL. """ url: String! """ First paragraph, plain text. """ summary: String } """ A failure the caller can act on, carried inside the payload rather than only in the top-level `errors` array. The top-level array still reports malformed documents; this reports things that went wrong while resolving a well-formed one. """ type UserError { """ Machine-readable reason. Branch on this, not on the message. """ code: UserErrorCode! """ Path to the argument or field at fault, e.g. ["product", "id"]. """ field: [String!] """ Human-readable explanation. Wording may change; the code will not. """ message: String! } """ Result of asking for one product. """ type ProductPayload { """ The product, or null when `userErrors` explains why not. """ product: Product """ Empty on success. """ userErrors: [UserError!]! } """ Result of asking for the category tree. """ type CategoryPayload { """ Ordered by depth, then name. """ categories: [Category!]! """ Empty on success. """ userErrors: [UserError!]! } """ Result of asking for the policy pages. """ type PolicyPayload { """ Every published policy page. """ policies: [Policy!]! """ Empty on success. """ userErrors: [UserError!]! } """ A page of products, in the Relay connection shape agents already know. """ type ProductConnection { """ Nodes with their cursors. """ edges: [ProductEdge!]! """ The products themselves, when you do not need cursors. """ nodes: [Product!]! """ How to get the next page. """ pageInfo: PageInfo! """ Total matching the filter, not just this page. """ totalCount: Int! """ Empty on success. """ userErrors: [UserError!]! } """ One product and its position in the connection. """ type ProductEdge { """ Opaque. Pass it back as `after`; do not parse it. """ cursor: String! """ The product. """ node: Product! } """ Relay page information. """ type PageInfo { """ Whether another page follows. """ hasNextPage: Boolean! """ Whether a page precedes this one. """ hasPreviousPage: Boolean! """ Cursor of the first edge, null when empty. """ startCursor: String """ Cursor of the last edge. Pass as `after` for the next page. """ endCursor: String }