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}}
}
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:json | JSON body; Tiger syntax-highlights and formats it. |
body:xml | XML or SOAP body. |
body:text | Plain text. |
body:form | URL-encoded form (application/x-www-form-urlencoded); each line is a key: value pair. |
body:multipart | Multipart form data; file rows use the value prefix @file: followed by an absolute path. |
body:graphql | GraphQL 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:
body.path.to.field- a JSON path into the response body.header.HeaderName- a response header value.status- the HTTP status code as a string.
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:
{{$uuid}}- a random UUID v4.{{$timestamp}}- Unix timestamp in seconds.{{$isoTimestamp}}- ISO 8601 UTC datetime string.{{$randomInt}}- a random integer between 0 and 1000.
Collection layout on disk
A Tiger collection is an ordinary folder. The only convention is:
- Each request is a
.tigerfile anywhere in the tree. - Sub-folders group requests; Tiger shows them in the sidebar as folders.
- An optional
collection.tigerat the root holds collection-level settings (display name, default auth, documentation). - An optional
folder.tigerinside any sub-folder holds folder-level settings (display name, default auth, documentation) - identical format tocollection.tiger. - An
environments/sub-folder holds one.tigerenvironment file per named environment.
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:
- Diff: a changed base URL, an added header, or a tweaked body field shows up as a readable line-level diff in
git diffand on GitHub. - Merge conflicts: two branches that edited different requests produce no conflict. Two branches that changed the same field produce a normal text conflict resolvable in any editor.
- Code review: API changes can be reviewed in a pull request alongside the code that implements them.
- Search:
grep,ripgrep, and editor search work on the collection without any plugin. - Portability: the folder opens in Tiger on any platform. It can also be read, edited, and committed by scripts or CI jobs that do not have Tiger installed.
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.