# Introduction

How the API is structured, how authentication works, and how multi-tenant requests are scoped

The API is a multi-tenant REST service. Every endpoint speaks JSON, returns the same response
envelope, and uses the same error contract. Tenant-scoped resources live under an organization id
carried in the URL path. This page is the mental model you need before calling any endpoint.

## Base URL

All examples in these docs use the production base URL:

```text
https://api.indexhog.com
```

> **Warning:** **HTTPS only:** All requests must use HTTPS. The API sends HSTS with a one-year max-age and
> rejects mixed-content flows.

## Machine-readable discovery

Use these unauthenticated resources to configure API clients, code generators, and agents:

- [OpenAPI 3.1 specification](https://api.indexhog.com/agents/openapi.json) — Machine-readable contract for API clients and code generators.
- [Capabilities manifest](https://api.indexhog.com/agents/capabilities.json) — Agent-oriented inventory of endpoints, scopes, authentication, and features.

## Authentication Methods

The API accepts three authentication methods. Pick based on who is calling.

### Session cookie — browser / first-party app

Users sign in through one of the auth endpoints (email + password, magic link, passkey, social, or SSO). A successful sign-in sets an HTTP-only session cookie. Browsers send it automatically when you make requests with `credentials: 'include'`; a server acting on behalf of a signed-in user must echo the cookie back on each call.

Use this method for browser apps and any server-rendered UI calling the API on behalf of a signed-in user.

### Personal Access Token — scripts, CI, integrations

A Personal Access Token (PAT) is a long-lived credential created while signed in (see [Quick Start](/docs/get-started/quick-start)). Send it on every request as:

```text
X-Api-Key: tkn_AbCdEf0123456789...
```

PATs carry an explicit scope list (a subset of the user's permissions) and can optionally be pinned to a single organization.

### OAuth 2.1 access token — third-party apps

Third-party applications use the OAuth 2.1 Authorization Code flow against the built-in Better Auth OAuth Provider and call the API with the resulting access token:

```text
Authorization: Bearer eyJhbGciOi...
```

Use this method when your app acts on behalf of users who are not your own — i.e. a public OAuth integration. There is no password grant.

## Organization Context

Most endpoints are tenant-scoped. The authenticated principal may belong to several organizations, so tenant routes carry the active organization id directly in the URL path:

```text
/api/user/organizations/{organizationId}/projects
```

- **Account routes** (`/api/user/me`, `/api/user/oauth-clients`, ...) are not tenant-scoped.
- **Tenant routes** (`/api/user/organizations/{organizationId}/...`) require a valid organization id in the URL. Requests without it fail with `TENANT_CONTEXT_MISSING`.
- **Public routes** (`/api/public/*`) and **auth routes** (`/api/auth/*`) are not tenant-scoped.
- An organization-pinned PAT rejects requests whose `:organizationId` URL segment does not match the pin.

See [API Structure](/docs/get-started/api-structure) for the full request/response contract.

## Route Tiers

Every route belongs to exactly one tier. The tier determines which auth it demands.

| Tier         | Mount                                       | Auth                                          | Purpose                                          |
| ------------ | ------------------------------------------- | --------------------------------------------- | ------------------------------------------------ |
| Public       | `/api/public/*`                             | none                                          | Health, invite preview                           |
| Auth         | `/api/auth/*`                               | per endpoint                                  | Sign-in, sign-up, MFA, passkeys, OAuth, SSO      |
| User Account | `/api/user/*`                               | session, PAT, or OAuth token                  | `/me`, `/oauth-clients`, notifications, ...      |
| User Tenant  | `/api/user/organizations/:organizationId/*` | session, PAT, or OAuth token + org membership | Projects, payments, members, settings            |
| Webhook      | `/api/webhook/*`                            | provider-signed (inbound only)                | Inbound provider callbacks — never called by you |

## Request Envelope

Every JSON response uses the same shape:

```json
{
"success": true,
"status": 200,
"code": "OK",
"message": "User fetched successfully",
"data": { "...": "..." },
"meta": { "...": "..." }
}
```

- `success` — boolean, mirrors `status < 400`.
- `status` — HTTP status code as a number.
- `code` — machine-readable code (`OK` on success, `VALIDATION_ERROR`, `NOT_FOUND`, `RATE_LIMIT_ERROR`, ... on failure).
- `message` — human-readable summary.
- `data` — the resource or resource list on success.
- `meta` — pagination, validation error details, or other structured metadata.

See [API Structure](/docs/get-started/api-structure) for pagination and query conventions and [Error Handling](/docs/get-started/errors) for the full error code list.

## Best Practices

- **Pick the right credential:** Sessions for first-party UIs, PATs for headless automation, OAuth Bearer tokens for third-party apps acting on behalf of other users' accounts.
- **Put the organization id in the URL path:** Every tenant-scoped route nests under `/api/user/organizations/{organizationId}/...`. The API never guesses from a default.
- **Store credentials as secrets:** Treat PATs and OAuth client secrets like passwords. Use environment variables or a secret manager — never commit them.
- **Request the minimum scopes:** PATs and OAuth clients take an explicit scope list. Ask only for what you actually need.
- **Check `code`, not just HTTP status:** The `code` field is stable; HTTP status classes can shift as the API evolves.
- **Handle 401 and 403 differently:** `401` means re-authenticate (session expired / token invalid); `403` means your credential cannot access this resource no matter how you retry.

## Next Steps

1. **Make your first call:** Follow [Quick Start](/docs/get-started/quick-start) to sign in and issue a PAT.
2. **Learn the envelope:** Read [API Structure](/docs/get-started/api-structure) for pagination, headers, and request conventions.
3. **Handle failures:** Review [Error Handling](/docs/get-started/errors) before you ship.
4. **Budget your calls:** Check [Rate Limits](/docs/get-started/rate-limits) to understand request budgets.
5. **Lock down scopes:** See [Scopes & Permissions](/docs/get-started/scopes) before creating production keys.