---
title: "Onboarding & API Keys"
description: "Setup checklist for first-run configuration — API keys, OAuth, and provider connections"
---

# Onboarding

When you first open an app built on the agent-native framework, you'll see a
**Setup** checklist in the agent sidebar. It keeps first-run configuration close
to the agent chat: connect an AI engine, optionally point the app at shared
infrastructure, and add providers only when you need them.

<Diagram id="doc-block-r5rlac" title="The setup checklist" summary="Only Connect an AI engine is required. The panel tracks completion and auto-hides once everything required is done.">

```html
<div class="ob">
  <div class="diagram-card">
    <span class="diagram-pill warn">required</span
    ><strong>Connect an AI engine</strong
    ><small class="diagram-muted"
      >Connect Builder (one click) or paste an LLM key</small
    >
  </div>
  <div class="diagram-card">
    <span class="diagram-pill">optional</span><strong>Database</strong
    ><small class="diagram-muted">set <code>DATABASE_URL</code></small>
  </div>
  <div class="diagram-card">
    <span class="diagram-pill">optional</span><strong>Authentication</strong
    ><small class="diagram-muted">OAuth / access token</small>
  </div>
  <div class="diagram-card">
    <span class="diagram-pill">optional</span><strong>Email delivery</strong
    ><small class="diagram-muted">Resend / SendGrid</small>
  </div>
  <div class="diagram-arrow diagram-accent" aria-hidden="true">&rarr;</div>
  <div class="diagram-box ok">all required done &rarr; panel auto-hides</div>
</div>
```

```css
.ob {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.ob .diagram-card {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 12px 14px;
}
.ob .diagram-arrow {
  font-size: 22px;
}
```

</Diagram>

## For end users

### What you'll see

<WireframeBlock id="doc-block-ob1">
  <Screen
    surface="panel"
    html={
      "<div style='display:flex;flex-direction:column;gap:10px;padding:14px 16px;min-height:460px;box-sizing:border-box'><div style='display:flex;align-items:center;gap:8px'><strong style='flex:1'>Setup</strong><span class='wf-pill accent'>1 of 4</span><span data-icon='chevronUp'></span></div><div class='wf-card' style='display:flex;align-items:center;gap:8px'><span data-icon='check' style='color:var(--wf-ok)'></span><span style='flex:1'>Database</span><small class='wf-muted'>optional</small></div><div class='wf-card' style='display:flex;flex-direction:column;gap:8px'><div style='display:flex;align-items:center;gap:8px'><strong style='flex:1'>Connect an AI engine</strong><span class='wf-pill' style='color:var(--wf-warn);border-color:var(--wf-warn)'>required</span></div><small class='wf-muted'>Connect Builder (one click) or paste an LLM key</small><button class='primary'>Connect Builder.io</button><small style='text-decoration:underline'>or paste your own key</small></div><div class='wf-card' style='display:flex;align-items:center;gap:8px'><span style='flex:1'>Authentication</span><small class='wf-muted'>optional</small></div><div class='wf-card' style='display:flex;align-items:center;gap:8px'><span style='flex:1'>Email delivery</span><small class='wf-muted'>optional</small></div><div style='flex:1'></div><small style='text-decoration:underline;align-self:flex-start'>Hide setup</small></div>"
    }
  />
</WireframeBlock>

- A **Setup** panel above the agent chat with a checklist like "Connect an AI
  engine", "Email delivery", etc.
- A counter at the top (e.g. "1 of 4") shows how many steps are ready.
- The current step is expanded; finished steps show a green check and stay
  readable if you open them.
- Required steps show a small red **required** pill. The panel stays visible
  until every required step is complete.
- Once everything required is done, the panel hides itself automatically.
- The whole panel can be collapsed with the chevron in the top-right, or
  hidden entirely with **Hide setup** at the bottom.

### How to complete each step

Steps offer one or more **methods** — different ways to satisfy the same
requirement. The primary path is shown first; secondary paths are kept compact
behind a picker or disclosure when a step has several equivalent providers.

- **Connect a service (one click)** — e.g. _Connect Builder_ for the managed
  AI gateway. Click the button, a window opens, you sign in, the window closes,
  and the step is marked complete. No keys to copy.
- **Paste an API key or fill a form** — e.g. choose an LLM provider, database,
  OAuth provider, or email provider, paste the value(s), click **Save**.
  Secret fields use a password input so the value isn't shown on screen. Saved
  values go into your local `.env` (or workspace settings) — see
  [Security](/docs/security) for where they live.
