> ## Documentation Index
> Fetch the complete documentation index at: https://docs.useshipd.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Convex

> Mount the Convex component, receive snapshot updates, and evaluate flags in backend functions.

## Overview

The `@shipdit/convex` component keeps the last valid shipd snapshot in Convex. Your queries read
that local copy, so flag evaluation stays reactive and does not add a network request to each
function call. The integration is available on every shipd plan.

Mount **one named component instance per shipd environment**. For example, mount
`shipdDevelopment` and `shipdProduction` separately. Each instance has its own SDK key, signing
secret, snapshot, and health status.

## Install

```bash theme={null}
npm install @shipdit/convex
```

Add the component to `convex/convex.config.ts`. The `httpPrefix` gives each instance a distinct
webhook route.

```ts convex/convex.config.ts theme={null}
import { defineApp } from "convex/server";
import { v } from "convex/values";
import shipd from "@shipdit/convex/convex.config.js";

const app = defineApp({
  env: {
    SHIPD_PRODUCTION_SDK_KEY: v.string(),
    SHIPD_PRODUCTION_WEBHOOK_SECRET: v.string(),
  },
});

app.use(shipd, {
  name: "shipdProduction",
  httpPrefix: "/shipd/production",
  env: {
    SHIPD_SDK_KEY: app.env.SHIPD_PRODUCTION_SDK_KEY,
    SHIPD_WEBHOOK_SECRET: app.env.SHIPD_PRODUCTION_WEBHOOK_SECRET,
  },
});

export default app;
```

Run `npx convex dev` after changing component mounts so Convex regenerates `components`.

## Configure environment values

Set these values for every mounted instance:

```bash theme={null}
npx convex env set SHIPD_PRODUCTION_SDK_KEY shipd_sdk_server_...
npx convex env set SHIPD_PRODUCTION_WEBHOOK_SECRET replace-after-dashboard-setup
```

| Value | Purpose |
| - | - |
| `SHIPD_SDK_KEY` | Server SDK key. Fetches the snapshot for exactly one shipd environment. |
| `SHIPD_WEBHOOK_SECRET` | Verifies signed invalidations from shipd. |

The component fetches snapshots from `https://edge.useshipd.com`.

Do not expose the SDK key or webhook secret to browser code.

## Create the webhook destination

1. Open the project in the shipd dashboard and go to **Settings → Integrations**.
2. Under **Convex**, choose the matching environment.
3. Enter
   `https://<your-deployment>.convex.site/shipd/production/webhook`.
4. Click **Create destination**.
5. Copy the signing secret immediately and replace
   `SHIPD_PRODUCTION_WEBHOOK_SECRET` in Convex.
6. Redeploy, then click **Test delivery**.

The callback hostname must end in `.convex.site` and use HTTPS. shipd signs the exact request body
and sends only publication metadata. The component fetches the canonical snapshot from the edge;
the webhook never contains flags or credentials.

## Wrap host queries

Create one wrapper for each mounted instance. Defaults are required and stay type-safe.

```ts convex/lib/shipd.ts theme={null}
import { Shipd } from "@shipdit/convex";
import { components } from "../_generated/api";

type Flags = {
  "new-checkout": { type: "boolean"; default: false };
  "search-variant": {
    type: "variant";
    default: "control";
    variants: readonly ["control", "semantic"];
  };
};

export const shipd = new Shipd<Flags>(components.shipdProduction, {
  "new-checkout": false,
  "search-variant": "control",
});
```

Component functions cannot see your host application's `ctx.auth`. Authorize in the host function
before calling the wrapper:

```ts convex/checkout.ts theme={null}
import { query } from "./_generated/server";
import { shipd } from "./lib/shipd";

export const navigation = query({
  args: {},
  handler: async (ctx) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) throw new Error("Unauthenticated");

    return {
      newCheckout: await shipd.isEnabled(ctx, "new-checkout", {
        userId: identity.subject,
      }),
      search: await shipd.getVariant(ctx, "search-variant", {
        userId: identity.subject,
      }),
    };
  },
});
```

The wrapper also provides `getNumber`, `getJson`, and `evaluate`. Use `evaluate` when you need the
reason, matched rule, snapshot version, or stale state.

## Inspect sync health

Call `getStatus` from an authorized host query:

```ts theme={null}
export const shipdStatus = query({
  args: {},
  handler: async (ctx) => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) throw new Error("Unauthenticated");
    return shipd.getStatus(ctx);
  },
});
```

`hasSnapshot` tells you whether an initial sync succeeded. `version`, `etag`, and `fetchedAt`
identify the cached snapshot. `lastAttemptAt`, `lastError`, and `lastErrorAt` describe sync health.
`stale: true` means a valid cached snapshot exists but the latest refresh failed.

Webhooks provide fast invalidation. A one-minute ETag poll is the reliability fallback. You can
also call `syncNow` from an authorized host action.

## Understand defaults and stale snapshots

* Before the first valid snapshot, the wrapper returns your typed default.
* A missing flag or value-type mismatch also returns its typed default.
* After a successful sync, the component keeps the last valid snapshot indefinitely.
* A refresh failure marks that snapshot stale but does not replace it with defaults.
* Older out-of-order snapshots are ignored.

This makes temporary shipd, webhook, or network failures fail stable instead of changing flag
behavior.

## Rotate credentials

Rotate the webhook secret without creating a second destination:

1. In the project's **Settings → Integrations**, choose the environment, then click **Rotate secret**.
2. Copy the new secret immediately.
3. Update the matching `SHIPD_WEBHOOK_SECRET` value in Convex and redeploy.
4. Click **Test delivery**.

There is no overlap window: requests signed with the old secret fail after rotation. Coordinate
the dashboard rotation and Convex deployment during a short maintenance window. The one-minute
poll continues refreshing snapshots if webhook delivery fails.

Rotate the SDK key separately by creating a replacement server SDK key for the same environment in **SDK keys**, updating
`SHIPD_SDK_KEY`, confirming a successful sync, and then revoking the old key.

## Next

* [Node.js and server runtimes](/sdks/node)
* [Environments](/concepts/environments)
* [Keys](/concepts/keys)
* [Complete Convex example](/examples/convex)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.