Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.vaultgraph.com/docs/llms.txt

Use this file to discover all available pages before exploring further.

Status: v1.0 (stable). Backwards-incompatible changes will bump the major version. This document is the canonical reference for merchants implementing a VaultGraph commerce backend over HTTPS. It defines the request/response envelope, signing scheme, error model, idempotency contract, retry/timeout behavior, and pagination convention. The @vaultgraph/sdk/adapter helper wraps everything below in a single typed factory.

1. Transport

  • Direction: VaultGraph → merchant. The merchant exposes one HTTPS endpoint; the gateway calls it.
  • Method: POST.
  • URL shape: {endpoint_url}/{method} — the configured endpoint_url joined with the method name as a single path segment. The path prefix is the merchant’s choice — VaultGraph does not reserve or require any particular name. Example: https://shop.example.com/webhooks/vaultgraph/createCheckout.
  • TLS: required. The gateway will not call plain http:// endpoints.
  • Content-Type: application/json; charset=utf-8 on both directions.

2. Request envelope

{
  "protocol": "1.0",
  "method": "createCheckout",
  "params": { "input": { "currency": "GBP" } }
}
FieldTypeNotes
protocolstringWire protocol version. Currently "1.0". Merchants MUST reject requests with an unsupported protocol.
methodstringOne of the methods in §6. Mirrors the path segment.
paramsobjectMethod-specific arguments. Always an object (never an array or primitive).

Headers

HeaderRequiredNotes
Content-Typeyesapplication/json.
X-VaultGraph-VersionyesSame value as protocol in the body. Lets edge proxies short-circuit rejection.
X-VaultGraph-SignatureyesSee §3.
X-VaultGraph-Idempotency-Keyon mutating methods (§5)UUID v4 generated per logical operation by the gateway.

3. Signing

Authentication is HMAC-SHA256 over ${timestamp}.${idempotencyKey}.${rawBody}.
  1. The gateway takes the current Unix timestamp in seconds.
  2. It computes signature = HMAC_SHA256(secret, "${timestamp}.${idempotencyKey}.${rawBody}") and hex-encodes it. ${idempotencyKey} is the value of the X-VaultGraph-Idempotency-Key header (§5); on non-mutating methods, which carry no idempotency key, it is the empty string.
  3. It sets X-VaultGraph-Signature: t=<timestamp>,v1=<hex_signature>.
Merchants MUST:
  1. Reject requests where X-VaultGraph-Signature is missing or malformed.
  2. Reject requests where |now - timestamp| > 5 minutes. This is the replay window.
  3. Recompute the HMAC over the timestamp, the X-VaultGraph-Idempotency-Key header value (empty string when absent), and the raw body bytes (not a re-serialized JSON), and reject on mismatch. Use a constant-time comparison.
The t=…,v1=… shape leaves room for v2 to ship a new digest in the future without breaking existing verifiers.

Secret rotation

VaultGraph issues the signing secret when you create the commerce backend in the portal and lets you rotate it from the backend’s settings page. After a rotation the gateway begins signing with the new secret within 60 seconds. The protocol does not multiplex signatures, so coordinating a dual-accept window on your verifier (accept both old and new during the cutover) is the merchant’s responsibility.

4. Response envelope

Success:
{
  "data": {
    /* method-specific result */
  }
}
Failure:
{
  "error": {
    "code": "checkout_not_open",
    "status": 409,
    "detail": "Checkout ck_abc has already completed."
  }
}
FieldTypeNotes
dataanyRequired on success. Method-specific shape (§6).
error.codestringRequired on failure. See §7 for the recognized set.
error.statusintegerOptional. HTTP-style status the gateway should surface. Defaults to the HTTP response status.
error.detailstringOptional. Human-readable message safe for upstream display; do not include secrets or PII.
The HTTP status code SHOULD mirror error.status. Returning a 2xx with an error field is tolerated (the gateway prefers the envelope) but discouraged. A response with neither data nor error — or an HTTP error with no JSON body — is treated as commerce_backend_unavailable (HTTP 502).

