First Steps
Your guide to spaces, collections, selectors, and scripts.
1. First Launch
When you open Synapse for the first time, a Welcome Screen appears and asks you to choose a spaces directory — a folder on your computer where all your API projects (called spaces) will live.
Synapse remembers your choice. Next time you open the app it goes straight to your spaces.
Try Sample Project
Don't have a workspace yet? Click "Try Sample Project…" on the Welcome Screen. Pick an empty folder and Synapse exports two ready-made sample spaces into it:
- DummyJSON — login with JWT, Bearer-token-protected endpoints, GET/POST/PUT, script line extraction, and a custom
.ktsscript that decodes the JWT. - PokéAPI — public Pokémon REST + GraphQL API with environment, pokémon, and region selectors.
The app opens the exported folder immediately — you can start sending requests right away.
2. Workspace Structure
Each space is a plain folder inside your spaces directory:
my-space/
├── workspace.json ← space name (required)
├── collections/ ← your HTTP requests (required)
├── selectors/ ← dropdown definitions (required)
└── scripts/ ← automation scripts (optional)
Secrets are not stored in the space. They're encrypted and saved in Synapse's local config folder, outside any repository — see Secrets.
workspace.json
The only file required at the root. It holds the display name shown in the toolbar.
{
"name": "DummyJSON API"
}
Live file watching
Synapse watches your space folder for changes. Save a JSON file from any editor and the UI updates instantly — no reload needed.
3. Collections — Request Files
Each .json file in collections/ defines one HTTP request. Sub-folders become collapsible groups in the sidebar. Prefix filenames with 01-, 02-, … to control sort order.
Field reference
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
id | string | ✅ | — | Stable unique identifier. Use lowercase-kebab-case. |
name | string | ✅ | — | Display label in the sidebar and response tabs. |
method | string | ✅ | — | GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS. |
url | string | ✅ | — | Full URL. Supports {{...}} templates. |
headers | object (string→string) | — | {} | Request headers. Values support templates. |
queryParams | object (string→string) | — | {} | Query parameters (?key=value). Values support templates. |
bodyType | string | — | "none" | none, json, raw, or form. |
body | string or null | — | null | Raw request body. For JSON, escape inner quotes with \". Supports templates. |
description | string or null | — | null | Info text shown next to the request. |
scriptLines | array of strings | — | [] | Post-response extraction rules. See Scripts. |
Templates work in url, headers, queryParams, and body — any field that accepts a string value.
Example — Login with token extraction (from the DummyJSON sample)
{
"id": "login",
"name": "Login",
"method": "POST",
"url": "{{selector.environment.BASE_URL}}/auth/login",
"headers": {
"Content-Type": "application/json",
"Accept": "application/json"
},
"bodyType": "json",
"body": "{\"username\":\"{{selector.credentials.USERNAME}}\",\"password\":\"{{selector.credentials.PASSWORD}}\"}",
"scriptLines": [
"TOKEN = response.body.accessToken",
"REFRESH = response.body.refreshToken",
"USER_ID = response.body.id",
"run:parse-token"
]
}
This single request demonstrates:
- Selectors in URL —
{{selector.environment.BASE_URL}}switches the base URL when you change the Environment dropdown. - Selectors in body —
{{selector.credentials.USERNAME}}pulls the active credentials. - Script lines — extract the JWT token from the response.
TOKENis then available as{{script.TOKEN}}in every subsequent request. - Custom script —
run:parse-tokentriggers a.ktsfile that decodes the JWT to extract the user ID.
4. Selectors — Dynamic Dropdowns
Selectors appear as dropdown chips in the toolbar. Switching a selector instantly updates every request that references it. Each selector is one .json file in selectors/.
Field reference
| Field | Type | Required | Description |
|---|---|---|---|
id | string | ✅ | Used in templates: {{selector.<id>}} or {{selector.<id>.<KEY>}}. |
name | string | ✅ | Label shown in the toolbar. |
defaultValue | string or null | — | Pre-selected option value. Falls back to first option. |
options | array | ✅ | List of choices (see below). |
Each option has:
| Field | Type | Required | Description |
|---|---|---|---|
value | string | ✅ | Injected by {{selector.<id>}}. |
label | string | ✅ | Display text in the dropdown. |
variables | object (string→string) | — | Extra key/value pairs accessed as {{selector.<id>.<KEY>}}. |
Two patterns
With variables — bundle multiple config values per option (e.g. environment URLs, credentials):
{
"id": "credentials",
"name": "Credentials",
"defaultValue": "emilys",
"options": [
{
"value": "emilys",
"label": "Emily Johnson",
"variables": { "USERNAME": "emilys", "PASSWORD": "emilyspass" }
},
{
"value": "michaelw",
"label": "Michael Williams",
"variables": { "USERNAME": "michaelw", "PASSWORD": "michaelwpass" }
}
]
}
Use as {{selector.credentials.USERNAME}} and {{selector.credentials.PASSWORD}} in any request.
Without variables — the value is used directly (e.g. a product ID in a URL path):
{
"id": "product",
"name": "Product",
"defaultValue": "1",
"options": [
{ "value": "1", "label": "Pixel 10 Pro" },
{ "value": "19", "label": "Samsung Galaxy S24 Ultra" }
]
}
Use as {{selector.product}} → inserts 1 or 19 directly into the URL.
Tip: You can edit selector variables directly inside Synapse — click the ⚙ Settings icon in the toolbar.
5. Scripts — Dynamic Values & Automation
A. Built-in Functions
Always available, no setup. Use in any template field.
| Function | Template | Output example |
|---|---|---|
uuid() | {{script.uuid()}} | 550e8400-e29b-41d4-a716-446655440000 |
timestamp() | {{script.timestamp()}} | 1718358792000 |
isoTimestamp() | {{script.isoTimestamp()}} | 2026-06-14T10:33:12.000Z |
randomNumeric(n) | {{script.randomNumeric(6)}} | 482910 |
randomAlphabetic(n) | {{script.randomAlphabetic(9)}} | xKvPtRmoQ |
sign(payload,secret) | {{script.sign(data,mySecret)}} | HMAC-SHA256 digest |
base64(value) | {{script.base64(hello)}} | aGVsbG8= |
B. Script Lines (Post-Response Extraction)
Run automatically after a response. Extract values into session variables for use in subsequent requests.
Syntax: VARIABLE_NAME = source.path.to.value
| Source prefix | Extracts from |
|---|---|
response.body.<key>.<key>… | Response JSON body (dot-separated key chain, integer = array index) |
response.headers.<header-name> | Response header |
request.body.<key>.<key>… | Request JSON body |
request.headers.<header-name> | Request header |
Examples:
TOKEN = response.body.accessToken
USER_ID = response.body.id
FIRST_ITEM = response.body.products.0.title # array index
TRACE_ID = response.headers.X-Request-Id
Extracted variables are available as {{script.TOKEN}}, {{script.USER_ID}}, etc.
Note: the body path is a simple dot-separated key walk, not JSONPath. Wildcards, $, and filters are not supported.
C. Custom Kotlin Scripts (.kts)
For complex logic — JWT decoding, HMAC signatures, multi-step transformations. Place .kts files in the scripts/ folder and trigger them with run:<name> in scriptLines.
Example — decode a JWT and extract the user ID:
scripts/parse-token.kts
import java.util.Base64
val token = context.variables["TOKEN"] ?: error("TOKEN not set — send Login first")
val payloadJson = String(Base64.getUrlDecoder().decode(token.split(".")[1]))
val idMatch = """"id"\s*:\s*(\d+)""".toRegex().find(payloadJson)
result["JWT_USER_ID"] = idMatch?.groupValues?.get(1) ?: ""
Available bindings: context.variables, context.responseBody, context.responseHeaders, context.requestHeaders, context.requestBody. Write output with result["KEY"] = "value".
Trigger in a request's scriptLines:
TOKEN = response.body.accessToken
run:parse-token
After Login, {{script.JWT_USER_ID}} is available everywhere.
6. Secrets — Encrypted Local Credentials
API keys, tokens, and passwords don't belong in plain JSON or in your Git history. Synapse keeps them in an encrypted, machine-local vault that lives outside your repository, so they never appear in your collections or anything an AI agent can read.
Import from a .env file (once)
Click the 🔒 Secrets icon in the toolbar (next to ⚙ Settings) → Import .env, and select your .env file:
API_KEY=sk-live-abc123
DB_PASSWORD=hunter2
WEBHOOK_SECRET=whsec_xyz
You only need to do this once per machine. The .env file is not copied into your space or repository — Synapse reads it, encrypts each value, and stores the result in its own local config folder (next to config.json, outside the repo). Keep the original .env file safe and outside your repository.
Use them in any request
Reference a secret with the {{secrets.KEY}} template — anywhere templates work (url, headers, queryParams, body):
{
"headers": {
"Authorization": "Bearer {{secrets.API_KEY}}"
}
}
At send time Synapse decrypts the value on the fly and injects it.
How the encryption works
- The encryption key is derived from your machine (username, OS name & version, architecture, home directory) — it is never written to disk.
- Encrypted secrets are stored per project in Synapse's local config directory, never in the space/repository. Nothing about your secrets is tracked in Git.
- Because the values never enter the repo, neither your teammates' checkouts nor an AI agent reading the repository can see them. Each developer imports their own
.envonce.
Hiding secrets in responses
In the Response/Request viewer, a Hide secrets checkbox sits next to Copy (on by default). While it's on, any resolved secret value is shown — and copied — as its {{secrets.KEY}} placeholder instead of the real value. Untick it to reveal and copy the actual values when you need them.
7. Tips & Recipes
Chain requests — automatic token passing
Add scriptLines to Login → send once → every request uses {{script.TOKEN}} in its Authorization header — no copy-pasting.
Switch environments or identities instantly
Put URLs in an environment selector, credentials in a credentials selector. Flip the dropdown — the whole workspace retargets.
Control sidebar order
Prefix filenames: 01-login.json, 02-current-user.json. Folders work the same way.
Keep secrets safe
Import sensitive values via the 🔒 Secrets dialog instead of hardcoding them. They're encrypted with a machine-local key and stored outside your repository — nothing is tracked in Git. Import your .env once per machine, keep the original file outside the repo, and reference values with {{secrets.KEY}}.
Let an AI write your collections
Every file is structured JSON in a predictable path. Tell any LLM what API you need and drop the output into collections/ and selectors/ — your secrets stay encrypted and out of reach.
Combine templates freely
Templates resolve at send time and can be mixed in any string field:
{{selector.environment.BASE_URL}}/products/{{selector.product}}?ts={{script.timestamp()}}