Skip to content
This page View Markdown Open in ChatGPT Open in Claude

Request headers

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

x-am-authorization

The token of an API user : the application calling. Its groups decide which APIs, tables and fields the call may reach. The token comes from the token API and expires after jwtOptions.expiresIn seconds (72 hours by default, expiresInSeconds in the token request changes it).

x-am-authorization: eyJhbGciOiJIUzI1NiIsInR1BydlRrblJlcS
  • Missing or invalid : 401. Valid but no group grants the API : 403.
  • Public APIs (apiAccessType: IS_PUBLIC in the settings) 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) 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.

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.

x-aws-authorization

The access token of AWS Cognito, when an AWS auth provider is configured. Read from g.req.auth.authAWS.

x-aws-authorization: eyJhbGciOInR5cCI6IkpXVCJ9

x-google-authorization

The id token of a Google sign-in, when a Google auth provider is configured. Read from g.req.auth.authGoogle.

x-google-authorization: eyJhbGciOiJIUzI1I6IkpXVCJ9

x-azure-authorization

The token of Azure Active Directory, when an Azure auth provider is configured. Read from g.req.auth.authAzure.

x-azure-authorization: eyJhbGciOiJIUzI1NI6IkpXVCJ9

x-custom-authorization

The token of a custom auth provider : your own generator and validator code. What the validator returns is in g.req.auth.authCustom.

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
x-am-response-case: camelCase
Answer
{ "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 produces, into one level with _ between the names : handy for grids, CSV exports and spreadsheets. Default : no_action.

no_action
{ "id": 101, "state_id": { "id": 201, "country_id": { "id": 301, "country_name": "INDIA" }, "state_name": "GUJARAT" }, "city_name": "AHMEDABAD" }
make_flat
{ "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
{
    "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. 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.

x-am-internationalization: Hindi
Default
{ "code": 400, "message": "Please provide id param value" }
Hindi
{ "code": 400, "message": "कृपया आईडी पैरामीटर का मान प्रदान करें." }
Chinese simple
{ "code": 400, "message": "请提供id参数的值" }
Spanish
{ "code": 400, "message": "Proporcione el valor del parámetro id." }
Japanese
{ "code": 400, "message": "id パラメータの値を入力してください" }
Urdu
{ "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.

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.

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 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.

x-am-cache-control

For an API with caching 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
{ "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.

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.

x-am-encrypted-payload: true

x-am-tenant-username

The tenant of the request, for the APIs of a multi-tenant 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 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.
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, 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.

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.

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.
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.

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 :

const rows = await g.sys.db.getAll({
    instance: 'mysql8', database: 'inventory', collection: 'customers',
    headers: { 'x-am-response-case': 'camelCase', 'x-am-tenant-username': 'acme' },
});
rows = await g.sys.db.get_all(
    'mysql8', 'inventory', 'customers',
    headers={'x-am-response-case': 'camelCase', 'x-am-tenant-username': 'acme'},
)
$rows = $g->sys->db->getAll(
    'mysql8', 'inventory', 'customers',
    headers: ['x-am-response-case' => 'camelCase', 'x-am-tenant-username' => 'acme'],
);
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" },
});
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
}
let rows = g.sys.db.get_all(
    GetAll::new("mysql8", "inventory", "customers")
        .headers(json!({ "x-am-response-case": "camelCase", "x-am-tenant-username": "acme" })),
)?;
var rows = g.sys.db.getAll(new GetAll("mysql8", "inventory", "customers")
    .headers(Map.of("x-am-response-case", "camelCase", "x-am-tenant-username", "acme")));
val rows = g.sys.db.getAll(
    GetAll("mysql8", "inventory", "customers")
        .headers(mapOf("x-am-response-case" to "camelCase", "x-am-tenant-username" to "acme")),
)