5. Idempotency

The gateway sends X-VaultGraph-Idempotency-Key: <uuid> on every mutating method:
  • createCheckout
  • addLineItems
  • updateLineItem
  • removeLineItem
  • applyDiscount
  • setFulfillment
  • setBuyer
  • completeCheckout
Reads (searchProducts, getProduct, getCheckout, listFulfillmentMethods, getOrder, listOrders) do not carry an idempotency key. The contract:
  • The merchant SHOULD store the full response (status + body) keyed by the idempotency key.
  • If the same key arrives a second time within the merchant’s TTL, the merchant SHOULD return the stored response verbatim instead of re-running the operation.
  • Cache both success AND error responses. A mutating call that fails after committing a side effect (e.g. order written, downstream notification failed) must NOT re-execute on retry. The gateway treats merchant errors with recognized codes as deterministic answers and will not retry them, but a network blip mid-response can still cause a retry — the cache is what protects against the duplicate.
  • Recommended TTL: 24 hours. Retries beyond that window are vanishingly rare; storage costs grow if you keep them forever.
The SDK helper exposes an idempotencyStore option that handles this for you given a { get(key), set(key, { status, body }) } interface — most merchants will plug in their existing cache layer (Redis, Postgres unlogged table, KV).

6. Methods

MethodMutatingparamsdata
searchProductsno{ input: SearchCatalogInput } (see Catalog search)ProductPage ({ products: Product[], pagination?: { … } })
getProductno{ id: string }Product or nullproduct_not_found (404)
createCheckoutyes{ input: CheckoutCreateInput }Checkout
getCheckoutno{ id: string }Checkout or nullcheckout_not_found
addLineItemsyes{ checkout_id: string, line_items: LineItem[] }Checkout
updateLineItemyes{ checkout_id: string, line_item_id: string, quantity: integer ≥ 0 }Checkout
removeLineItemyes{ checkout_id: string, line_item_id: string }Checkout
applyDiscountyes{ checkout_id: string, code: string }Checkout
listFulfillmentMethodsno{ checkout_id: string }FulfillmentMethod[] (see Fulfillment)
setFulfillmentyes{ checkout_id: string, fulfillment_method_id: string, shipping_address?: ShippingDestination }Checkout
setBuyeryes{ checkout_id: string, buyer: Buyer, billing_address?: BillingAddress }Checkout
completeCheckoutyes{ checkout_id: string }Order
getOrderno{ id: string }Order or nullorder_not_found (404)
listOrdersno{}Order[]
TypeScript types for every params and data shape ship from @vaultgraph/sdk/adapter — install it for types even if you don’t use the handler factory in §10. searchProducts takes { input: SearchCatalogInput }, where every field of SearchCatalogInput is optional — an empty input returns the first page of the whole catalog:
{
  "query": "running shoes", // optional free-text search
  "filters": {
    // optional
    "categories": ["footwear"], // OR logic across the list
    "price": { "min": 5000, "max": 20000 }, // inclusive, integer minor units
  },
  "pagination": { "cursor": "…", "limit": 20 }, // optional; limit is clamped to ≤ 100
}
The merchant SHOULD honor query and filters; a merchant that cannot run a given filter MAY ignore it, but SHOULD NOT error.

Pagination

searchProducts returns a ProductPage{ "products": Product[], "pagination"?: { … } }, not a bare array. Pagination is cursor-based and caller-driven:
  • The merchant chooses the cursor encoding (offset, keyset, base64 JSON — whatever fits the underlying store). The gateway treats it as an opaque blob: it never inspects or generates the value.
  • When more pages remain, the merchant returns pagination: { "has_next_page": true, "cursor": "<token>" }. cursor MUST be present whenever has_next_page is true.
  • On the last page the merchant either omits pagination entirely or returns pagination: { "has_next_page": false }. total_count MAY be included on any page.
  • The caller passes the returned cursor straight back as input.pagination.cursor on the next call; the gateway forwards it unchanged.
  • The gateway does not auto-paginate — each call returns exactly one page and the caller drives the loop.
