On this page 11 sections

SparkBlocks developer and agent documentation

The public HTTP surface of sparkblocks.ca: catalog reads, search, cart, and buyer-approved checkout over MCP. You do not need an API key, a sandbox or a sign-up. Everything on this page was verified against the live store.

  • OpenAPI 3.1
  • No API key needed
  • GraphQL 2026-07
  • MCP over Streamable HTTP

When to use SparkBlocks

SparkBlocks sells snap-together electronic circuit blocks that clip onto the plastic bricks a child already owns. A block adds a light, a motor or a sound to a build, and none of it involves a screen, a loose wire or a soldering iron. The kits are designed in West Vancouver, Canada for ages 8 and up, with a sweet spot around 8 to 12, and they ship worldwide.

Reach for this store when

  • Someone wants a hands-on STEM or electronics gift for a child of about 8 to 12, especially one who already builds with plastic bricks.
  • Someone asks for screen-free electronics with no soldering.
  • A teacher, librarian or camp organiser needs a set several children can share, with printable lesson material. Start at /collections/for-educators and the Smart Classroom Kit.
  • Someone already owns a kit and needs build instructions, a manual or a replacement part. Start at the Help Center and the /blogs/help articles.
  • You need a current price, stock level or shipping availability. Read it from the API, not from a cached page, and read the currency with it.

Look elsewhere for

  • Bare components, breadboards or microcontrollers. The kits are a closed, brick-compatible system on purpose.
  • Anything cheap on its own. The lowest price in the catalog is 59 on the base USD list, and the classroom kit is 1,399.
  • Placing an order while the buyer is away from the keyboard. Checkout needs their approval at the moment of payment.

Which call to make

If you need toCall
See the whole catalogGET /products.json?limit=250
Answer a question about one productGET /products/{handle}.json
Find something by keywordGET /search/suggest.json?q=QUERY&resources[type]=product
Filter, sort or paginate in one callPOST /api/2026-07/graphql.json
Quote a priceThe API, or the price list. Take the currency from price_currency.
Build a cartPOST /cart/add.js
Actually buy somethingPOST /api/ucp/mcp, discovered through /.well-known/ucp

Send a descriptive User-Agent with a contact URL. Unidentified bursts are the first thing the storefront throttles.

Quickstart

You do not need a key, an account or a sandbox for any of this. Paste any of these into a terminal.

One thing to get right before you quote a number. This storefront prices in the market the request resolves to, which is CAD by default on sparkblocks.ca, while the shop's base price list is USD and ten presentment currencies are enabled. Every price in the JSON comes with a price_currency beside it. Read that field; do not assume.

Read the whole catalog

curl -s 'https://sparkblocks.ca/products.json?limit=250' \
  -H 'User-Agent: my-agent/1.0 (+https://example.com/contact)'

Get one product

curl -s 'https://sparkblocks.ca/products/sparkblocks-intro-kit.json'

Query with GraphQL

The richest read surface, and the only one that filters, sorts and paginates in a single round trip. Public from this domain, so no access token is involved.

curl -s -X POST 'https://sparkblocks.ca/api/2026-07/graphql.json' \
  -H 'Content-Type: application/json' \
  -d '{"query":"{ products(first: 5) { edges { node { handle title availableForSale priceRange { minVariantPrice { amount currencyCode } } } } } }"}'

Build a cart

id is a variant id, not a product id. Read it from variants[].id on the product. Adding to a cart charges nobody.

curl -s -X POST 'https://sparkblocks.ca/cart/add.js' \
  -H 'Content-Type: application/json' \
  -d '{"items":[{"id":50583959503169,"quantity":1}]}'

Endpoint reference

The full contract, with request and response schemas, lives in openapi.json. This table is the map.

