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

# Authentication & Getting Started

> How to authenticate with the Matproof REST API, base URLs, headers, error handling, rate limits, and pagination.

# Authentication & Getting Started

The Matproof REST API exposes everything you can do in the app — manage frameworks, controls, evidence, vendors, risks, findings, people — programmatically. This page covers the basics every API client needs.

## Base URL

```
https://api.matproof.com/v1
```

All endpoints are versioned under `/v1`. Breaking changes will ship under `/v2` — `/v1` stays stable for at least 12 months after a successor version is released.

For local development against a self-hosted Matproof instance, point at your own host (the OpenAPI spec also lists `http://localhost:3333` for the typical dev port).

## Authentication

Every request requires an **API key** sent in the `X-API-Key` header:

```
X-API-Key: mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

API keys are scoped to a specific organization and act with the [role](/features/rbac-roles) you assign at creation. Treat them like passwords — never commit them to source control, never expose them client-side.

### Getting an API key

<Steps>
  <Step title="Open Settings → API Keys">
    Only **Owner** and **Admin** roles can create API keys.
  </Step>

  <Step title="Click New API Key">
    * **Name** — describe what this key is for (e.g. "GitHub Actions evidence push")
    * **Acting role** — pick the role this key acts as. Use **Auditor** for read-only integrations
    * **Expires** — optional; recommended for keys with broad access
  </Step>

  <Step title="Copy the key once">
    Matproof shows the key exactly once. Store it in your secrets manager immediately. If you lose it, revoke and create a new one.
  </Step>
</Steps>

### Optional: organization scoping

When a single user has access to multiple organizations, set `X-Organization-Id` to scope each request:

```
X-Organization-Id: org_abc123
```

For API keys created within a specific organization, the org is inferred from the key and this header is optional.

## Your first request

```bash theme={null}
curl https://api.matproof.com/v1/people \
  -H "X-API-Key: mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

Response:

```json theme={null}
{
  "data": [
    { "id": "mem_abc", "name": "Alex Kim", "email": "alex@example.com", "role": "admin" },
    { "id": "mem_def", "name": "Sam Patel", "email": "sam@example.com", "role": "employee" }
  ],
  "meta": { "total": 12, "page": 1, "perPage": 50 }
}
```

## Headers reference

| Header              | Required              | Purpose                                                                                  |
| ------------------- | --------------------- | ---------------------------------------------------------------------------------------- |
| `X-API-Key`         | Always                | Authentication                                                                           |
| `X-Organization-Id` | When ambiguous        | Selects which organization to operate against                                            |
| `Content-Type`      | On `POST` / `PATCH`   | Always `application/json`                                                                |
| `Idempotency-Key`   | Recommended on `POST` | Prevents duplicate writes if the request retries (any unique string per logical request) |

## Pagination

List endpoints return paginated results. Default page size is 50; max is 200.

```bash theme={null}
curl "https://api.matproof.com/v1/people?page=2&perPage=100" \
  -H "X-API-Key: ..."
```

Response includes `meta`:

```json theme={null}
{
  "data": [...],
  "meta": {
    "total": 215,
    "page": 2,
    "perPage": 100,
    "totalPages": 3
  }
}
```

## Error responses

Errors return a standard shape with HTTP status codes:

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "Email already exists for another member of this organization",
    "details": {
      "field": "email",
      "value": "alex@example.com"
    }
  },
  "requestId": "req_xyz789"
}
```

| HTTP  | Code               | When                                                                           |
| ----- | ------------------ | ------------------------------------------------------------------------------ |
| `400` | `validation_error` | Invalid request body or parameters                                             |
| `401` | `unauthorized`     | Missing or invalid API key                                                     |
| `403` | `forbidden`        | API key's role lacks permission for this operation                             |
| `404` | `not_found`        | Resource doesn't exist or isn't visible to your org                            |
| `409` | `conflict`         | Idempotency key reused for a different payload, or unique-constraint violation |
| `429` | `rate_limited`     | Rate limit exceeded — see `Retry-After` header                                 |
| `5xx` | `internal_error`   | Server-side error — safe to retry with same idempotency key                    |

Always log the `requestId` — quote it when contacting support.

## Rate limits

Per-API-key request budgets:

| Plan         | Requests / minute | Burst      |
| ------------ | ----------------- | ---------- |
| Free         | 30                | 60         |
| Starter      | 60                | 120        |
| Professional | 300               | 600        |
| Enterprise   | Negotiated        | Negotiated |

Responses include rate-limit metadata:

```
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 287
X-RateLimit-Reset: 1714828800
```

When you hit the limit, you get `429` with a `Retry-After` header (seconds). Back off and retry.

## Idempotency

For any `POST` that creates a resource, send `Idempotency-Key: <unique-string>`. Matproof remembers the result for 24 hours — retrying with the same key returns the original response without creating a duplicate.

Use a stable, unique string per logical request: a UUID generated by your code, or a deterministic hash of the request payload.

```bash theme={null}
curl -X POST https://api.matproof.com/v1/findings \
  -H "X-API-Key: ..." \
  -H "Idempotency-Key: 9f3a-2b8c-4d12-finding-create" \
  -H "Content-Type: application/json" \
  -d '{ "title": "Vulnerable npm package", "severity": "high" }'
```

## Webhooks

Matproof pushes events to endpoints you configure when:

* A questionnaire is submitted by a vendor
* A control's status changes
* Evidence is about to expire
* A finding is created or its severity changes
* A risk score changes

Configure under **Settings → Webhooks**. Each delivery includes an `X-Matproof-Signature` header (HMAC-SHA256 of the raw body with your webhook secret) — verify it before processing.

## Security best practices

* Store API keys in a secrets manager (AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault, 1Password, Doppler). Never in `.env` files committed to git.
* Issue **one key per integration** — when you decommission an integration, revoke its key
* Use the **Auditor** role for read-only keys (most integrations don't need write)
* Set **expirations** on keys with broad access; rotate before expiry
* Monitor key usage in **Settings → API Keys → \[key] → Usage** and revoke any key not seen in 30 days

## SDKs and codegen

Matproof doesn't ship official SDKs yet. The OpenAPI spec at [openapi.json](https://docs.matproof.com/openapi.json) is OpenAPI 3.0 and works with standard generators:

* `openapi-generator-cli` — official multi-language generator
* `openapi-typescript` — TypeScript types
* `openapi-fetch` — typed fetch wrapper for TS
* `openapi-python-client` — Python client generator

For most use cases, raw HTTP via `fetch` / `requests` / `httpx` is simple enough that an SDK isn't necessary.

## Versioning

* The current major version is **v1**. All paths begin with `/v1/`.
* Backwards-compatible changes (new endpoints, new optional fields, new optional query params) are added to v1 without notice
* Breaking changes ship under `/v2` — v1 is maintained in parallel for at least 12 months
* Subscribe to API changelog notifications under **Settings → API Keys → Subscribe to API changelog**

## What's next

<CardGroup cols={2}>
  <Card title="Resource Reference" href="/api-reference/resources">
    Catalogue of every resource group — what each covers
  </Card>

  <Card title="Sample: List people" href="/api-reference/people-list">
    A typical list endpoint with full request / response shape
  </Card>
</CardGroup>