listOrders has no pagination in v1.0. Merchants should cap returned rows at a reasonable limit (≤ 100) and document the limit out-of-band.

Fulfillment

listFulfillmentMethods returns the methods selectable for a checkout — each a FulfillmentMethod ({ id, name, description, amount, currency, method_type }), where amount is integer minor units and method_type is shipping, pickup, or digital. setFulfillment accepts an optional shipping_address (a ShippingDestination — a postal address). When the chosen method’s method_type is shipping, the merchant MUST require a deliverable destination and SHOULD reject a missing or incomplete address with fulfillment_address_required (400). pickup and digital methods carry no address.

7. Error codes

The recognized set the merchant may emit:
CodeDefault statusWhen
product_not_found404getProduct finds no row.
variant_not_found404A variant id on a line-item is unknown.
checkout_not_found404getCheckout / addLineItems / … on a missing checkout.
checkout_not_open409Mutation on a completed or otherwise closed checkout.
checkout_not_ready409completeCheckout before buyer / fulfillment are set.
checkout_empty409completeCheckout on a checkout with no line items.
line_item_not_found404updateLineItem / removeLineItem on an unknown line.
discount_not_found404applyDiscount with an invalid code.
fulfillment_method_not_found404setFulfillment with an invalid method id.
fulfillment_address_required400setFulfillment for a shipping method with no address.
order_not_found404getOrder finds no row.
Any other code is mapped to commerce_backend_unavailable (HTTP 502) on the gateway side. The detail is preserved but the original code is replaced — agents and downstream consumers do not see unknown codes.

8. Timeout & retry

The gateway defaults:
  • Per-attempt timeout: 10 seconds (configurable per backend in the portal).
  • Retries: up to 2 (configurable per backend in the portal), with exponential backoff starting at 100 ms.
Retries fire on:
  • network errors (DNS, TCP, TLS, socket close mid-response),
  • response timeouts (the request aborted before headers),
  • HTTP 5xx without a recognized error envelope.
Retries do not fire on a well-formed error envelope with a recognized code — those are deterministic answers. They also do not fire on 4xx without an envelope (treated as a 502 once, then surfaced). Because every mutating method carries an X-VaultGraph-Idempotency-Key, the merchant can safely dedupe. Reads are naturally idempotent.

9. Versioning

  • Envelope version is the protocol field plus the X-VaultGraph-Version header. Additive payload changes (new methods, new optional fields on data) ship under the same envelope.
  • Backwards-incompatible envelope changes will bump the major version (1.02.0) and use a new header value. The gateway will continue to send 1.0 to backends that have not opted in.
  • Merchants that don’t implement a method may return {"error":{"code":"not_implemented","status":501}} — the gateway surfaces it as commerce_backend_unavailable. The @vaultgraph/sdk/adapter helper emits this envelope automatically for any method you don’t supply, so you can ship a partial implementation and grow into the surface incrementally.

10. Reference implementation

The @vaultgraph/sdk/adapter helper wraps signing, replay rejection, dispatch, idempotency caching, and error serialization in a single typed factory:
import { createAdapterHandler } from "@vaultgraph/sdk/adapter";

const handler = createAdapterHandler({
  signingSecret: process.env.VAULTGRAPH_SIGNING_SECRET!,
  idempotencyStore: kvBackedStore,

  async searchProducts({ query, filters, pagination }) {
    /* ... */
  },
  async getProduct(id) {
    /* ... */
  },
  async createCheckout(input) {
    /* ... */
  },
  // ...one method per row in §6 — every method is optional, anything you
  // skip auto-responds with `not_implemented` (501).
});

// Plug into your HTTP framework — handler is just (req) => res.
See @vaultgraph/sdk on npm for the full surface.