---
key: api-authentication
title: Authenticate with OAuth 2.1 or an API key
audience: developer
section: api/authentication
routes: []
since: 0.0.1
updated: 0.0.1
status: published
summary: Get a token as a connected app (OAuth 2.1 with PKCE), as an AI teammate (client credentials) or as an integration (API key), and send it on every call.
keywords: [oauth, token, api key, client credentials, pkce, authentication, scopes]
related: [api-overview, api-scopes-reference, api-limits, run-an-ai-teammate, connect-an-ai-agent, api-conventions]
---

Nova is its own OAuth 2.1 authorization server. Discovery documents live at
the origin root, so a standards-based client needs only the base URL:

```
GET https://app.novabmp.com/.well-known/oauth-authorization-server
GET https://app.novabmp.com/.well-known/oauth-protected-resource
```

## Connected apps: authorization code with PKCE

Use this when a person connects your app to their own Nova account.

1. Register your client once (dynamic registration is open to public clients):

```http
POST /oauth/register
Content-Type: application/json

{"client_name": "My assistant", "redirect_uris": ["https://example.com/callback"],
 "grant_types": ["authorization_code", "refresh_token"], "token_endpoint_auth_method": "none"}
```

   The response carries `client_id`. Keep the `registration_access_token` if
   you want to read or delete the registration later.

2. Send the person to `/oauth/authorize` with `response_type=code`, your
   `client_id`, a registered `redirect_uri`, the `scope` you need (space
   separated, see below), a random `state`, and a PKCE `code_challenge`
   (`S256`). Nova shows them what you ask for; they choose the organization
   and whether you act as them or as one of their agent teammates.

3. Exchange the code:

```http
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=...&redirect_uri=...&client_id=...&code_verifier=...
```

   You receive `access_token` (a short-lived JWT), `refresh_token` and the
   granted `scope`. Refresh with `grant_type=refresh_token`; each refresh token
   works once and replaying one revokes the whole connection.

## AI teammates and integrations: client credentials

A person in the organization adds the AI teammate under **Administration ›
Team & Access › AI Teammates** (or the API key under **Administration ›
Integrations & API › API & Connected Apps**) and gives you a `client_id` and
secret.

```http
POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&client_id=...&client_secret=...&scope=accounts:read quotes:write
```

An organization API key can also be sent directly as a bearer token
(`Authorization: Bearer nova_live_...`) without exchanging it first.

## Sending the token

```http
GET /v1/crm/accounts/?q=smith
Authorization: Bearer <access_token>
```

Machine credentials are bound to one organization; you do not send
`X-Organization-ID`. A browser session token must send it.

## Scopes

A `scope` value is a space-separated list of **bundles** (short names such as
`accounts:read`, `invoices:write`, `files:write`, `organization:read`,
`help:read`), **permission keys** (such as `billing.invoices.void`), or both.
Bundles expand to the permission keys they stand for; the full list is in
[[api-scopes-reference]]. A grant narrows a token to what it needs and never
widens what the person or AI teammate could do themselves. Anything an
organization can do can be granted, including deletes, voids and settings; a
grant is refused only when it reaches past what the person approving it holds.

## Revoking

- A person disconnects an app from **Profile › Connected apps**.
- An admin disconnects any app or revokes an API key under
  **Administration › Integrations & API › API & Connected Apps**, and pauses
  or retires an AI teammate under **Administration › Team & Access › AI
  Teammates**.
- A client can revoke its own token at `POST /oauth/revoke`.

Tokens stop working immediately; there is no grace period.
