Headdy for Craft CMS

Craft Commerce 5

Commerce, minus the head

Craft Commerce exposes products over GraphQL but has never shipped a single cart mutation. A headless store ends up posting form-encoded requests with a session cookie, a CSRF token and credentials: 'include', then gets stuck at payment, because Craft only accepts a return URL it hashed into a Twig form itself. Headdy is the storefront API that was missing.

Headdy

One request, one cart

The first add-to-cart creates the cart and hands back a token. Send that token as a header from then on. No cookie, no CSRF round trip, and every amount is an object, never a float.

javascript
const res = await fetch(`${API}/carts`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', 'X-Headdy-Key': KEY },
  body: JSON.stringify({ items: [{ purchasableId: 123, qty: 2 }] }),
});

const { cart } = await res.json();
cart.token;                    // "hdc_…" — send back as X-Headdy-Cart
cart.totals.total;             // { amount: "49.98", minorUnits: 4998,
                               //   currency: "USD", formatted: "$49.98" }

Features

Everything a storefront calls, from the first add-to-cart to a paid order, with one service behind all of it.

Carts over REST

Create, patch and empty carts; add, update and remove line items; set addresses, email, coupon and shipping, in one PATCH or six small calls, with the same result either way.

  • Changes apply in a fixed order, so a coupon never misses its minimum
  • A failed change still returns the cart, so the UI can re-render

Checkout that tells you what's missing

One GET returns what checkout still needs, the shipping methods and gateways that can supply it, and the store's own rules, so the front end never finds them out by trial and error.

Payment that works headlessly

Any Commerce gateway, including off-site ones. Their redirect comes back as JSON, with a method and form data, instead of a 302 your fetch can't follow.

  • Return URLs checked against an allow-list, not a Twig hash
  • The list starts empty, so nothing redirects until you allow it

Bearer tokens, stored as hashes

Cart and customer tokens are stored only as SHA-256 hashes, so a database dump yields nothing usable. Keys carry scopes, origins and rate limits of their own.

GraphQL cart mutations

Eleven mutations and two queries on Craft's own GraphQL endpoint, only on schemas you grant them to. They resolve through the same service as REST, so the two can never drift.

Customer accounts

Sign-in, registration, order history, an address book and saved cards. A customer token never logs anyone into Craft, and control panel accounts are refused at the door.

  • Wrong passwords count toward Craft's own lockout
  • Registration honours Craft's email verification

Webhooks and a request log

Signed, queued webhooks for cart, order and payment events, and a log of every API request, so you can see exactly what a front end sent.

A setup check that gates a deploy

The Overview screen and a console command flag the misconfiguration before your front-end team does: no key, no gateway, an empty redirect list, an open CORS policy.

Headdy in the control panel

Real screens from a Craft 5 install running Commerce: the setup check, the request log, an API key and a webhook endpoint.

The Headdy Overview screen showing the storefront API URL, a configuration check with two warnings about open CORS and an empty redirect list, and request counts for the last 24 hours
The Headdy request log listing nineteen API requests with their method, path, status and error code, the API key that made each one, and its response time
Editing the Next.js storefront API key in Headdy: its public key, seven scopes, an origin restriction, a rate limit, an optional expiry and a switch to rotate the secret
Editing a Headdy webhook endpoint subscribed to the order.paid, order.statusChanged and payment.failed topics, with its signing secret

Screenshots from a live install, not mockups.

Or the same cart over GraphQL

Grant a schema the Headdy storefront component and the cart mutations appear on Craft's own GraphQL endpoint. Pass the cart token as an argument. Payment stays on REST, because a gateway redirect needs a real HTTP response.

graphql
mutation {
  headdyCartAddItem(cartToken: "hdc_…", purchasableId: 456, qty: 2) {
    totalQty
    lineItems { description qty total { formatted } }
    totals { total { formatted } }
  }
}

query {
  headdyCheckout(cartToken: "hdc_…") {
    ready
    missing          # ["shippingMethod"]
    amountDue { formatted }
  }
}

Frequently Asked Questions

The questions worth answering before you install it.

Two editions, both of them complete

Lite is $99 with a $79/year renewal and runs a complete guest checkout on its own: carts, shipping, coupons, checkout and payment through any gateway. Pro is $199 with a $159/year renewal, and adds customer accounts, GraphQL cart mutations, webhooks and the request log.