> ## 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.

# API authentication

> Wire-level detail for non-SDK callers.

<Tip>
  The SDK handles this for you. Read on if you're calling the REST endpoints directly — from a language without an official SDK, or for ad-hoc `curl` debugging.
</Tip>

## The header

<Snippet file="auth-headers.mdx" />

## Full request example

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.vibefollow.com/api/v1/events \
    -X POST \
    -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" }
    }'
  ```

  ```ts Node (fetch) 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' },
    }),
  });
  ```
</CodeGroup>

A success response:

```http theme={null}
HTTP/1.1 202 Accepted
Content-Type: application/json

{ "data": { "accepted": true } }
```

<Info>
  `202 Accepted` confirms the event was enqueued, not that it has been fully processed. Vibefollow's ingest pipeline is asynchronous — the event lands in your project's event stream within a few hundred milliseconds.
</Info>

## Failure modes

<AccordionGroup>
  <Accordion title="401 Unauthorized" icon="key">
    Header missing, malformed, or key revoked. Body: `{ "errors": [{ "code": "auth_required", ... }] }`.
  </Accordion>

  <Accordion title="403 Forbidden" icon="shield-halved">
    Key valid but doesn't authorise access to the resource (e.g. wrong project).
  </Accordion>

  <Accordion title="422 Unprocessable Entity" icon="circle-exclamation">
    Request body failed validation. `errors[0].field` names the offending field.
  </Accordion>

  <Accordion title="429 Too Many Requests" icon="gauge-high">
    Rate limited. Respect the `Retry-After` header.
  </Accordion>

  <Accordion title="5xx Server error" icon="server">
    Safe to retry — the `Idempotency-Key` prevents duplicates.
  </Accordion>
</AccordionGroup>

The full mapping to typed SDK errors is documented in [Errors](/errors).

## Idempotency in detail

The `Idempotency-Key` is required on every mutating request — the SDK auto-generates one if you don't pass it. The backend dedupes within a 24-hour window keyed by `(project_id, idempotency_key)`.

| Scenario                                 | Result                                                            |
| ---------------------------------------- | ----------------------------------------------------------------- |
| Same key + same body within 24h          | Returns the **original** response (the second request is a no-op) |
| Same key + **different** body within 24h | Rejected with `422` (`code: "idempotency_key_reuse"`)             |
| Same key after 24h                       | The key is forgotten; safe to reuse                               |

<Warning>
  Use a fresh UUID per logical write. Don't share keys across unrelated events.
</Warning>

## CORS

The API does **not** send `Access-Control-Allow-Origin` headers. The browser cannot call Vibefollow directly — build a server proxy if you need browser-originated events.

## TLS

HTTPS only. Plaintext HTTP requests are refused at the edge with a redirect to `https://`. TLS 1.2+ required. We do not support TLS 1.0/1.1.