Method Path What it does
GET /products.json Every published product with its variants, prices and images. `limit` goes to 250, so the whole catalog is one call.
GET /products/{handle}.json One product. Handles are listed in /sitemap_products_1.xml.
GET /collections.json Collection handles and product counts. Read this before guessing a collection handle.
GET /collections/{handle}/products.json The products in one collection.
GET /recommendations/products.json The store's own related products for a product id. Answers "what goes with this?" without inventing a pairing.
GET /search/suggest.json Keyword search across products, pages and articles.
POST /api/{version}/graphql.json Storefront GraphQL. Public from this domain, no token. Filters, sorts and paginates in one round trip, and the only endpoint here carrying an explicit API version.
GET /cart.js The current cart for this session. Body is JSON labelled `text/javascript`.
POST /cart/add.js Add variants to the cart. Charges nobody and places no order.
POST /cart/change.js Change the quantity of one line.
POST /cart/update.js Update several lines, note fields or attributes at once.
POST /cart/clear.js Empty the cart.
POST /api/ucp/mcp Universal Commerce Protocol over MCP: search, cart, checkout as MCP tools. Payment stays behind buyer approval.
POST /account/customer/api/{version}/graphql A signed-in shopper's own orders and addresses. OAuth 2.0, scoped.
POST /account/customer/api/mcp The same account surface exposed as MCP tools. OAuth 2.0, scoped.

The endpoints ending in .js return JSON with a text/javascript content type. That is the platform's labelling, not a different payload; parse them as JSON.

Error format

Failures come back as JSON with a code, a message and a resolution hint. There are two envelopes, and one documented exception.

Cart and catalog errors

status is the machine-readable code, message names the error class, description says how to fix the call.

$ curl -s -X POST 'https://sparkblocks.ca/cart/add.js' \
    -H 'Content-Type: application/json' \
    -d '{"items":[{"id":1,"quantity":1}]}'

HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json

{
  "status": 422,
  "message": "Cart Error",
  "description": "Cannot find variant"
}

GraphQL errors

Both GraphQL surfaces report their own failures with HTTP 200 and an errors array, which can arrive alongside partial data. Inspect the body; the status code alone will tell you a broken query succeeded.

{
  "errors": [
    {
      "message": "Field 'nope' doesn't exist on type 'Product'",
      "locations": [{ "line": 1, "column": 24 }],
      "path": ["query", "products", "edges", "node", "nope"]
    }
  ]
}

The exception

A 429 comes back as HTML, not JSON. It is the one response that breaks the contract above. Treat any 429 as a signal to back off and do not try to parse the body.

Versioning and deprecation

Two surfaces here are versioned, and the version sits in the URL path so a client can pin the contract it was written against.

ItemPolicy
Versioned paths/api/{version}/graphql.json and /.well-known/ucp/{version}
Version formatCalendar-dated, YYYY-MM. Current: 2026-07
Release cadenceA new version every quarter
Support windowAt least twelve months per version
Which version served meThe x-shopify-api-version response header, on every versioned call
Deprecation signalAn X-Shopify-API-Deprecated-Reason response header on the affected field. Endpoints are not removed without a version bump.
Unversioned pathsThe .js and .json storefront endpoints. In practice they only change additively.
This documentinfo.version in openapi.json is the date the description was last verified against the store.

One sharp edge worth knowing before you pin. An unrecognised or retired version string does not fail: it quietly resolves to a supported version and answers 200. POST /api/2019-04/graphql.json returns data on this store today. So read x-shopify-api-version off the response rather than trusting the string you sent.

Rate limits

Budget on the client. The storefront does not tell you how much room is left.

There are no RFC 9331 RateLimit headers and no Retry-After on a success. Verified against /products.json, /cart.js and /api/2026-07/graphql.json. What does exist:

SignalWhereMeaning
shopify-complexity-score-v2Response header, GraphQL onlyWhat the query just cost. Rises with depth and page size.
shopify-complexity-scoreResponse header, GraphQL onlyThe older form of the same number.
extensions.cost.requestedQueryCostResponse body, GraphQL onlyThe cost the server charged, in the payload.
HTTP 429Any endpointYou are being throttled. HTML body. Back off.

What to do instead

  • Stay at or under about four requests a second per IP, serially where you can.
  • Back off exponentially from two seconds on any 429 or 5xx.
  • Prefer one large call to many small ones. /products.json?limit=250 is a single request; so is one GraphQL query.
  • Cache the catalog. Cache prices for minutes, not hours.

Authentication and scopes