- **Open a link** — some steps point to a sign-in page or docs. Click
  **Continue** and finish the flow in the new tab.
- **Ask the agent** — a few steps offer a "Let the agent set it up" option.
  Click it and the agent picks up in the chat, walking you through any
  external setup (creating OAuth credentials, etc.).

### The built-in steps you'll usually see

- **Connect an AI engine** (required) — the only mandatory step. Connect
  Builder for a one-click managed gateway, or open the secondary provider-key
  picker and paste your own LLM key.
- **Database** (optional) — set `DATABASE_URL` when you want to use a specific
  SQL database connection string.
- **Authentication** (optional) — built-in email/password accounts work by
  default. Add OAuth or access-token sign-in only when you want those paths.
- **Email delivery** (optional) — useful before deploy for password resets,
  team invitations, and share notifications. Use the provider you already use;
  local development can run without it.
- **Connect a GitHub repository** (optional) — grants a scoped GitHub token so
  the agent can read/write repo files for cloud or headless work, without
  cloning the repo or running code. Skip it for local development where the
  agent already has the repo on disk.

Templates can add their own steps on top of these — e.g. a CRM template might
add "Connect Gmail", a docs template might add "Pick a default workspace". See
[Authentication](/docs/authentication) for sign-in setup details.

### Coming back to the checklist

If you hit **Hide setup**, the panel disappears for that browser session.
Required steps that aren't yet complete will surface again on next load. Once
everything required is done, the panel auto-hides for good — there's nothing
left to do.

## For developers

If you're building a template, you register onboarding steps so they appear in
the user's sidebar checklist. The framework handles rendering, completion
tracking, and dismissal — you just declare what the step is and how it's
satisfied.

The system is **auto-mounted**. Templates don't need to wire anything to get
the four built-in steps (LLM, database, auth, email). To add app-specific
steps (Gmail, Slack, Notion, etc.), call `registerOnboardingStep()` from a
server plugin.

### Auto-mounted routes

All routes live under `/_agent-native/onboarding/`:

| Route                                               | Purpose                           |
| --------------------------------------------------- | --------------------------------- |
| `GET /_agent-native/onboarding/steps`               | List steps with completion status |
| `POST /_agent-native/onboarding/steps/:id/complete` | Mark step complete (override)     |
| `POST /_agent-native/onboarding/dismiss`            | Dismiss the onboarding banner     |
| `POST /_agent-native/onboarding/reopen`             | Clear dismissal (re-show panel)   |
| `GET /_agent-native/onboarding/dismissed`           | Read dismissal + allComplete flag |

<Endpoint id="doc-block-3rl1c" title="List onboarding steps" method="GET" path="/_agent-native/onboarding/steps" summary="List all registered steps with their completion status" responses={[
  {
    "status": "200",
    "description": "Array of steps with completion status for the current user/app."
  }
]}>

Drives the sidebar checklist — returns each step's id, title, methods, required flag, and whether `isComplete` currently passes.

</Endpoint>

### Adding a step from a template

<AnnotatedCode
  id="doc-block-10o0l9n"
  title="Registering a custom onboarding step"
  filename="server/plugins/my-onboarding.ts"
  language="ts"
  code={
    'import { defineNitroPlugin } from "@agent-native/core/server";\nimport { registerOnboardingStep } from "@agent-native/core/onboarding";\nimport { listOAuthAccounts } from "@agent-native/core/oauth-tokens";\n\nexport default defineNitroPlugin(() => {\n  registerOnboardingStep({\n    id: "gmail",\n    order: 100,\n    title: "Connect Gmail",\n    description: "Grant read/send access so the agent can work with email.",\n    methods: [\n      {\n        id: "oauth",\n        kind: "link",\n        primary: true,\n        label: "Sign in with Google",\n        payload: { url: "/_agent-native/google/auth-url?scope=mail", external: false },\n      },\n      {\n        id: "delegate",\n        kind: "agent-task",\n        label: "Let the agent set it up",\n        badge: "beta",\n        payload: { prompt: "Walk me through connecting Gmail. Set env vars as needed." },\n      },\n    ],\n    isComplete: async () => {\n      const accounts = await listOAuthAccounts("google");\n      return accounts.length > 0;\n    },\n  });\n});'
  }
  annotations={[
    {
      lines: "5",
      label: "Auto-mounted",
      note: "Register from a Nitro plugin — the framework handles rendering, completion tracking, and dismissal.",
    },
    {
      lines: "7",
      label: "Stable id",
      note: "Re-registering with the same `id` after defaults load overrides a built-in step.",
    },
    {
      lines: "12-19",
      label: "Primary method",
      note: '`primary: true` marks the big CTA. `kind: "link"` sends the user into the OAuth flow.',
    },
    {
      lines: "20-26",
      label: "Delegate path",
      note: '`kind: "agent-task"` hands the setup to the agent chat with a prompt.',
    },
    {
      lines: "28-31",
      label: "Completion check",
      note: "`isComplete` runs server-side. OAuth tokens live in the `oauth_tokens` store — check it, not `process.env.GMAIL_REFRESH_TOKEN`.",
    },
  ]}
