> ## 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.

# VaultGraph SDK

> Manage VaultGraph shops from your backend

The official Node.js SDK for [VaultGraph](https://vaultgraph.com), the hosted MCP gateway for e-commerce merchants.

This enables AI chats to interact with your merchant backend and turns your AI chat into a full, interactive shopping experience for your customers.

Use this SDK to:

* **Integrate your storefront with VaultGraph in one file** with the `@vaultgraph/sdk/adapter` helper. The hosted gateway POSTs to one HTTPS endpoint you expose; your cart/checkout/order records never leave your infrastructure. **This is the merchant integration path** — your storefront never calls VaultGraph.
* Manage your VaultGraph workspace from your backend (create shops, rotate keys).
* Generate Ed25519 signing keys with the bundled key helpers.

## Install

```bash theme={null}
pnpm add @vaultgraph/sdk
```

Also works with `npm install @vaultgraph/sdk` or `yarn add @vaultgraph/sdk`.

## Before you start

1. Sign up at [app.vaultgraph.com](https://app.vaultgraph.com) and create your organization.
2. Create a shop, then a deployment for that shop. Each deployment gets its own MCP endpoint.
3. Generate an API key. Organization keys (`vk_...`) cover org-wide operations such as listing shops; deployment keys (`dk_...`) are scoped to a single deployment.

Keep API keys out of browser code — the SDK is server-side only.

## Integrate your storefront (`@vaultgraph/sdk/adapter`)

Mount one HTTPS endpoint and your storefront is wired to VaultGraph. The helper handles signing, replay protection, idempotency dedupe, and error serialization. Callbacks are typed over `Checkout`, `Buyer`, `LineItem`, `Order`, `Product`, `Variant`, … from `@vaultgraph/sdk/adapter`.

If you're integrating an existing storefront, **this is the only thing you need to implement on your side.** There are no merchant-side calls to VaultGraph and no REST endpoints to consume. The gateway calls your endpoint; you respond.

**Every method is optional.** Implement only what you need today — anything you skip auto-returns `not_implemented` (501), which the gateway surfaces as `commerce_backend_unavailable` to agents. The gateway also advertises only the tools you implement: the handler reports your method set on request, so agents see exactly what your backend serves. Add methods incrementally as you build out your storefront:

```ts theme={null}
import { createAdapterHandler } from "@vaultgraph/sdk/adapter";

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

  // Runs once per request, before dispatch, and is the only place the raw
  // `context.session_token` is read. Verify it and return a typed identity (any
  // shape you like) or `null` for an anonymous caller. The result is handed to
  // every method as its final `session` argument — already authenticated, so
  // owner-scoped reads gate on it; the handler strips the token from the
  // context methods see. Throw to reject the request outright.
  verifySession: (context) => verifySessionToken(context?.session_token),

  async searchProducts({ query, filters, pagination } = {}) {
    // Return a ProductPage: `{ products, pagination? }`. Set
    // `pagination: { has_next_page: true, cursor }` while more rows remain;
    // omit `pagination` on the last page.
    return myDb.products.search({ query, filters, pagination });
  },
  async getProduct(id) {
    return myDb.products.find(id);
  },
  async listCategories() {
    // Return your store's whole category vocabulary: `{ value, count? }[]`.
    // The labels are what agents pass to `searchProducts`' filters.categories,
    // so this is how they discover categories and recover from an empty search.
    return myDb.products.categories();
  },
  async getOrder(id, _context, session) {
    // Owner-scoped read: return the order only when the verified caller owns
    // it. Throw the same `order_not_found` for a missing OR non-owned id so
    // existence isn't leaked. Key orders on an opaque id, not an enumerable
    // sequential number.
    const order = session ? await myDb.orders.find(id) : null;
    if (!order || order.customerId !== session.customerId)
      throw { code: "order_not_found", status: 404 };
    return order;
  },
  async listOrders(input, _context, session) {
    // The verified `session` from `verifySession` is the final argument of
    // every method; scope the read to it. Reject an anonymous caller with
    // `authentication_required` (401) so the agent prompts the user to sign in,
    // rather than an empty page that reads as "you have no orders".
    // `input.pagination` carries the optional cursor/limit (limit must be ≤ 100
    // — the gateway rejects a larger value), and you return `OrderPage` —
    // `{ orders }` plus an optional `pagination` cursor for the next page.
    if (!session)
      throw {
        code: "authentication_required",
        status: 401,
        detail: "Sign in to view your orders.",
      };
    return myDb.orders.forCustomer(session.customerId, input?.pagination);
  },

  // …add `createCheckout`, `addLineItems`, `completeCheckout`, etc. as you
  // implement them. The protocol reference lists every method with its
  // inputs, return shape, preconditions, and which error to throw when.
});
```

The handler is framework-agnostic — it returns `(req) => Promise<res>`. Mount it from any HTTP server:

```ts theme={null}
// Next.js App Router
export async function POST(request: Request) {
  const result = await handler({
    rawBody: await request.text(),
    headers: Object.fromEntries(request.headers),
  });
  return new Response(result.body, {
    status: result.status,
    headers: result.headers,
  });
}
```

Configure the endpoint URL and a shared HMAC secret on your deployment in [app.vaultgraph.com](https://app.vaultgraph.com) and you're live. The full wire contract — envelope shape, signing scheme, error mapping, idempotency rules, and retry policy — is documented in the [Remote adapter protocol reference](https://vaultgraph.com/docs/remote-adapter-protocol).

Pass an optional `idempotencyStore` to `createAdapterHandler` — any `{ get(key), set(key, { status, body }) }` and the helper replays the cached response on retries instead of re-running your code.

Pass an optional `verifySession` to authenticate the caller once per request: it is the sole consumer of `context.session_token`, turning it into a typed identity that the handler hands to every method as the final `session` argument. The handler strips `session_token` from the context methods receive, so a method can't bypass the hook by trusting the raw token. Return `null` for an anonymous caller, or throw to reject the request. Omit it and every method is still called with a final `session` argument, but it is always `undefined` — configure the hook to authorize owner-scoped reads.

Implement `requestAuthentication` / `verifyAuthentication` to let customers sign in mid-conversation: you email a one-time code and return an `AuthChallenge`, then verify the code and return an `AuthSession` whose `session_token` arrives on later requests as `context.session_token` — the same token `verifySession` authenticates. Flow details and security guidance (enumeration resistance, attempt caps, token scope) are in the [protocol reference](https://vaultgraph.com/docs/remote-adapter-protocol)'s Customer sign-in section.

## Manage shops

Use the shops client for control-plane operations against `/api/shops` — separate from storefront integration:

```ts theme={null}
import { createShopsClient } from "@vaultgraph/sdk";

const shops = createShopsClient({
  apiKey: process.env.VAULTGRAPH_API_KEY!, // vk_...
});

// Create
const shop = await shops.create({
  name: "Acme Store",
  description: "Flagship storefront.",
});

// List and get
const all = await shops.list();
const detail = await shops.get(shop.id);

// Update
await shops.update(shop.id, { name: "Acme Store EU" });

// Delete
await shops.delete(shop.id);
```

## Key helpers

The SDK exports server-side helpers for Ed25519 signing keys:

```ts theme={null}
import { generateKeyPair, derivePublicKeyPem } from "@vaultgraph/sdk";

const { privateKey, publicKey } = generateKeyPair();
const derivedPublicKey = derivePublicKeyPem(privateKey);
```

These run entirely locally and do not call the VaultGraph API.

## API reference

### Client factories

| Function                        | Description                                                     |
| ------------------------------- | --------------------------------------------------------------- |
| `createShopsClient(options)`    | CRUD for `/api/shops` (org-wide, `vk_` keys)                    |
| `createAdapterHandler(adapter)` | Build a typed webhook handler for a VaultGraph commerce backend |

### Key helpers

| Function                  | Description                                       |
| ------------------------- | ------------------------------------------------- |
| `generateKeyPair()`       | Generate a PEM-encoded Ed25519 key pair           |
| `derivePublicKeyPem(key)` | Derive the SPKI PEM public key from a private key |

### Types

**Shops:** `ShopRecord`, `ShopCreateInput`, `ShopUpdateInput`, `ShopsClient`, `ShopsClientOptions`

**Adapter (`@vaultgraph/sdk/adapter` subpath):** `MerchantCommerceAdapter`, `AdapterHandler`, `AdapterHandlerOptions`, `AdapterHandlerRequest`, `AdapterHandlerResponse`, `AdapterIdempotencyStore`, `AdapterHandlerError`, `Product`, `Variant`, `Price`, `PriceRange`, `Media`, `ProductPage`, `ProductPagination`, `RequestContext`, `SearchCatalogInput`, `FulfillmentMethod`, `Checkout`, `CheckoutCreateInput`, `Order`, `Buyer`, `BillingAddress`, `LineItem`, `ShippingDestination`, `AuthChallenge`, `AuthSession`.

## Links

* Platform: [app.vaultgraph.com](https://app.vaultgraph.com)
* Website: [vaultgraph.com](https://vaultgraph.com)
* npm: [@vaultgraph/sdk](https://www.npmjs.com/package/@vaultgraph/sdk)
