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

# Authentication

> All requests to tenant-scoped endpoints require a Firebase Auth JWT.

All tenant-scoped endpoints require an `Authorization: Bearer <token>` header. Tokens are issued by Google Identity Platform (Firebase Auth) after sign-in.

## Token format

Tokens are standard OIDC JWTs. The issuer is `https://securetoken.google.com/{projectId}`. The platform validates the token on every request — no session or API key is stored server-side.

The `principal_id` derived from your token takes the form:

```
oidc:{projectId}#{firebaseUid}
```

This is the identifier used in member records, audit logs, and ownership attribution.

## Getting a token

If you're calling the API from a browser-based app:

```javascript theme={null}
import { getAuth } from "firebase/auth";

const auth = getAuth();
const token = await auth.currentUser.getIdToken();
// token is valid for 1 hour; refresh as needed
```

For server-to-server or testing use the Firebase Admin SDK or the REST sign-in endpoint with a service account.

## Using the token

Pass the token as a Bearer header on every request:

```bash theme={null}
curl -H "Authorization: Bearer $TOKEN" \
  https://tally-platform-api-iikaevm4pq-ue.a.run.app/v1/meta
```

## Access control

Authentication establishes your identity. Authorization is a separate layer:

* **Tenant membership** — your principal must be a member of the tenant you're acting on behalf of, with an appropriate role.
* **Ownership** — writes and administrative actions require the tenant to own the subject.
* **Grants** — counterparty read access is controlled by explicit grants issued by the owning tenant, plus a per-principal `allow_counterparty_access` flag.

See [Access Grants](/concepts/grants) and [Principals & Roles](/concepts/principals) for details.

## Principal types

Every principal has a `principal_type` that distinguishes how it authenticates and operates:

| Type              | Description                                                                                                                 |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `human`           | An interactive user authenticated via OIDC/Firebase. Default for all new principals.                                        |
| `service_account` | A non-interactive integration credential (API key, CI runner, etc.). Deterministic, same code path every call.              |
| `ai_agent`        | A reasoning-based caller that invokes an LLM as part of its operation. Non-deterministic; requires explicit classification. |

Register the type when adding a non-human principal to a tenant:

```bash theme={null}
curl -X PUT https://tally-platform-api-iikaevm4pq-ue.a.run.app/v1/tenants/{tenant_id}/principals/{principal_id} \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "api_functional_role": "tenant_reader",
    "principal_type": "service_account"
  }'
```

### Human-only constraints

Certain privileged actions require the **calling** principal to have `principal_type: human`. Service account and AI agent callers are rejected with `422`:

* **Setting `allow_counterparty_access: true`** — enabling counterparty data visibility for a principal must be an explicit human decision. The `account_admin` making this call must be using their own OIDC session token, not an API key.
* **Assigning `api_functional_role: tenant_owner`** — the ownership accountability role may not be held by an automated caller.

Revoking these (setting `allow_counterparty_access: false` or downgrading from `tenant_owner`) carries no human-caller requirement.

## Unauthenticated endpoints

The following endpoints do not require a token:

* `GET /health`
* `GET /v1/meta`
* `GET /v1/ops/readiness`
