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

Request headers

A header changes how API Maker answers a call, without changing the URL. The headers work on every API : database, custom and system APIs.

In short

  • x-am-authorization carries the token of the app that calls. Most calls need it.
  • The other headers are optional. Leave them out and you get JSON, with the keys as they are.
  • x-am-meta works in the admin panel only.
Shape Every API Answer with Request Headers: camelCase, XML, YAML, Flat9 chapters and the transcript

All headers

Header Values What it does
x-am-authorization a token The API user : the app that calls. Needed unless the API is public.
x-am-user-authorization a token The person who uses the app, 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 your own provider.
x-am-response-case camelCase, snakeCase… The case of the keys in data.
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 Turns nested objects into one level.
x-am-meta true, false Adds how the call ran, in meta. Admin panel only.
x-am-cache-control no_action, reset_cache Skips the cached answer and refreshes it.
x-am-get-encrypted-data no_encryption, get_only_encryption, get_data_and_encryption Encrypts the answer.
x-am-encrypted-payload true Says that 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-am-run-in-debugger true Runs the code in the debugger.
x-no-compression, accept-encoding true / gzip, br, deflate, identity Compression of the answer.

x-am-authorization

The token of an API user : the app that calls. The groups of that API user decide which APIs, tables and fields the call may reach.

x-am-authorization: eyJhbGciOiJIUzI1NiIsInR1BydlRrblJlcS
  • Get the token from the token API. It lasts jwtOptions.expiresIn seconds : 72 hours by default. expiresInSeconds in the token request changes it.
  • No token, or a token that is not valid : 401. A good token, but no group allows the API : 403.
  • A public API (apiAccessType: IS_PUBLIC in its settings) does not need it.
  • Your code reads the API user in g.req.auth.authAMUser.

x-am-user-authorization

The token of a person : a row of your own users table. A DB token generator (auth provider) makes it, through the token API.

x-am-user-authorization: eyJhbGciOiJIpXVCJBydlRrblJlcS
  • It is needed when the settings of the database, the table, the API or the custom API name that provider in authProviders.
  • Your code reads the person in g.req.auth.authAMDB : the columns of the user that the generator selects.
  • 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 set up. Your code reads it in 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 set up. Your code reads it in g.req.auth.authGoogle.

x-google-authorization: eyJhbGciOiJIUzI1I6IkpXVCJ9

x-azure-authorization

The token of Azure Active Directory, when an Azure auth provider is set up. Your code reads it in g.req.auth.authAzure.

x-azure-authorization: eyJhbGciOiJIUzI1NI6IkpXVCJ9

x-custom-authorization

The token of a custom auth provider : your own code makes and checks the token. Your code reads what the check returns in g.req.auth.authCustom.

x-custom-authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

x-am-response-case

Changes the case of the keys in data, also inside nested objects and lists. The keys of the envelope (success, statusCode…) do not change. 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
data
[ { "id": "6713c2f8e4b0a1d2c3f4a5b6", "firstName": "JOHN", "lastName": "DOE" } ]
  • The _id of MongoDB becomes id in camelCase, as in the answer above.
  • A value that is not in the table keeps the keys as they are.

x-am-response-object-type

Turns nested objects into one level, with _ between the names. Use it for grids, CSV files and spreadsheets, for example after a deep. 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 : how the call ran. Default : false.

The admin panel only

meta shows the inside of your servers : their processes, the ports of the sandboxes, the groups that let a caller in. So API Maker gives it to the API Testing page of the admin panel only. The request must come through the encrypted channel of the panel, with the token of an administrator. Every other caller gets the answer without meta, whatever the header says. A value other than true or false is still refused with 400.

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, "runtime": "node", "runOnNativeProcess": false } ]
    }
}
Key What it holds
executionTime, executionTimeMS The time spent in API Maker, without the network.
apiAccessGroups The groups of the API user, and which one allowed the call.
runBy For a call that runs code (a custom API, a hook) : the server and process, the runtime, and the port of the sandbox. Code on the native process has runOnNativeProcess: true and port: null. Code of a group of separate sandboxes also shows enableSeparateSandboxForThis and allowAllPackagesOfAdmin. A call handed to one of the first sandboxes (x-am-run-in-sandbox) shows twice : once with publishToRedis: true for the hand-over, once for the sandbox that ran it.

x-am-internationalization

The name of a language of i18N Management. Every message of the answer comes in that language : the messages of API Maker, and your own (errorList, the texts your code throws). 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": "कृपया आईडी पैरामीटर का मान प्रदान करें." }

Add a language or change a message : the next call uses it. Nothing restarts.

