GRAYPASS DOC/DEV-01

Get an API key.

Self-service. Free for testing. The secret stays on your server and mints renewable ct_* browser credentials. The public key identifies that keypair to the one-tag loader. Rotate either from the portal.

Test keys are issued instantly, capped at 1,000 req/h (tenant-wide), and rotate anytime. Live keys lift the cap to 10,000 req/h. You get 100 free sessions, no card needed. After that, buy a session pack or add a card to keep going.

Client scope is retained for test and legacy integrations, but the production browser path uses a subject-bound ct_* minted by your backend. A pk_* in the one-tag loader is only a public keypair identifier. Client keys cannot mint tokens or call administrative routes.

By generating a key you agree to our terms.

Save these now.

We don't store the secret key in plaintext. If you lose it you'll have to rotate. To manage this tenant, create or sign in to a verified account using the same email address; GrayPass will attach it after mailbox verification.

public_key

secret_key

The secret key gives full tenant access. Never embed it in client code, never commit it.

5 min quickstart

Drop-in install.

Add the SDK in your client, the middleware on your server. That's it.

npm install @graypass/sdk

import { GrayPassV2 } from "@graypass/sdk";

// Register this page's exact HTTPS origin in the GrayPass portal first.
// init returns a client instance - keep it around for the session lifetime
const gp = GrayPassV2.init({
  tokenProvider: async () => {
    const response = await fetch("/graypass/client-token", {
      method: "POST",
      credentials: "include",
    });
    if (!response.ok) throw new Error("client token unavailable");
    return await response.json(); // includes client_token + exact origin
  },
});

// startSession returns V2SessionStartResponse - snake_case fields, mirrors the JSON API.
const { session_id, enrolled, trust, state } = await gp.startSession({ userId: "user_42" });

gp.on("trust_change", ({ confidence, state, reasons }) => {
  console.log("trust ->", confidence, state, reasons);
});

// when the user clicks "Open dashboard":
const verdict = await gp.authorize("dashboard.example.com", { minTrust: 0.7 });
if (verdict.authorized) {
  document.cookie = `gp_token=${verdict.token}; Secure; SameSite=Lax`;
} else {
  // verdict.fallback ∈ {step_up_required, re_authenticate, wait_for_trust, session_killed}
}

// One-tag alternative:
// <script src="https://api.graypass.org/gp/graypass.js"
//   data-pk="pk_live_…" data-token-url="/graypass/client-token" defer></script>
// middleware.ts
import { withGrayPass } from "@graypass/middleware/nextjs";

export default withGrayPass({
  apiKey: process.env.GRAYPASS_SECRET_KEY!,
  minTrust: 0.4,
  target: "dashboard",
  stepUpRedirect: "/step-up",
});

export const config = { matcher: ["/dashboard/:path*"] };
curl -X POST https://app.graypass.org/v2/sessions/start \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -d '{"user_id":"user_42"}'