# Authentication

The Hyperproof TPRM Core API uses **OAuth 2.1 `client_credentials`**. You exchange a
client id and a client secret for a short-lived access token, and then send
that token as a Bearer token on every request.

## 1. Get your API credentials

Before you can call the API, you need a **client_id** and a **client_secret**.
They are issued per organization from the **Hyperproof app**: an organization admin
opens the API credentials screen and creates a client.

Each organization has **one client_id**, which can hold **multiple secrets**,
each with its own set of scopes. This lets you:

- issue a separate secret for each integration, and revoke just that one;
- **rotate without downtime** — create a new secret, migrate your integration
to it, then retire the old one. Both secrets work during the overlap.


The plaintext secret is shown **only once**, at creation (or through a one-time
reveal link). Store it somewhere safe. Hyperproof keeps only an encrypted copy
and can never show it to you again; if you lose it, rotate the secret to get a
new one.

Credentials are managed in the Hyperproof app under your organization login, not through
this external API. There is deliberately no endpoint here that returns a
plaintext secret.

## 2. Exchange credentials for a token

Send `grant_type=client_credentials` in the form body, and authenticate with
HTTP Basic (`client_id:client_secret`):

```bash
curl -X POST https://api.expent.ai/tprm-api/oauth/token \
  -u "CLIENT_ID:CLIENT_SECRET" \
  --data-urlencode grant_type=client_credentials
# -> {"access_token":"eyJ…","token_type":"Bearer","expires_in":600,"scope":"…"}
```

Access tokens are **self-contained JWTs** and are valid for **10 minutes**.
Hyperproof keeps no server-side session, so there is no refresh token for this
grant. When a token expires, simply request a new one.

## 3. Call the API

Send the token as a Bearer token:

```bash
curl -X POST https://api.expent.ai/tprm-api/vendors/search \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" -d '{}'
```

## Scopes — ask only for what you need

Your client is granted a fixed set of scopes. Omit the `scope` parameter on the
token request to receive all of them, or pass a space-separated subset to limit
the token. Requesting a scope your client was not granted returns
`400 invalid_scope`.

```bash
curl -X POST https://api.expent.ai/tprm-api/oauth/token \
  -u "CLIENT_ID:CLIENT_SECRET" \
  --data-urlencode grant_type=client_credentials \
  --data-urlencode 'scope=tprm.vendors.read tprm.vendors.write'
```

| Scope | Grants |
|  --- | --- |
| `tprm.vendors.read` | List, search, and read vendors and vendor fields. |
| `tprm.vendors.write` | Create, update, archive, and delete vendors. |
| `tprm.assessments.read` | List and read assessments and their responses. |
| `tprm.documents.read` | List a vendor's documents and download their files. |
| `tprm.monitoring.read` | Read vendor risk-monitoring status and results. |
| `tprm.exports.read` | Request assessment, risk-register, and risk-monitor exports; poll and download them. |
| `tprm.users.read` | Export your organization's users. The user directory is PII, so it has its own scope. |
| `tprm.stats.read` | Read your organization's daily TPRM statistics snapshots. |


## Need stronger token security?

For integrations that require it, tokens can be **sender-constrained with
DPoP**, which makes a stolen token useless without the matching private key.
See the **DPoP** guide.