x-am-run-in-sandbox

Which sandboxes may run the code of this call : a custom API, the hooks, the events.

Separate Sandboxes for Heavy APIs: Bun, Sandbox Count and Restarts10 chapters and the transcript
x-am-run-in-sandbox: 0      # any sandbox (the default)
x-am-run-in-sandbox: 1      # always the first sandbox
x-am-run-in-sandbox: 2      # one of the first two
  • Use 1 when your code keeps something in memory, like a connection that a process initializer opened. Every call then meets the same runner, so g.shared is the same for all of them.
  • "The first" means the oldest sandboxes of the code : the ones of its language and runtime, or of its group of separate sandboxes. How many sandboxes there are is a setting.
  • The call does not start a sandbox of its own : it uses the ones that run (one is started if none runs). It asks for 5 and only two run ? It uses those two.
  • 0, or a value that is no number : any sandbox.
  • When the sandboxes restart, the first one is the oldest that still takes calls. No call fails for it.
  • Code with runOnNativeProcess runs in no sandbox : the header changes nothing for it.

x-am-run-in-debugger

x-am-run-in-debugger: true

The code of the call stops at your breakpoints. It works while debugging is on for your account, and the API testing page is open in the admin panel. That page sends the header by itself. See Debugging.

x-am-content-type-response

The format of the answer. Default : application/json.

Value The answer
application/json { "success": true, "statusCode": 200, "data": [ … ] }
text/xml The same envelope in XML. Each item of a list is an <_el>.
text/yaml The same envelope in YAML.
text/plain, text/html When data is a text : that text alone. Anything else stays JSON.
application/octet-stream When data is a base64 text (a data:…;base64, URL works too) : the bytes it stands for. Anything else stays JSON.
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>
  • Errors are never plain text or bytes.
  • The /stream APIs answer JSON, whatever this header says.
  • A custom API can choose 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 cached answer comes back from Redis, when there is one. cache
reset_cache The API runs against the database. Its answer replaces the cached one. api

x-am-get-encrypted-data

Encrypts the answer with the transfer key of the secret (encryptionAlgorithmFETransfer and secretFETransfer) : the key you share with your web 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 app decrypts encryptedData with the same algorithm and key : AES, or the older RC4 or TripleDES. Anyone who has the app has that key : see what it protects.

x-am-encrypted-payload

Says that the body is encrypted : { "dataEncFE": "…" }. dataEncFE is { data, createdAt }, encrypted with the transfer key of the secret.

x-am-encrypted-payload: true
  • A body older than feTransferDataValidityInSeconds of the secret is refused : 15 seconds by default.
  • With acceptOnlyEncryptedData: true in the settings of an API, a plain body is refused.
  • See Security features.

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.

GET /api/schema/admin/crm/crm/customers
x-am-tenant-username: acme
  • It does what the tenant in the path does : /api/schema/admin/crm::acme/crm/customers. When a request does both, the path wins.
  • Each multi-tenant instance looks the tenant up in its own tenants table : the Connection String Multi Tenant of the instance. So one header serves every multi-tenant instance that a call uses.
  • An unknown tenant 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. Its g.sys.db and g.sys.system calls pass it on, and so do the hooks.
  • Cached answers of custom and system APIs are kept per tenant.
  • A token of a person made with this header works for that tenant only.
  • On an instance that is not multi-tenant, the header changes nothing.

x-am-sandbox-timeout

How long the code of this call may run, in milliseconds : a custom API and the hooks of its versions, or the hooks of an API. It counts wherever the code runs : in a sandbox, on the native process, or in the server itself.

x-am-sandbox-timeout: 60000
  • Without the header, a custom API gets its own customApiTimeoutInSeconds. Anything else gets sandboxReqTimeout of the configuration : 13000 ms.
  • The header wins over customApiTimeoutInSeconds.
  • When the time is over, the answer is the error API didn't respond in 60000ms. The runner of that code takes no new request, and ends once its other requests are done.
  • Event listeners, schedulers, migration scripts and process initializers have times of their own : see Time of a run.

x-no-compression

API Maker compresses an answer of compressThreshold bytes or more : 51200 by default. With true, this answer goes out as it is.

x-no-compression: true
  • A file that a custom API sends is compressed, whatever its size.
  • The /stream APIs are never compressed.

accept-encoding

The standard header. API Maker takes the first of gzip, br (Brotli) and deflate that the header accepts. So a browser that sends gzip, deflate, br gets gzip : it costs much less CPU than Brotli, for nearly the same size.

accept-encoding: br

identity, or no header, means no compression. The headers of the answer are listed in Response format.

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")),
)