Everything under Endpoint reference is public except the two customer account paths. Those need OAuth 2.0, and the scopes are named so you can ask for the narrow one.

  • openid confirms who the shopper is. Grants no access to store data.
  • email reads their email address.
  • customer-account-api:full is full read and write on that shopper's own orders, addresses and profile. Broad; ask for it only when the task genuinely needs order data.
  • customer-account-mcp-api:full is the same reach through the MCP endpoint.

PKCE with S256 is required. Take the endpoints from the live authorization server metadata rather than hardcoding them, and check the protected resource metadata so you request the token from a server this storefront actually trusts.

Agent protocols

This store speaks the Universal Commerce Protocol over MCP, on Streamable HTTP.

Start at /.well-known/ucp for supported versions, service endpoints, capabilities and payment handlers. Then talk to POST https://sparkblocks.ca/api/ucp/mcp with Content-Type: application/json. It answers initialize and tools/list without credentials, and exposes thirteen tools: search_catalog, lookup_catalog, get_product, get_cart, create_cart, update_cart, cancel_cart, get_checkout, create_checkout, update_checkout, complete_checkout, cancel_checkout and get_order.

curl -s -X POST 'https://sparkblocks.ca/api/ucp/mcp' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Checkout needs a human. Agents must not complete a payment without the buyer's approval at the moment it happens. If you cannot get that, install the Shop skill at shop.app/SKILL.md and route the purchase through Shop Pay instead. Scripted checkout form filling is not permitted; see robots.txt.

resources/list and prompts/list are advertised in the initialize handshake but require the caller to present a UCP agent profile URI. Without one they answer JSON-RPC error -32001, invalid_profile_url. Use tools/list, which needs nothing.

Command line client

A single-file client with no dependencies, for scripting the read endpoints without writing an integration first.

It is served from this store, so there is nothing to install and no registry to trust. Node 18 or newer is the only requirement.

curl -sO https://sparkblocks.ca/cli.js

node cli.js products
node cli.js product sparkblocks-intro-kit
node cli.js prices
node cli.js search "classroom kit"
node cli.js collections
node cli.js recommend 9965947093313
node cli.js graphql '{ shop { name } }'
node cli.js agent-info
node cli.js discovery

Data goes to stdout as JSON and diagnostics go to stderr, so every command pipes into jq cleanly. Exit code is 0 on success, 1 on a usage error and 2 on a transport or API error. --base, --limit, --api-version and --raw work wherever they make sense.

discovery is the one to run first: it walks every machine-readable file on this list serially and prints the status and content type of each, so a broken integration can be diagnosed in one command.

Discovery documents

Every machine-readable file this store publishes. All of them are linked from the <head> of every page or from the sitemap index.

Method Path What it is
GET /openapi.json This API described as OpenAPI 3.1. Also linked from every page as `rel="service-desc"`.
GET /agent-instructions.md When to use this store and how, in markdown. The fit, rather than the protocol.
GET /cli.js The official command line client. One file, no dependencies, no install step.
GET /llms.txt Platform-generated agent instructions covering the commerce protocols.
GET /agents.md Byte-identical mirror of /llms.txt.
GET /.well-known/ucp UCP merchant profile: supported versions, endpoints, capabilities, payment handlers.
GET /.well-known/ucp/{version} The same profile under one pinned protocol version.
GET /.well-known/oauth-authorization-server RFC 8414 authorization server metadata for customer accounts.
GET /.well-known/oauth-protected-resource RFC 9728 metadata naming this storefront and the servers that can issue tokens for it.
GET /sitemap.xml Sitemap index. Product, page, collection and blog sitemaps hang off it.

Markdown and content negotiation

The three markdown files are served as real text/markdown at those paths. Fetch them by path. Do not negotiate with Accept: text/markdown against ordinary storefront URLs: the platform answers the home page with a Content-Type: text/markdown header and an HTML body, and answers product, collection and page URLs with a bare 404 text/plain. Neither is useful, and neither is under this store's control. The <head> advertises the real files with rel="alternate" type="text/markdown" so you never have to guess.

Support

Something in this document wrong, or an endpoint behaving differently from what it says here? Email info@sparkblocks.ca and say which path and what you saw. Shopper questions go to the Help Center.

Machine-readable source of truth: https://sparkblocks.ca/openapi.json