> For the complete documentation index, see [llms.txt](https://engyon.gitbook.io/engyon/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://engyon.gitbook.io/engyon/en/api/basic-operations.md).

# Basic operations

This page explains how to authenticate against the Engyon API and how to perform requests in general. All examples use `curl` so you can translate them to the language of your choice.

### Obtaining an access token

The API uses the OAuth 2.0 **client credentials** grant. Exchange the `client_id` and `client_secret` of your service account for a short-lived access token.

**Endpoint**

```
POST https://audit.engyon.io/api/v1/oauth/token/
```

**Request**

```bash
curl -X POST "https://audit.engyon.io/api/v1/oauth/token/" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"
```

**Successful response**

```json
{
  "access_token": "........",
  "expires_in": 3600,
  "token_type": "Bearer",
  "scope": "read write"
}
```

| Field          | Description                                               |
| -------------- | --------------------------------------------------------- |
| `access_token` | The token you include with API requests                   |
| `expires_in`   | Token validity in seconds (currently **3600** — one hour) |
| `token_type`   | Always `Bearer`                                           |
| `scope`        | Assigned scopes                                           |

{% hint style="info" %}
Request a new token before the current one expires. Do not hardcode tokens in your integration; retrieve them at runtime and cache them until shortly before expiration.
{% endhint %}

### Authenticating an API request

Include the access token in the `Authorization` header of every API request:

```
Authorization: Bearer YOUR_ACCESS_TOKEN
```

**Example — retrieving client organisations**

```bash
curl -X GET "https://audit.engyon.io/api/v1/client-organisations/" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"
```

**Example — creating a resource (JSON body)**

```bash
curl -X POST "https://audit.engyon.io/api/v1/client-organisations/" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
    "id": "fictiva-holding",
    "name": "Fictiva Holding",
    "country": "NL",
    "domains": ["fictiva.example"],
    "allow_password_login": false
  }'
```

### Common response patterns

#### Success

| Status           | Meaning                                                  |
| ---------------- | -------------------------------------------------------- |
| `200 OK`         | The request succeeded (read or update)                   |
| `201 Created`    | A new resource was created                               |
| `204 No Content` | The resource was successfully deleted (where applicable) |

#### Errors

| Status                  | Meaning                                                                              |
| ----------------------- | ------------------------------------------------------------------------------------ |
| `400 Bad Request`       | Validation failed. The body contains error messages per field.                       |
| `401 Unauthorized`      | Missing or invalid access token. Obtain a new token.                                 |
| `402 Payment Required`  | Insufficient credits to create the audit (only when creating audits).                |
| `403 Forbidden`         | The service account does not have the required role/permission.                      |
| `404 Not Found`         | The resource does not exist, or is not accessible to your audit firm.                |
| `409 Conflict`          | The operation conflicts with the current state (e.g., the user is already assigned). |
| `429 Too Many Requests` | You have exceeded the rate limit. Wait and try again (see Rate limiting).            |

### Pagination

*List* endpoints return a paginated envelope:

```json
{
  "count": 42,
  "next": "https://audit.engyon.io/api/v1/client-organisations/?page=2",
  "previous": null,
  "results": [
    { "...": "..." }
  ]
}
```

| Query parameter | Default | Description                                  |
| --------------- | ------- | -------------------------------------------- |
| `page`          | `1`     | Page number                                  |
| `page_size`     | `20`    | Number of results per page (maximum **100**) |

**Example**

```bash
curl -X GET "https://audit.engyon.io/api/v1/client-organisations/?page=1&page_size=50" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"
```

Follow the `next` URL until it is `null` to retrieve all pages.

### Rate limiting

API usage is rate-limited **per audit firm**. All service accounts of the same organisation share one quota. Therefore, creating extra API clients does not increase that quota.

Endpoints are grouped into **cost tiers**. Cheap, frequently used calls (e.g., retrieving reference data) have a higher limit than heavy operations such as creating an audit. Each operation in the [Swagger UI](https://audit.engyon.io/api/v1/docs/) specifies the tier it consumes (`Rate limit tier: read`).

Each tier has up to three windows:

| Window      | Period     | Purpose                                                      |
| ----------- | ---------- | ------------------------------------------------------------ |
| `burst`     | per minute | Absorbs a short batch without allowing a client to run wild  |
| `sustained` | per hour   | The budget your integration plans against                    |
| `daily`     | per day    | Prevents a slow leak from consuming an entire day's capacity |

A request is rejected as soon as **one** of the windows of the relevant tier is exhausted. A missing window means that tier is unlimited in that period.

| Tier          | What it covers                                                                                                   | Burst     | Sustained  | Daily        |
| ------------- | ---------------------------------------------------------------------------------------------------------------- | --------- | ---------- | ------------ |
| `reference`   | Reference and lookup data that is the same for every organisation (work packages, industries, roles, assertions) | 120 / min | 3,000 / hr | —            |
| `read`        | Single-object reads and small, audit-related lists                                                               | 180 / min | 6,000 / hr | 60,000 / day |
| `read_heavy`  | Reads that span shards or retrieve an audit-wide overview (audits, checks, files, file requests)                 | 30 / min  | 900 / hr   | 8,000 / day  |
| `write`       | Write actions on a small number of rows without cascade (amounts, scope, metadata, assignments)                  | 120 / min | 4,000 / hr | 40.000 / day |
| `write_heavy` | Write actions that cascade through a tree, copy files across the audit network, or call S3/Kafka                 | 30 / min  | 1,000 / hr | 10,000 / day |
| `lifecycle`   | Creation, deletion, and scope resync of audits and component audits                                              | 5 / min   | 60 / hr    | 300 / day    |
| `token`       | Issuance of OAuth2 access tokens (`POST /api/v1/oauth/token/`)                                                   | 10 / min  | 120 / hr   | —            |

The `token` tier counts **per `client_id`**, not per audit firm: one integration cannot exhaust the token budget of another.

{% hint style="info" %}
Current limits can be retrieved via `GET /api/v1/rate-limits/`. The table above shows the configuration at the time of writing; in case of a change, that endpoint is leading.
{% endhint %}

**Retrieving current limits**

```bash
curl -X GET "https://audit.engyon.io/api/v1/rate-limits/" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Accept: application/json"
```

**When exceeded**

* The API responds with `429 Too Many Requests`
* The response contains a `Retry-After` header with the number of seconds you must wait before trying again
* If multiple windows are exhausted at the same time, `Retry-After` is the longest remaining wait time

Design your integration to respect `Retry-After` and avoid unnecessary polling. Cache reference data (e.g., work packages and industries) where it makes sense.

### Organisation scope

Remember from the [Introduction](/engyon/en/api/introduction.md):

* The audit firm is always derived from the service account.
* Resource `id` values are external IDs.
* You can only see and modify data belonging to your audit firm.

### Interactive documentation

For the complete list of endpoints, request bodies, and response schemas, open the Swagger UI: <https://audit.engyon.io/api/v1/docs/>

You must be logged into Engyon to open this page. The raw OpenAPI schema is available at <https://audit.engyon.io/api/v1/schema/>.

### What to read next

Continue with the [Use cases](/engyon/en/api/use-cases.md) to implement concrete flows, such as creating a client organisation or an audit.
