The .tiger format

Every Tiger request is a plain-text .tiger file you own. The format is human-readable, diff-friendly, and designed so that a collection of requests works exactly like a folder of source code in Git.

Overview

A .tiger file is a sequence of named blocks. Each block looks like blockname { … }, optionally with a subtype after a colon: body:json { … }. Blocks can appear in any order. Brace matching is depth-aware, so JSON bodies with nested {} are parsed correctly without any escaping.

Tiger's serializer is deterministic: re-saving an unchanged request writes the same bytes every time, so commits in Git only show meaningful edits.

A complete request file

The example below covers every block a request file can contain. In practice most requests only use a handful of them.

meta {
  name: Create post
  seq: 2
}

post {
  url: {{baseUrl}}/posts
}

query {
  draft: true
}

headers {
  Content-Type: application/json
  Accept: application/json
  ~X-Debug: 1
}

body:json {
  {
    "title": "Hello from Tiger",
    "body": "Roar",
    "userId": {{userId}}
  }
}

auth:bearer {
  token: {{accessToken}}
}

capture {
  postId: body.id
  etag: header.ETag
}

script:pre {
  tiger.env.set("timestamp", Date.now().toString());
}

script:post {
  tiger.test("status is 201", () => tiger.expect(tiger.response.status).toBe(201));
  tiger.test("id present", () => tiger.expect(tiger.response.body.id).toBeDefined());
}

docs {
  Creates a new post. Requires an active bearer token in {{accessToken}}.
  The returned `id` is captured into `postId` for downstream requests.
}

Block reference

meta

Holds metadata about the request. name is the display label shown in the sidebar. seq is an optional integer that controls the order Tiger uses when running the collection; lower numbers run first.

meta {
  name: Get user profile
  seq: 3
}

Method block

Exactly one block named after an HTTP method must be present. Supported names are get, post, put, patch, delete, head, and options. The block contains a single url: key.

get {
  url: {{baseUrl}}/users/{{id}}
}
i

The method block name is always lowercase in the file even though Tiger displays it in uppercase in the UI.

query

URL query parameters as key: value lines. Tiger appends them to the URL before sending. {{variable}} interpolation works in both keys and values. A leading ~ disables the row without deleting it.

query {
  page: 1
  limit: 50
  ~debug: true
}

headers

Request headers as key: value lines. A leading ~ disables the row; the line is preserved in the file and shown greyed-out in the editor so it can be re-enabled without retyping.

headers {
  Accept: application/json
  X-Correlation-ID: {{$uuid}}
  ~X-Debug: 1
}

body

The request body. The subtype after the colon selects the body type and drives the Content-Type header when auto-detection is active:

Subtype Meaning
body:jsonJSON body; Tiger syntax-highlights and formats it.
body:xmlXML or SOAP body.
body:textPlain text.
body:formURL-encoded form (application/x-www-form-urlencoded); each line is a key: value pair.
body:multipartMultipart form data; file rows use the value prefix @file: followed by an absolute path.
body:graphqlGraphQL query string. Variables live in a separate graphqlvars block.
body:json {
  {
    "title": "Hello",
    "published": true
  }
}

For GraphQL, the query and variables are separate blocks:

body:graphql {
  query GetUser($id: ID!) {
    user(id: $id) {
      name
      email
    }
  }
}

graphqlvars {
  {
    "id": "{{userId}}"
  }
}

auth

Per-request authentication. The subtype selects the scheme. If this block is absent, the request inherits the default auth from its folder or collection. An explicit auth:none opts out even when a collection default is set.

auth:bearer {
  token: {{accessToken}}
}
auth:basic {
  username: alice
  password: {{basicPassword}}
}
auth:apikey {
  key: X-API-Key
  value: {{apiKey}}
  in: header
}

The in field for auth:apikey accepts header (default) or query.

auth:oauth2 {
  grant_type: client_credentials
  token_url: {{tokenUrl}}
  client_id: {{clientId}}
  client_secret: {{clientSecret}}
  scope: read write
}

capture

Extracts values from the response and writes them into environment variables. Each line maps a variable name to a source path. The path prefix selects where to read from:

