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

# Quickstart

> Make your first gavAI API call in under 5 minutes.

export const ErrorTable = ({errors}) => <div className="gv-error-table" role="table" aria-label="Errors and fixes">
    <div className="gv-error-table__head" role="row">
      <span className="gv-error-table__col-head" role="columnheader">Error</span>
      <span className="gv-error-table__col-head" role="columnheader">Cause</span>
      <span className="gv-error-table__col-head" role="columnheader">Fix</span>
    </div>
    {errors.map((err, i) => <div className="gv-error-table__row" role="row" key={i}>
        <code className="gv-error-table__code" role="cell">{err.code}</code>
        <div className="gv-error-table__cause" role="cell">{err.cause}</div>
        <div className="gv-error-table__fix" role="cell">{err.fix}</div>
      </div>)}
  </div>;

export const ExpectedOutput = ({caption = "Expected output", children}) => <div className="gv-output">
    <div className="gv-output__label">
      <svg className="gv-output__icon" viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
        <polyline points="15 10 20 15 15 20" />
        <path d="M4 4v7a4 4 0 0 0 4 4h12" />
      </svg>
      <span>{caption}</span>
    </div>
    <div className="gv-output__body">{children}</div>
  </div>;

export const PageMeta = ({audience, readingTime, prereqs, level}) => {
  const items = [audience && ({
    key: "audience",
    label: "AUDIENCE",
    value: audience
  }), readingTime && ({
    key: "time",
    label: "READ",
    value: readingTime
  }), level && ({
    key: "level",
    label: "LEVEL",
    value: level
  }), prereqs && prereqs.length > 0 && ({
    key: "prereqs",
    label: "PREREQS",
    value: prereqs.join(" · ")
  })].filter(Boolean);
  if (items.length === 0) return null;
  return <div className="gv-page-meta" role="group" aria-label="Page metadata">
      {items.map(item => <div className="gv-page-meta__item" key={item.key}>
          <span className="gv-page-meta__label">{item.label}</span>
          <span className="gv-page-meta__value">{item.value}</span>
        </div>)}
    </div>;
};

export const CTABanner = ({eyebrow, headline, subhead, ctaLabel, ctaHref, secondaryCtaLabel, secondaryCtaHref}) => <section className="gv-cta">
    <div className="gv-cta__copy">
      {eyebrow && <Eyebrow label={eyebrow} />}
      <h2 className="gv-cta__headline">{headline}</h2>
      {subhead && <p className="gv-cta__subhead">{subhead}</p>}
    </div>
    <div className="gv-cta__actions">
      {ctaLabel && ctaHref && <a className="gv-cta__button" href={ctaHref}>
          {ctaLabel}
        </a>}
      {secondaryCtaLabel && secondaryCtaHref && <a className="gv-cta__secondary" href={secondaryCtaHref}>
          {secondaryCtaLabel}
          <svg viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
            <line x1="7" y1="17" x2="17" y2="7" />
            <polyline points="7 7 17 7 17 17" />
          </svg>
        </a>}
    </div>
  </section>;

export const BrandCard = ({eyebrow, title, href, icon, badge, cta, featured = false, horizontal = false, children}) => {
  const classes = ["gv-card", featured && "gv-card--featured", horizontal && "gv-card--horizontal"].filter(Boolean).join(" ");
  const inner = <>
      {icon && <span className="gv-card__icon" aria-hidden="true">
          <Icon icon={icon} />
        </span>}
      <div className="gv-card__main">
        {(eyebrow || badge) && <div className="gv-card__meta">
            {eyebrow && <Eyebrow label={eyebrow} tone="muted" />}
            {badge && <span className="gv-card__badge">{badge}</span>}
          </div>}
        <h3 className="gv-card__title">{title}</h3>
        {children && <div className="gv-card__body">{children}</div>}
        {cta && <span className="gv-card__cta">
            {cta}
            <svg className="gv-card__cta-arrow" viewBox="0 0 24 24" width="12" height="12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
              <line x1="7" y1="17" x2="17" y2="7" />
              <polyline points="7 7 17 7 17 17" />
            </svg>
          </span>}
      </div>
    </>;
  return href ? <a className={classes} href={href}>
      {inner}
    </a> : <div className={classes}>{inner}</div>;
};

export const NumberedStep = ({n, title, children}) => <li className="gv-step">
    <span className="gv-step__index">{n}</span>
    <div className="gv-step__body">
      <h3 className="gv-step__title">{title}</h3>
      <div className="gv-step__content">{children}</div>
    </div>
  </li>;

export const NumberedSteps = ({children}) => <ol className="gv-steps">{children}</ol>;

export const ArrowItem = ({children}) => <li className="gv-arrow-item">
    <span className="gv-arrow-item__arrow" aria-hidden="true">{"↳︎"}</span>
    <span className="gv-arrow-item__content">{children}</span>
  </li>;

export const ArrowList = ({children}) => <ul className="gv-arrow-list">{children}</ul>;

export const Eyebrow = ({label, tone = "default"}) => <div className={`gv-eyebrow gv-eyebrow--${tone}`}>
    <span className="gv-eyebrow__bar" aria-hidden="true" />
    <span className="gv-eyebrow__label">{label}</span>
  </div>;

<Eyebrow label="QUICKSTART" />

<PageMeta audience="Developers" readingTime="5 min" prereqs={["gavAI workspace", "curl or Node/Bun"]} level="Beginner" />

