# Security

## Tokens

All requests to the Apentis API must include a **Bearer token** in the `Authorization` header:

```http
Authorization: Bearer {{api_token}}
```

### Token lifecycle

* **Type:** static API tokens (for now).
* **Validity:** tokens do **not expire automatically** — rotation is managed by the client and Apentis support.
* **Scope:** each token is tied to a client (a tenant) and an environment (test vs. production).

### Best practices

* Store tokens securely in a **secrets manager** or key vault.
* Never commit tokens to code repositories, support tickets, or logs.
* Rotate tokens periodically (recommended every **90 days**) or when staff change.
* Use separate tokens per environment (Test, Production).

### Storage at Apentis

* Tokens are generated with **≥ 256 bits of entropy**.
* At rest, tokens are stored only in **hashed form** (bcrypt).
* During validation, the presented token is hashed and compared against the stored hash in constant time.

### Error codes

Invalid or missing token → **401 Unauthorized**

```json
{
  "error": "Unauthorized - Missing or invalid token",
  "code": 401
}

```

## IP whitelisting

To strengthen security and reduce attack surface, the Apentis API supports **IP whitelisting**. This ensures that only requests originating from **trusted IP addresses** can access the API.

### Default behavior

By default:

* API access is **restricted to whitelisted IP addresses** per client.
* If **no IPs are whitelisted**, the client will be **denied access** by default (fail-closed).

### How to configure whitelisted IPs ?

During onboarding or via support, clients can provide one or more static IPs (e.g. backend server, proxy).

You can request updates to your IP whitelist at any time by contacting [Apentis support](mailto:support@apentis.eu) with:

* Your `customer_id`
* List of authorized IP addresses (IPv4 or IPv6)
* Optional labels (e.g. "Prod server", "Backup VPN")

### If your IP is not allowed

Blocked requests will receive:

```html
HTTP/1.1 403 Forbidden
Content-Type: application/json
```

```json
{
  "error": "FORBIDDEN",
  "message": "Your IP address is not authorized to access the API.",
  "code": "IP_WHITELIST_BLOCKED",
  "ip": "203.0.113.55",
  "timestamp": "2025-08-29T08:45:00Z"
}
```

### Supported configuration (recommended)

For cloud environments (AWS, Azure, GCP), you must ensure static outbound IP addresses.

#### Production (required)

Production environments must use static public egress IP(s).

For example in AWS:

* Deploy your application in private subnets
* Route outbound traffic through a NAT Gateway
* Associate the NAT Gateway with an Elastic IP
* Provide us with the Elastic IP(s) to allowlist

If you operate in multiple availability zones, you may have multiple NAT Gateways and therefore multiple Elastic IPs. All must be provided for allowlisting.

#### Development / Test

Development environments may use:

* One or more static public IP addresses
* A fixed NAT egress IP
* A corporate office IP

Please provide the exact outbound public IP(s) used when calling the API.

### Unsupported configurations

The following setups are not supported:

* Dynamic / rotating outbound IPs
* Serverless environments without fixed egress configuration
* Allowlisting entire cloud provider IP ranges
* Temporary IP check bypass for production

For security reasons, Apentis does not allow broad IP ranges or dynamic IP-based access in production.

### IP rotation or dynamic IPs

If your current setup results in rotating outbound IP addresses (e.g. default Elastic Beanstalk configuration, serverless functions, or auto-scaling public instances), you must implement static egress before going live.

If you are unsure how to configure static outbound IPs in your cloud provider, please contact your infrastructure team. We are happy to clarify the requirements from our side.

## API rate limits

To ensure fair usage and protect the platform from abuse, the Apentis API enforces **rate limits** on all incoming requests.

### Default rate limit

By default, each authenticated client is allowed:

* **1,000 requests per hour**
* With a **burst capacity** of up to **20 requests within 5 seconds**

{% hint style="info" %}
These limits apply **per client** (based on your API token)
{% endhint %}

### What is burst capacity?

The API allows occasional short bursts of traffic — for example, if your system sends several requests at once — without blocking them immediately. However, sustained high-frequency requests will be throttled once the hourly limit is exceeded.

This means:

* You can send up to **20 requests in a quick burst** without delay.
* As long as your total stays below **1,000 requests per hour**, your traffic will be accepted.

### What happens if you exceed the limit?

If your application exceeds the allowed request rate, the API will return the following:

```html
HTTP/1.1 429 Too Many Requests
```

With additional headers:

```html
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1720000000
Retry-After: 120
```

| Header                  | Description                               |
| ----------------------- | ----------------------------------------- |
| `X-RateLimit-Limit`     | Your total hourly quota                   |
| `X-RateLimit-Remaining` | Requests you have left in this hour       |
| `X-RateLimit-Reset`     | UNIX timestamp when your quota resets     |
| `Retry-After`           | Number of seconds to wait before retrying |

### Best practices

* Monitor the rate limit headers to **avoid being blocked**.
* If your application receives a `429 Too Many Requests` response, implement a **retry mechanism** with exponential backoff.
* Contact us if you require a **custom limit** for your integration.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://apentis.gitbook.io/apentis-api/security.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
