# Request Headers

> Every request header API Maker understands - the tokens, the case and format of the response, flat objects, metadata, caching, encryption of the payload and the reply, the language of the messages, the tenant, the sandbox and compression - with an example of each.

Source: https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html

Headers change how API Maker answers a call, without changing the URL. They work on every API : generated, schema, custom and system. The response carries a few headers of its own, listed at the end.

## All headers

| Header | Values | What it does |
|---|---|---|
| [`x-am-authorization`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-authorization) | a token | The API user : which application calls. Needed unless the API is public. |
| [`x-am-user-authorization`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-user-authorization) | a token | The person, from a DB token generator. |
| [`x-aws-authorization`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-aws-authorization), [`x-google-authorization`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-google-authorization), [`x-azure-authorization`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-azure-authorization), [`x-custom-authorization`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-custom-authorization) | a token | The person, from AWS Cognito, Google, Azure AD or a custom provider. |
| [`x-am-response-case`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-response-case) | `camelCase`, `snakeCase`… | The case of every key of the answer. |
| [`x-am-content-type-response`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-content-type-response) | `application/json`, `text/xml`, `text/yaml`… | The format of the answer. |
| [`x-am-response-object-type`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-response-object-type) | `no_action`, `make_flat` | Flatten nested objects. |
| [`x-am-meta`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-meta) | `true`, `false` | Execution time, plan, groups and sandbox in `meta`. |
| [`x-am-cache-control`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-cache-control) | `no_action`, `reset_cache` | Drop the cached answers of the API before this call. |
| [`x-am-get-encrypted-data`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-get-encrypted-data) | `no_encryption`, `get_only_encryption`, `get_data_and_encryption` | Encrypt the answer. |
| [`x-am-encrypted-payload`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-encrypted-payload) | `true` | The body is encrypted. |
| [`x-am-internationalization`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-internationalization) | the name of a language | The language of the messages. |
| [`x-am-tenant-username`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-tenant-username) | a tenant | The tenant of a multi-tenant instance. |
| [`x-am-sandbox-timeout`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-sandbox-timeout) | milliseconds | How long the code of the call may run. |
| [`x-am-run-in-sandbox`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-am-run-in-sandbox) | `0`, `1`, `2`… | Which sandboxes may run the code. |
| [`x-no-compression`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#x-no-compression), [`accept-encoding`](https://docs.apimaker.dev/v4/docs/apis-all/header/requestHeader.html#accept-encoding) | `true` / `br`, `gzip`, `deflate`, `identity` | Compression of the answer. |

## x-am-authorization

The token of an [API user](https://docs.apimaker.dev/v4/docs/apis-security/api-user-permission.html) : the application calling. Its [groups](https://docs.apimaker.dev/v4/docs/apis-security/api-group-permission.html) decide which APIs, tables and fields the call may reach. The token comes from the [token API](https://docs.apimaker.dev/v4/docs/apis-all/system-apis/system-generated-token-api.html) and expires after `jwtOptions.expiresIn` seconds (72 hours by default, `expiresInSeconds` in the token request changes it).

```text
x-am-authorization: eyJhbGciOiJIUzI1NiIsInR1BydlRrblJlcS
```

- Missing or invalid : `401`. Valid but no group grants the API : `403`.
- Public APIs (`apiAccessType: IS_PUBLIC` in the [settings](https://docs.apimaker.dev/v4/docs/settings/apiSettings.html)) do not need it.
- Read it in your code from `g.req.auth.authAMUser`.

## x-am-user-authorization

The token of a **person**, a row of your own users table, made by a DB token generator ([auth provider](https://docs.apimaker.dev/v4/docs/authorization/AMDB.html)) through the token API. It is required when the settings of the database, the table, the API or the custom API list that provider in `authProviders`.

```text
x-am-user-authorization: eyJhbGciOiJIpXVCJBydlRrblJlcS
```

- Read it in your code from `g.req.auth.authAMDB` : the columns of the user selected by the generator, and the groups of the groups column.
- A token made for a tenant works for that tenant only, see [Multi-tenant](https://docs.apimaker.dev/v4/docs/features/multi-tenant.html#users-of-a-tenant).

## x-aws-authorization

The access token of AWS Cognito, when an [AWS auth provider](https://docs.apimaker.dev/v4/docs/authorization/sign-in-providers.html#aws) is configured. Read from `g.req.auth.authAWS`.

```text
x-aws-authorization: eyJhbGciOInR5cCI6IkpXVCJ9
```

## x-google-authorization

The id token of a Google sign-in, when a [Google auth provider](https://docs.apimaker.dev/v4/docs/authorization/sign-in-providers.html#google) is configured. Read from `g.req.auth.authGoogle`.

```text
x-google-authorization: eyJhbGciOiJIUzI1I6IkpXVCJ9
```

## x-azure-authorization

The token of Azure Active Directory, when an [Azure auth provider](https://docs.apimaker.dev/v4/docs/authorization/sign-in-providers.html#azure) is configured. Read from `g.req.auth.authAzure`.

```text
x-azure-authorization: eyJhbGciOiJIUzI1NI6IkpXVCJ9
```

## x-custom-authorization

The token of a [custom auth provider](https://docs.apimaker.dev/v4/examples/req/auth/authCustom.html) : your own generator and validator code. What the validator returns is in `g.req.auth.authCustom`.

```text
x-custom-authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
```

## x-am-response-case

Changes the case of every key of the answer, nested objects and arrays included. Default : `noChange`.

| Value | `first_name` becomes |
|---|---|
| `noChange` | `first_name` |
| `camelCase` | `firstName` |
| `capitalCase` | `First Name` |
| `constantCase` | `FIRST_NAME` |
| `dotCase` | `first.name` |
| `headerCase` | `First-Name` |
| `noCase` | `first name` |
| `paramCase` | `first-name` |
| `pascalCase` | `FirstName` |
| `pathCase` | `first/name` |
| `sentenceCase` | `First name` |
| `snakeCase` | `first_name` |

```text
x-am-response-case: camelCase
```

**Answer**

```json
{ "firstName": "JOHN", "lastName": "DOE" }
```

A value which is not in the table leaves the keys as they are.

## x-am-response-object-type

Flattens nested objects, such as the ones [deep](https://docs.apimaker.dev/v4/docs/apis-all/query-params/deep.html) produces, into one level with `_` between the names : handy for grids, CSV exports and spreadsheets. Default : `no_action`.

**no_action**

```json
{ "id": 101, "state_id": { "id": 201, "country_id": { "id": 301, "country_name": "INDIA" }, "state_name": "GUJARAT" }, "city_name": "AHMEDABAD" }
```

**make_flat**

```json
{ "id": 101, "state_id_id": 201, "state_id_country_id_id": 301, "state_id_country_id_country_name": "INDIA", "state_id_state_name": "GUJARAT", "city_name": "AHMEDABAD" }
```

## x-am-meta

Adds `meta` to the answer. Default : `false`.

**x-am-meta: true**

```json
{
    "data": [ { "id": 301, "country_name": "INDIA" } ],
    "meta": {
        "executionTime": "15ms",
        "executionTimeMS": 15,
        "apiAccessGroups": [ { "groupId": "6381b80359bdbd3a87c9abd5", "groupName": "all_permission", "hasAccess": true } ],
        "runBy": [ { "apiCategory": "CUSTOM_APIS", "serverId": "…", "serverName": "primary", "processId": "…", "port": 49731 } ]
    }
}
```

| Key | Meaning |
|---|---|
| `executionTime`, `executionTimeMS` | Time spent in API Maker, without the network. |
| `apiAccessGroups` | The groups of the API user and which one granted the call. |
| `runBy` | Which server and process answered, and the port of the sandbox which ran the code : for calls which run code (custom APIs, hooks). |

## x-am-internationalization

The name of a language of [i18N Management](https://docs.apimaker.dev/v4/docs/i18/i18.html). Every message of the answer, the messages of API Maker as well as your own from `errorList` and thrown strings, comes back in that language. The value `Default` (or no header) gives the original messages.

```text
x-am-internationalization: Hindi
```

**Default**

```json
{ "code": 400, "message": "Please provide id param value" }
```

**Hindi**

```json
{ "code": 400, "message": "कृपया आईडी पैरामीटर का मान प्रदान करें." }
```

**Chinese simple**

```json
{ "code": 400, "message": "请提供id参数的值" }
```

**Spanish**

```json
{ "code": 400, "message": "Proporcione el valor del parámetro id." }
```

**Japanese**

```json
{ "code": 400, "message": "id パラメータの値を入力してください" }
```

**Urdu**

```json
{ "code": 400, "message": "براہ کرم id پیرامیٹر کی قدر فراہم کریں۔" }
```

- Add a language, change a message, and the next call answers with it : no restart.

## x-am-run-in-sandbox

Which of the sandboxes of the account may run the code of this call (custom API, hooks, events). An account has one sandbox of a language on each server by default, for the whole API Maker process, and up to `sandboxCountForAdmin` while it is busy.

```text
x-am-run-in-sandbox: 0      # any sandbox (default)
x-am-run-in-sandbox: 1      # always the first sandbox
x-am-run-in-sandbox: 2      # one of the first two
x-am-run-in-sandbox: 3      # one of the first three
```

- Pin a call to one sandbox when your code keeps state in memory, such as a connection opened by a [process initializer](https://docs.apimaker.dev/v4/docs/features/process-initializers.html).

## x-am-content-type-response

The format of the answer. Default : `application/json`. The envelope is the same in every format.

| Value | The answer |
|---|---|
| `application/json` | `{ "success": true, "statusCode": 200, "data": [ … ] }` |
| `text/xml` | The same envelope in XML, an array as `<_el>` items. |
| `text/yaml` | The same envelope in YAML. |
| `text/plain`, `text/html`, `application/octet-stream` | The same envelope as text, HTML or bytes. |

**text/xml**

```xml
<?xml version='1.0'?>
<root>
    <success>true</success>
    <statusCode>200</statusCode>
    <data>
        <_el>
            <customer_id>4</customer_id>
            <first_name>JOHNNY</first_name>
        </_el>
    </data>
</root>
```

A custom API can also decide the format itself with `g.res.contentType`, see [Content types](https://docs.apimaker.dev/v4/examples/res/res.html#content-types).

## x-am-cache-control

For an API with [caching](https://docs.apimaker.dev/v4/docs/features/automatic-caching.html) on. Default : `no_action`.

| Value | What happens | `x-am-data-source` of the answer |
|---|---|---|
| `no_action` | The answer comes from Redis when it is there. | `cache` |
| `reset_cache` | The API runs against the database, and its answer replaces the cached one. | `api` |

## x-am-get-encrypted-data

Encrypts the answer with `encryptionAlgorithmFETransfer` and `secretFETransfer` of the secret, the key you share with your frontend or mobile app. Default : `no_encryption`.

| Value | The answer |
|---|---|
| `no_encryption` | `data` only. |
| `get_only_encryption` | `encryptedData`, and `data` is `null`. |
| `get_data_and_encryption` | `data` and `encryptedData`. |

**get_only_encryption**

```json
{ "success": true, "statusCode": 200, "data": null, "encryptedData": "U2FsdGVkX1+6qMdb3jXwJAUWH/qlwBq75mtA1kzpWccrNZRAr+CE2c3VtGpkEtVjH==" }
```

- The client decrypts `encryptedData` with the same algorithm and key (AES, or a legacy RC4 or TripleDES, as in the secret). Anyone who has the app has that key : see [what it protects](https://docs.apimaker.dev/v4/docs/features/security-features.html#encrypted-request-payloads).

## x-am-encrypted-payload

Tells API Maker that the body is encrypted : `{ "dataEncFE": "…" }`, where `dataEncFE` is `{ data, createdAt }` encrypted with the transfer key of the secret. A payload older than `feTransferDataValidityInSeconds` is refused. With `acceptOnlyEncryptedData: true` in the settings, plain bodies are refused. See [Security features](https://docs.apimaker.dev/v4/docs/features/security-features.html#encrypted-request-payloads).

```text
x-am-encrypted-payload: true
```

## x-am-tenant-username

The tenant of the request, for the APIs of a [multi-tenant](https://docs.apimaker.dev/v4/docs/features/multi-tenant.html) instance : they work on the database of that tenant.

- It names the tenant only. Each multi-tenant instance looks the tenant up in its own tenants table, the entry of the default secret chosen as its **Connection String Multi Tenant**. So one header serves every multi-tenant instance a request or a custom API uses, even when they use different tenants tables.
- It does what naming the tenant in the path does, after the instance name and two colons : `/api/schema/admin/crm::acme/crm/customers`. When a request does both, the tenant of the path is used.
- A tenant the tenants table does not have, or which the `find` of its secret entry leaves out, is refused with 400 : `Unable to find tenant with username 'acme'.`
- An empty value names no tenant : the request works on the structure database.
- A custom API reads the tenant in `g.req.params.tenantUsername`, and passes it on to the APIs it calls with `g.sys.db` and `g.sys.system`. The hooks of the request do the same.
- Custom and system APIs with caching keep their cached answers per tenant.
- A [token of a user](https://docs.apimaker.dev/v4/docs/features/multi-tenant.html#users-of-a-tenant) made with this header works for that tenant only.
- An instance which is not multi-tenant has one database for every tenant : the header does not change the database it uses.

```text
GET /api/schema/admin/crm/crm/customers
x-am-tenant-username: acme
```

## x-am-sandbox-timeout

How long the code of this call (a custom API and the hooks of its versions, the hooks of an API) may run, in milliseconds, wherever it runs : in a runner of a sandbox, in the runner of the server (`runOnNativeProcess`), or in the server itself. Default : the time of the custom API (`customApiTimeoutInSeconds`), else `sandboxReqTimeout` of the [configuration](https://docs.apimaker.dev/v4/docs/am-resources/api-maker-configurations.html), `13000`. When the time is over, the answer is the error `API didn't respond in <ms>ms.`, and the runner which holds the code takes no more requests : it is ended once the other requests it runs are over, each in its own time.

```text
x-am-sandbox-timeout: 60000
```

- A custom API sets its own limit in its settings with `customApiTimeoutInSeconds` : the header wins over it.
- Event listeners, schedulers, migration scripts and process initializers run with the time each of them names : see [Time of a run](https://docs.apimaker.dev/v4/docs/settings/sandboxSettings.html#time-of-a-run).

## x-no-compression

Answers of at least `compressThreshold` bytes (51200 by default) are compressed. `true` sends this answer as it is.

- A file sent by a custom API is compressed whatever its size. Streamed answers (the `/stream` APIs) are never compressed.

```text
x-no-compression: true
```

## accept-encoding

The standard header. API Maker picks the first of `gzip`, `br` (Brotli) and `deflate` which the header accepts : a browser sending `gzip, deflate, br` gets gzip, which costs far less CPU than Brotli for nearly the same size. Send `accept-encoding: br` to get Brotli. `identity`, or no header at all, means no compression.

```text
accept-encoding: gzip
```

## Response headers

| Header | Meaning |
|---|---|
| `x-am-data-source` | `cache` when the answer came from Redis, `api` when the API ran. Always `api` for streams. |
| `x-am-request-id` | The id of the request, the same in the logs of API Maker. |
| `content-encoding` | `br`, `gzip` or `deflate` when the answer is compressed. |

## From your code

The same headers go in `headers` of every `g.sys` call :

**TypeScript**

```typescript
const rows = await g.sys.db.getAll({
    instance: 'mysql8', database: 'inventory', collection: 'customers',
    headers: { 'x-am-response-case': 'camelCase', 'x-am-tenant-username': 'acme' },
});
```

**Python**

```python
rows = await g.sys.db.get_all(
    'mysql8', 'inventory', 'customers',
    headers={'x-am-response-case': 'camelCase', 'x-am-tenant-username': 'acme'},
)
```

**PHP**

```php
$rows = $g->sys->db->getAll(
    'mysql8', 'inventory', 'customers',
    headers: ['x-am-response-case' => 'camelCase', 'x-am-tenant-username' => 'acme'],
);
```

**C#**

```csharp
var rows = await g.Sys.Db.GetAll(new GetAll("mysql8", "inventory", "customers")
{
    Headers = new JsonObject { ["x-am-response-case"] = "camelCase", ["x-am-tenant-username"] = "acme" },
});
```

**Go**

```go
rows, err := g.Sys.Db.GetAll(am.GetAll{
	Instance: "mysql8", Database: "inventory", Collection: "customers",
	Headers: am.M{"x-am-response-case": "camelCase", "x-am-tenant-username": "acme"},
})
if err != nil {
	return nil, err
}
```

**Rust**

```rust
let rows = g.sys.db.get_all(
    GetAll::new("mysql8", "inventory", "customers")
        .headers(json!({ "x-am-response-case": "camelCase", "x-am-tenant-username": "acme" })),
)?;
```

**Java**

```java
var rows = g.sys.db.getAll(new GetAll("mysql8", "inventory", "customers")
    .headers(Map.of("x-am-response-case", "camelCase", "x-am-tenant-username", "acme")));
```

**Kotlin**

```kotlin
val rows = g.sys.db.getAll(
    GetAll("mysql8", "inventory", "customers")
        .headers(mapOf("x-am-response-case" to "camelCase", "x-am-tenant-username" to "acme")),
)
```
