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

# Authentication

> How Vibefollow API keys work end-to-end &mdash; issue, use, rotate, revoke.

Vibefollow authenticates every request with a single API key. There is no OAuth dance and no per-user token — one key per project, scoped to the project's ingest endpoints.

## Key format

Keys follow Stripe-style prefixes so you can spot them in a log:

| Prefix      | Environment | Status          |
| ----------- | ----------- | --------------- |
| `sk_live_…` | Production  | Available today |
| `sk_test_…` | Sandbox     | Coming soon     |

A key looks like `sk_live_AbCd1234…XyZ` (32+ random characters after the prefix). After issue we only ever display the first 8 and last 4 in the dashboard — the full secret is shown exactly once at creation time.

<Warning>
  The full key is revealed **exactly once** in the create-modal. The dashboard cannot recover it afterwards — if you lose it, revoke and reissue.
</Warning>

## Key lifecycle

<Steps>
  <Step title="Issue a key">
    1. Sign in at [app.vibefollow.com](https://app.vibefollow.com).
    2. Open **Settings → Developers → API keys**.
    3. Click **Create key**, name it (e.g. `prod-server`, `staging-server`), and copy the secret immediately.
    4. Store it in your secrets manager.

    {/* <Frame><img src="/images/dashboard-developers.png" alt="API keys section in the Vibefollow dashboard" /></Frame> */}
  </Step>

  <Step title="Store the key">
    Put the secret in your environment as `VIBEFOLLOW_API_KEY` — never in source, never in client bundles. Vault, AWS Secrets Manager, Doppler, fly secrets — pick one and stick to it.
  </Step>

  <Step title="Use the key">
    Every request must carry your API key in the `Authorization` header. The SDK does this for you; direct REST callers send it themselves.

    <Tabs>
      <Tab title="Node SDK">
        ```ts theme={null}
        import { VibeFollow } from '@vibefollow/sdk';

        const vf = new VibeFollow({ apiKey: process.env.VIBEFOLLOW_API_KEY! });
        ```
      </Tab>

      <Tab title="cURL">
        ```bash theme={null}
        curl https://api.vibefollow.com/api/v1/events \
          -H "Authorization: Bearer $VIBEFOLLOW_API_KEY" \
          -H "Content-Type: application/json" \
          -H "Idempotency-Key: $(uuidgen)" \
          -d '{
            "external_user_id": "usr_42",
            "name": "user_signed_up",
            "properties": { "plan": "trial" }
          }'
        ```
      </Tab>

      <Tab title="Raw fetch">
        ```ts theme={null}
        await fetch('https://api.vibefollow.com/api/v1/events', {
          method: 'POST',
          headers: {
            Authorization: `Bearer ${process.env.VIBEFOLLOW_API_KEY}`,
            'Content-Type': 'application/json',
            'Idempotency-Key': crypto.randomUUID(),
          },
          body: JSON.stringify({
            external_user_id: 'usr_42',
            name: 'user_signed_up',
            properties: { plan: 'trial' },
          }),
        });
        ```
      </Tab>
    </Tabs>
  </Step>

  <Step title="Rotate the key">
    There is no atomic "rotate" button — instead, create the new key first, deploy it to your servers, then revoke the old one. This window of overlap is the only safe way to rotate without dropping events.

    1. **Create** a new key (`prod-server-2`).
    2. **Deploy** the new key to your environment (`VIBEFOLLOW_API_KEY`).
    3. **Verify** traffic is flowing under the new key (the dashboard shows last-used timestamp per key).
    4. **Revoke** the old key.
  </Step>

  <Step title="Revoke the key">
    In **Settings → Developers → API keys**, click the menu next to the key and select **Revoke**. Revocation is immediate — the next request with that key returns `401 Unauthorized` with `AuthError`.

    <Warning>
      Revocation is permanent. Vibefollow does not retain the secret after revoke; if you revoke by mistake, the key cannot be restored — create a new one.
    </Warning>
  </Step>
</Steps>

## Security notes

<AccordionGroup>
  <Accordion title="Keys are hashed at rest" icon="lock">
    Keys are stored salted-hashed (Argon2id) — we cannot reveal a key after it leaves the create-modal. This is why losing the plaintext requires a reissue.
  </Accordion>

  <Accordion title="HTTPS only" icon="shield-halved">
    All API traffic is HTTPS-only. HTTP requests are refused at the edge with a redirect to `https://`. TLS 1.2+ is required.
  </Accordion>

  <Accordion title="Header-only, never query string" icon="link-slash">
    Keys are never sent as query parameters — always as `Authorization: Bearer …` headers. URLs end up in access logs; headers do not.
  </Accordion>

  <Accordion title="Server-side only" icon="server">
    The browser is **not** a supported runtime. If you find yourself wanting to call Vibefollow from a SPA, build a thin server proxy — never inline the key.
  </Accordion>
</AccordionGroup>

## What `AuthError` means

When the SDK throws [`AuthError`](/sdk/errors#autherror), one of these is true:

* The key is missing from the request.
* The key prefix is malformed (`sk_live_…` expected).
* The key has been revoked or never existed.
* The key belongs to a project the requested resource isn't part of.

The error message will tell you which. There is no automatic retry — this is a configuration problem on your side.