capture {
  postId: body.id
  etag: header.ETag
  lastStatus: status
}

Captured values are available as {{postId}}, {{etag}}, and so on in every subsequent request within the same runner run or manual send session.

script:pre and script:post

Sandboxed JavaScript that runs before the request is sent (script:pre) or after the response arrives (script:post). Post-scripts can define test assertions that appear in the Tests panel with pass/fail verdicts.

script:pre {
  tiger.env.set("nonce", crypto.randomUUID());
}
script:post {
  tiger.test("status 200", () => tiger.expect(tiger.response.status).toBe(200));
  tiger.test("has id", () => tiger.expect(tiger.response.body.id).toBeDefined());
}

See Pre/post scripts for the full API.

docs

Free-form documentation for the request, written in plain text or Markdown. Tiger renders this in the request's Docs tab. Useful for noting what a request does, what variables it expects, or how to interpret the response.

docs {
  Fetches a paginated list of posts for the given userId.
  Requires `baseUrl` and `userId` to be set in the active environment.
}

Disabled rows

Any key/value line in headers, query, vars, and capture blocks can be disabled by prefixing it with a tilde (~). Tiger skips the row when sending but keeps it in the file so it can be re-enabled later without retyping.

headers {
  Accept: application/json
  ~X-Internal-Trace: 1
}

The tilde is visible in Git diffs, making it easy to see which parameters were toggled on or off without a line ever being deleted.

Variable interpolation

{{variableName}} placeholders are resolved against the active environment at send time. They work in the URL, query params, headers, body, auth fields, and captures. Tiger also provides built-in dynamic variables that are re-evaluated on every send:

Collection layout on disk

A Tiger collection is an ordinary folder. The only convention is:

A typical layout looks like this:

my-api/
  collection.tiger          # collection settings (optional)
  environments/
    dev.tiger
    staging.tiger
    production.tiger
  auth/
    folder.tiger            # folder settings (optional)
    login.tiger
    refresh-token.tiger
  posts/
    list-posts.tiger
    create-post.tiger
    get-post.tiger
  users/
    list-users.tiger
    get-user.tiger

collection.tiger and folder.tiger

Both files share the same block format. They can contain a meta block (display name), an auth block (default auth inherited by requests that do not set their own), and a docs block.

meta {
  name: My API
}

auth:bearer {
  token: {{accessToken}}
}

docs {
  Internal REST API. All endpoints require a bearer token unless marked public.
  Set `baseUrl` and `accessToken` in the active environment before running.
}

Auth inheritance follows the order: request auth > folder auth > collection auth. A request with an explicit auth:none block sends no auth even when a collection default is set.

Environment files

Each file in the environments/ folder is a named variable set. The filename is used as the fallback identifier, but the meta.name value is what Tiger displays in the environment picker.

meta {
  name: dev
}

vars {
  baseUrl: http://localhost:8080
  userId: 42
}

vars:secret {
  accessToken: eyJhbGciOiJIUzI1...
}

Non-secret variables live in the vars block. Secret variables live in vars:secret; Tiger masks their values in the UI but still writes them to disk in the same file. For secrets you do not want committed, add the file to .gitignore or store the value in a separate environment file that is gitignored.

Like all other blocks, the ~ prefix disables individual variable rows.

Comments

Lines starting with # or // are ignored inside any key/value block (headers, query, vars, capture, and so on). Comments are not currently preserved through a save/reload cycle; Tiger omits them when it serializes.

headers {
  # Authentication
  Authorization: Bearer {{accessToken}}
  # Tracing (disabled)
  ~X-Request-ID: {{$uuid}}
}

Why plain text

Storing requests as plain text means every workflow that works on code also works on your API collection:

You can open any folder as a collection, including a folder that is already a Git repository containing application code. Tiger will pick up any .tiger files it finds and leave everything else alone.

Related pages

Environments
Named variable sets, secret masking, and switching between environments.
Pre/post scripts
The scripting API for dynamic setup, assertions, and test verdicts.
Collections
Creating, opening, cloning, and organizing collections in Tiger.
Variables
How {{variable}} interpolation works and built-in dynamic variables.