Welcome to the **Hyperproof TPRM Core API** — the external REST API and webhooks for
Third-Party Risk Management. Use it to manage vendors, read assessments and
documents, follow continuous risk monitoring, request exports, and receive
real-time webhook events.

**Base path:** every endpoint is located under `/tprm-api`. Choose your
environment (Development / Demo / Production) from the **Servers** dropdown.

## Versioning

URLs never contain a version. The API version is selected with the
`X-API-Version` request header; omitting it uses the current default (`v1`):

```
X-API-Version: v1
```

Sending an unsupported version returns `400` with
`client_code: request.api_version_unsupported`. New versions will be announced
ahead of time — pinning `X-API-Version` explicitly is recommended so a future
default change never breaks your integration.

## Quick start

```bash
# 1. Exchange your client credentials for a short-lived access token
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":"…"}

# 2. Call the API with that token ({} lists all vendors, newest first)
curl -X POST https://api.expent.ai/tprm-api/vendors/search \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" -d '{}'
```

## Where to go next

- **Authentication** — how to get credentials, exchange them for a token, and
limit what a token can do. Start here.
- **DPoP** — optional sender-constrained tokens for high-security integrations.
- **Vendors, Assessments, Monitoring, Documents, Exports, Downloads, Stats** —
the reference pages for each part of the API.
- **Webhooks** — subscribe to real-time events instead of polling.


## Conventions

A few rules apply across the whole API. They are included here rather than
repeated for each endpoint.

- **PATCH vs PUT.** `PATCH` updates only the fields you send. `PUT` is a
**full replace**: any writable field you leave out is cleared. Always send
the complete resource with a `PUT`.
- **Archive vs delete.** `POST /vendors/{id}/archive` is reversible and keeps
all of the vendor's data. `DELETE /vendors/{id}` is permanent: it removes the
vendor and everything that belongs to it. When in doubt, archive.
- **Idempotency.** Send an `Idempotency-Key` header on create and export
calls. If you repeat the same request within 24 hours, you get the original
result back instead of a duplicate.


## Dates and times

Every date and datetime is returned by the API in **your organization's time zone
and date format** (the same settings your Hyperproof UI uses). Datetimes contain
the time after a space — for example, with the `MM/DD/YYYY` format (`-` is always the date separator):

```
"started_at": "07-01-2026 14:30:00"     # datetime
"tprm_contract_end_date": "12-31-2026"  # date-only field
```

Send date values in the same format on writes (ISO 8601 is also accepted);
the API converts to UTC for storage.

## Errors

Every non-2xx response returns the same JSON structure, shown below. Branch your code on
`client_code` — it is stable and machine-readable. The `message` field is
human-readable text and may change over time. The same `request_id` is also
returned in the `X-Request-Id` response header; include it when you contact
support.

```json
{"error": {"client_code": "vendor.not_found", "message": "Vendor not found.",
           "request_id": "req_9f2…", "docs_url": "/tprm-api/docs#errors-vendor.not_found"}}
```

## Retries — 429 and 503

Two statuses can be retried, and both contain a `Retry-After` header (seconds):

- `429` (`request.rate_limited`) — you exceeded your client's rate limit.
- `503` (`internal.service_unavailable`) — the service is briefly congested or
a dependency is unavailable. Capacity recovers automatically; your request
was not processed.


Handle both the same way: wait at least `Retry-After` seconds, then retry with
exponential backoff and jitter — double the wait after each failed attempt, and
add a small random amount to it so that many clients do not all retry at the
same instant. Treat `503` as normal flow-control, not an
outage — during a traffic surge the API sheds excess requests immediately
instead of queueing them, so retries a few seconds later usually succeed.
Only non-idempotent requests need care: re-send a `POST /vendors` retry with
the same `Idempotency-Key` so it can never create a duplicate.

## Response headers

- `X-Request-Id` — the correlation id for the request.
- `X-Response-Time-Ms` — time spent inside the service, excluding network and
load-balancer time.