/>

### Checking Workspace Connections in Onboarding

When building templates that interact with external services (like Slack, Google Workspace, GitHub, or HubSpot), you should check if the workspace has already connected and granted that provider connection to your application. This prevents users from having to duplicate credentials (like API keys or refresh tokens) in their local environment variables when a central, managed connection exists.

You can check connection readiness in your `isComplete` callback using the connection catalog APIs:

```ts filename="server/plugins/my-onboarding.ts"
import { listWorkspaceConnectionProviderCatalogForApp } from "@agent-native/core/workspace-connections";

// Inside registerOnboardingStep:
isComplete: async () => {
  // Check if a managed workspace connection exists and is ready
  const catalog = await listWorkspaceConnectionProviderCatalogForApp({
    appId: "mail",
    templateUse: "mail",
    provider: "gmail",
  });
  const connection = catalog.providers[0];

  if (
    connection?.readiness.status === "ready" &&
    connection.workspaceConnection.grantState === "granted"
  ) {
    return true;
  }

  // Fall back to local environment variable check
  return !!process.env.GMAIL_REFRESH_TOKEN;
};
```

Refer to the [Workspace Connections](/docs/workspace-connections) documentation for the full list of connection provider catalog methods.

### Method kinds

| Kind               | Payload                                               | Use for                                   |
| ------------------ | ----------------------------------------------------- | ----------------------------------------- |
| `link`             | `{ url, external? }`                                  | Send user to an OAuth flow or docs page   |
| `form`             | `{ fields, writeScope? }`                             | Collect env vars (keys, secrets, URLs)    |
| `builder-cli-auth` | `{ scope: "llm" \| "browser" \| "image-generation" }` | Connect Builder (unlocks shared infra)    |
| `agent-task`       | `{ prompt }`                                          | Send a prompt to the agent chat to handle |

The `primary: true` flag marks a method as the big CTA for its step.
Use `badge: "soon"` plus `disabled: true` when a setup path should be visible
before it is available.

### Built-in steps

| ID                  | Required | Description                                             |
| ------------------- | -------- | ------------------------------------------------------- |
| `llm`               | yes      | Builder connection or a provider LLM key                |
| `database`          | no       | Default database or any SQL `DATABASE_URL`              |
| `auth`              | no       | Built-in accounts, optional OAuth or access token       |
| `email`             | no       | Resend or SendGrid for transactional email              |
| `github-repository` | no       | Scoped GitHub token for cloud/headless repo file access |

Any of these can be overridden by re-registering with the same `id` after the
defaults load.

### Client usage

The panel is already inside `<AgentPanel>`. To build a custom layout:

```tsx filename="app/components/MySidebar.tsx"
import {
  OnboardingPanel,
  OnboardingBanner,
  useOnboarding,
} from "@agent-native/core/client/onboarding";

function MySidebar() {
  const { allComplete, dismissed, currentStepId } = useOnboarding();
  if (allComplete || dismissed) return <Chat />;
  return (
    <>
      <OnboardingPanel />
      <Chat />
    </>
  );
}
```

For background on where step values are stored and how secrets are handled,
see [Security](/docs/security). For end-user messaging touchpoints (invitations,
password resets) that depend on the **Email delivery** step, see
[Messaging](/docs/messaging).

## What's next {#whats-next}

- [**Security**](/docs/security) — where onboarding secrets and step values are stored
- [**Authentication**](/docs/authentication) — sign-in setup for the Authentication step
- [**Workspace Connections**](/docs/workspace-connections) — the connection catalog APIs used by `isComplete` checks
- [**Messaging**](/docs/messaging) — email delivery setup that the Email delivery step unlocks