By the end of this page you'll have called `GET /v1/me` against the gavAI API with a test-mode key and read the response: the user the key belongs to, the token's scopes, and the workspace it acts on. Five minutes. The point isn't the endpoint — `/v1/me` is the cheapest, safest call to confirm authentication works before you ask the API to do anything real.

This is the code entry point. The Builder ([console](https://console.gavai.app)) and the [MCP server](/mcp/overview) reach the same workspace and the same isolation guarantees. See [Who this is for](/mission) if you're deciding which entry point fits.

## What you need

<ArrowList>
  <ArrowItem>A gavAI workspace. Sign up or sign in at [console.gavai.app](https://console.gavai.app).</ArrowItem>
  <ArrowItem>A terminal with `curl`, or Node/Bun if you want the TypeScript path.</ArrowItem>
  <ArrowItem>About five minutes.</ArrowItem>
</ArrowList>

## How a request gets authenticated

One sentence of mental model before you start, so the response shape isn't surprising:

Every gavAI API request carries one credential in the `Authorization` header. The API resolves that credential to three things — the **user** who owns it, the **token** itself (with its scopes), and the **workspace** the call runs against. `GET /v1/me` is the endpoint that hands those three back to you so you can see what your key resolves to before using it for anything else.

That's why this is a good first call: it's read-only, it touches every part of the auth path, and the response is a self-portrait of your access.

## Steps

<NumberedSteps>
  <NumberedStep n="01" title="Get a test API key">
    Open [console.gavai.app](https://console.gavai.app), go to **Settings → API keys**, and create a new key in test mode.

    Test-mode keys carry the prefix `gak_test_`. They run in an isolated sandbox: separate data, separate counters, no real Stripe charges. Production keys use the prefix `gak_live_` — you'll switch to those later. Copy the key now; the console won't show it again.

    <Warning>
      Treat `gak_test_*` keys as secrets. They are sandboxed but can read your test data. Never commit them to source control.
    </Warning>
  </NumberedStep>

  <NumberedStep n="02" title="Call /v1/me">
    Two paths — pick one. Both make the same call against the same endpoint.

    <Tabs>
      <Tab title="curl">
        ```bash theme={null}
        curl https://api.gavai.app/v1/me \
          -H "Authorization: Bearer gak_test_..."
        ```

        Replace `gak_test_...` with the key you just copied. The API accepts and returns `application/json`; you don't need to set `Content-Type` for a GET.
      </Tab>

      <Tab title="TypeScript">
        Install the SDK, set your key in the environment, then call `me.get()`:

        ```bash theme={null}
        npm install @gavai/sdk
        ```

        ```ts theme={null}
        import { Gavai } from "@gavai/sdk";

        const gv = new Gavai({ apiKey: process.env.GAVAI_API_KEY });
        const me = await gv.me.get();
        console.log(me);
        ```

        The `Gavai` client accepts either `apiKey` (`gak_live_*` or `gak_test_*`, the server-side path) or `sessionToken` (`gst_*`, for OAuth flows). Set `GAVAI_API_KEY` in your shell to your test key — don't hard-code it.
      </Tab>
    </Tabs>
  </NumberedStep>

  <NumberedStep n="03" title="Read the response">
    A successful call returns this shape:

    <ExpectedOutput caption="Successful response">
      ```json theme={null}
      {
        "user": {
          "id": "usr_01H..."
        },
        "token": {
          "id": "tok_01H...",
          "scopes": ["workspaces:read", "pages:write"]
        },
        "workspace": {
          "slug": "acme"
        }
      }
      ```
    </ExpectedOutput>

    Three pieces, one for each part of the auth model:

    * **`user.id`** — the user the key acts on behalf of.
    * **`token.id` and `token.scopes`** — the key itself, and the permissions it carries. Every other endpoint checks against this scope list.
    * **`workspace.slug`** — the workspace this call ran inside. Every gavAI API call runs within a single workspace; the key is bound to one.

    If you see this shape, the auth path works end to end. Every other endpoint you call from this key uses the same header.

    If something else came back:

    <ErrorTable
      errors={[
    { code: "401 unauthorized", cause: "The `Authorization` header didn't parse.", fix: "Include the full key with the `gak_test_` prefix and the word `Bearer` before it." },
    { code: "403 insufficient_scope", cause: "The key was minted without `workspaces:read`.", fix: "Return to **Settings → API keys** in the console and add the scope." },
  ]}
    />
  </NumberedStep>
</NumberedSteps>

## What you can do now

You have a working API key, a confirmed identity, and you've seen the shape every gavAI response is built around (user, token, workspace). From here:

<Columns cols={2}>
  <BrandCard eyebrow="GUIDE" title="Your first integration" href="/guides/first-integration">
    Read a workspace, fetch a page, patch a block, publish — end to end.
  </BrandCard>

  <BrandCard eyebrow="CONCEPTS" title="Core concepts" href="/concepts/overview">
    PageBlocks, capabilities, the schema model, and multi-tenant isolation — the model behind every endpoint.
  </BrandCard>

  <BrandCard eyebrow="SDKS" title="SDK overview" href="/sdks/overview">
    Typed access to every resource via `@gavai/sdk`, or the low-level `@gavai/sdk-typescript`.
  </BrandCard>

  <BrandCard eyebrow="API REFERENCE" title="REST API reference" href="/api-reference/introduction">
    Every endpoint, scope, request shape, and error code.
  </BrandCard>
</Columns>

<CTABanner eyebrow="NEXT STEPS" headline="Ready to integrate?" ctaLabel="Browse the API reference" ctaHref="/api-reference/introduction" />
