Everything below uses placeholder hosts (example.com), your-org, and {{ _.token }}-style variables. Do this only against systems you're authorized to manage, and keep real secrets in a private environment (or a secrets plugin), never in a shared/base environment or an export.

00Environments & variables — the one concept that makes this work

The trick that turns a pile of requests into a remote control is Insomnia's environments. Instead of hard-coding a host or a token into every request, you define variables once and reference them with template tags like {{ _.base_url }}. Two ideas to internalize:

  • Base vs sub-environments. Put shared, non-secret values in the Base Environment; create sub-environments (e.g. INT, PROD) that override a few keys so one click flips your whole collection between sites.
  • Private environments for secrets. Mark the environment holding tokens and passwords as private so it is excluded from exports — secrets never travel with the collection.

A typical environment for this guide:

gh_basehttps://api.github.com
owner / repoyour-org / your-repo
fmg_hostfmg-eu.example.com
fgt_hostfgt-01.example.com
adom / vdomroot / root
session(filled at runtime from the login response)
gh_token / fgt_token / fmg_pass(private environment)

One more superpower: response chaining. Insomnia can pull a value out of one request's response and feed it into another with a Response → Body Attribute tag. We use it below to capture a FortiManager session id automatically — no copy-paste.

01GitHub Actions — trigger pipelines from Insomnia

Authenticate. Create a fine-grained Personal Access Token (Settings → Developer settings) scoped to the repo with Actions: read & write. In Insomnia set the request auth to Bearer Token = {{ _.gh_token }}, and add the standard GitHub headers:

Authorization: Bearer {{ _.gh_token }}
Accept: application/vnd.github+json
X-GitHub-Api-Version: 2022-11-28

Bearer auth = "Authorization: Bearer " + token

Manipulate the environment. Keep gh_base, owner and repo in the base environment, and gh_token in a private one. Now every request is portable across repos by flipping two variables.

Do something. Trigger a workflow_dispatch pipeline — a 204 No Content means it's running:

POST {{ _.gh_base }}/repos/{{ _.owner }}/{{ _.repo }}/actions/workflows/deploy.yml/dispatches

{
  "ref": "main",
  "inputs": { "environment": "int", "version": "1.4.2" }
}

# Watch it run:
GET  {{ _.gh_base }}/repos/{{ _.owner }}/{{ _.repo }}/actions/runs?per_page=5
$ POST .../actions/workflows/deploy.yml/dispatches
HTTP/2 204 No Content            # accepted — the workflow is now running

$ GET .../actions/runs?per_page=5
{
  "total_count": 1,
  "workflow_runs": [
    { "id": 8123456789, "status": "in_progress", "head_branch": "main", "event": "workflow_dispatch" }
  ]
}

That's a deploy button living in your API client — parameters and all — instead of six clicks in the browser.

02FortiManager — JSON-RPC and the session dance

FortiManager doesn't use REST; it speaks JSON-RPC over a single endpoint, POST https://{{ _.fmg_host }}/jsonrpc. Authentication is a login call that returns a session id you must include in every subsequent request.

JSON-RPC request = method + params (url + data) + session + id

Authenticate. The login request needs no HTTP auth — the credentials go in the body:

POST https://{{ _.fmg_host }}/jsonrpc

{
  "method": "exec",
  "params": [{
    "url": "/sys/login/user",
    "data": { "user": "{{ _.fmg_user }}", "passwd": "{{ _.fmg_pass }}" }
  }],
  "id": 1
}

# Response contains the session:
# { "result": [...], "session": "aBcD…", "id": 1 }
$ POST https://{{ _.fmg_host }}/jsonrpc      (login)
{
  "result": [
    { "status": { "code": 0, "message": "OK" }, "url": "/sys/login/user" }
  ],
  "session": "s3ssI0n-eXAMPLE-t0k3n-0123456789abcdef",
  "id": 1
}

Capture the session into a variable. Rather than copy the token by hand, set the environment's session value to a Response → Body Attribute tag that points at the login request and the JSONPath $.session. From then on it's just {{ _.session }}.

Manipulate. Every real call carries the session and targets an ADOM. Reads use get, writes use set/add/update:

# List managed devices in an ADOM
POST https://{{ _.fmg_host }}/jsonrpc
{
  "method": "get",
  "params": [{ "url": "/dvmdb/adom/{{ _.adom }}/device" }],
  "session": "{{ _.session }}",
  "id": 2
}

# Log out cleanly when done
POST https://{{ _.fmg_host }}/jsonrpc
{ "method": "exec", "params": [{ "url": "/sys/logout" }], "session": "{{ _.session }}", "id": 99 }

Because the host and session are variables, the exact same collection points at any FortiManager by switching the sub-environment — one folder, every manager.

03FortiGate — REST with a token

A FortiGate exposes a proper REST API. Authenticate by creating a REST API Admin on the box (System → Administrators), which yields an API token; send it as a Bearer token. Keep fgt_host in the base environment and fgt_token in the private one.

Authorization: Bearer {{ _.fgt_token }}
# Base path: https://{{ _.fgt_host }}/api/v2

FortiGate REST call = base URL + (cmdb | monitor) + resource (+ ?vdom)

Manipulate the config. The API splits into cmdb (configuration) and monitor (live state). VDOM-aware calls take ?vdom=:

# Read config objects
GET  {{ _.fgt_host }}/api/v2/cmdb/firewall/address?vdom={{ _.vdom }}

# Create one
POST {{ _.fgt_host }}/api/v2/cmdb/firewall/address?vdom={{ _.vdom }}
{ "name": "net-lab-10", "subnet": "10.10.0.0 255.255.255.0" }

# Live status (monitor)
GET  {{ _.fgt_host }}/api/v2/monitor/system/status
$ GET .../api/v2/monitor/system/status
{
  "status": "success",
  "serial": "FGVMEV0000EXAMPLE",
  "version": "v7.4.4",
  "results": { "model": "FortiGate-VM64", "hostname": "fw-edge-01" }
}

Same pattern as before: authenticate once, template the host and token, and the request works against any FortiGate in the fleet.

04The cherry on the cake — run a whole folder at once

Here's where it stops being a client and starts being automation. When you have the same request across many devices — "pull status from every FortiGate", "check tags on every FortiManager" — you don't want to click each one. The plugin insomnia-plugin-run-requests-parallel adds a folder action that fires every request in a folder concurrently and shows you the results together.

  • Install: Insomnia → Preferences → Plugins → install insomnia-plugin-run-requests-parallel (or drop it into the plugins folder shown on that same screen), then restart.
  • Use: right-click a folder (say, one request per site) → Run requests (parallel). Twenty status checks come back in the time of one.

Fan out reads, not writes. Parallel is perfect for read-only fan-out (status, config pulls, tag checks). For anything that changes config, or where order matters (log in → act → log out), use Insomnia's built-in sequential collection runner instead — and always mind the target's rate limits so you don't trip protection or lock an account.

05Putting it together

One collection, a couple of environments, and three systems answer to the same remote control: dispatch a GitHub Actions deploy, pull a FortiManager ADOM's devices, tweak a FortiGate address object — then fan a health check across the whole estate in one click. Authenticate once, template everything, keep the secrets private, and let a folder do the clicking.