Bell Syntax Guide
The Bell language is designed to be as close to plain English as possible for making HTTP requests.
Variables and String Interpolation
Assign values to variables with =. Use {variableName} to interpolate a variable into a string.
# Assign a string to a variable
apiKey = "my-secret-token"
userId = 42
# Use variables inside strings with {variableName}
url "https://api.example.com/users/{userId}"
header "Authorization" "Bearer {apiKey}"
GETRequest Building
Use these keywords to build up your HTTP request before sending it.
url
Sets the full base URL for the request. When an environment is active, path should be used instead and the base URL comes from the environment config.
path
Appends a path segment to the base URL.
# url sets the full base URL for the request
url "https://api.example.com"
# path appends a segment to the base URL
# result: https://api.example.com/users/123
path "/users/123"
GETparam
Adds query parameters to the URL.
url "https://api.example.com/restaurants/search"
# param adds a query parameter: ?page=2&query=Sushi&location=NY
param "page" 2
param "query" "Sushi"
param "location" "NY"
# param can also use a variable directly: ?userId=<value>
userId = "abc123"
param userId
GETheader and headers
Adds HTTP headers to the request. Use header for individual headers or headers to set them all at once with an object.
token = "my-token"
url "https://api.example.com/data"
# Add individual headers
header "Authorization" "Bearer {token}"
header "Accept" "application/json"
# Or set all headers at once with an object
headers {
"Authorization": "Bearer {token}",
"Accept": "application/json"
}
GETbody
Sets the request body. Typically used with POST, PUT, and PATCH.
url "https://api.example.com/users"
body {
"name": "Jane Doe",
"email": "jane@example.com",
"role": "admin"
}
POSTSending a Request
Writing an HTTP method keyword dispatches the currently built request.
url "https://api.example.com/users"
# GET — retrieve a resource
GET
# POST — create a resource
body { "name": "Jane" }
POST
# PUT — replace a resource
path "/users/1"
body { "name": "Jane Doe" }
PUT
# PATCH — update fields on a resource
path "/users/1"
body { "name": "Jane D." }
PATCH
# DELETE — remove a resource
path "/users/1"
DELETELogging
Use log to print a value to the console during execution. You can log any expression, including response data, variables, and the current URL.
url "https://api.example.com/users/1"
GET
# Log a value or expression
log response.status
log response.body.name
# Log a variable
count = 42
log count
# Log the current URL
log urlResponse
After a request is dispatched, the response object is set. Bell keeps a history of all responses made in a file.
url "https://api.example.com/users/1"
GET
# Access the full response body
log response.body
# Reach into nested properties
log response.body.name
log response.body.address.city
# Access the HTTP status code
log response.status
# Access response headers
log response.headers
# When multiple requests are made, index into past responses
url "https://api.example.com/login"
POST
url "https://api.example.com/data"
GET
# response[0] is the login response, response[1] is the data response
log response[0].status
log response[1].bodyUser Interaction
input
Prompts the user for a value at runtime. The result can be assigned to a variable or used inline.
url "https://api.example.com/users"
# input() prompts the user for a value at runtime
id = input("Enter user ID")
path "/{id}"
GET
# input() can also be used inline in body or headers
url "https://api.example.com/posts"
body {
"title": input("Post title"),
"body": input("Post body")
}
POSTwarn
Displays a confirmation prompt before continuing. As an expression, it returns the value if confirmed. As a statement, it shows the message and waits for confirmation.
# warn as an expression — prompts for confirmation and returns the value
# execution stops if the user declines
prodUrl = warn "production.example.com"
url "https://{prodUrl}/users"
GET
# warn as a statement — shows a message and asks the user to confirm
warn "You are about to delete a user. Proceed?"
url "https://api.example.com/users/1"
DELETETesting
expect
Asserts that an expression is truthy. If the assertion fails, the file exits with an error. Use Bell files as lightweight API test suites.
url "https://api.example.com/users/1"
GET
# expect asserts that an expression is truthy
# the file exits with an error if it is not
expect response.status === 200
expect response.body.name === "Jane Doe"
url "https://api.example.com/users"
body { "name": "New User" }
POST
expect response.status === 201validate
Validates that a value matches a TypeScript type at runtime. Useful for verifying request and response shapes stay in sync with your type definitions.
import requestBody from "./request-body.json"
import { UserRequest, UserResponse } from "./types.ts"
# validate checks that a value matches a TypeScript type at runtime
validate requestBody as UserRequest
url "https://api.example.com/users"
body requestBody
POST
validate response.body as UserResponseImports
Bell supports three import forms.
# Import a default export from a JSON file
import requestBody from "./request-body.json"
# Import named exports from a TypeScript file
import { UserRequest, UserResponse } from "./types.ts"
# Import a JSON environment config file (anonymous import)
# This loads environment definitions (dev, prod, etc.)
import "./env-config.json".env Files
Bell can load standard .env files directly. All three import forms work with .env files (detected by filename, not extension).
# Anonymous import — all KEY=VALUE pairs become Bell variables.
# BELL_URL sets the base URL so path statements work without an explicit url call.
import ".env"
header "Authorization" "Bearer {API_KEY}"
path "/users/{USER_ID}"
GET
# Named import — pull specific keys from the .env file
import { API_KEY, USER_ID } from ".env"
# Default import — get all vars as a single object
import env from ".env"
log env.API_KEYBELL_URL=https://api.example.com
API_KEY=my-secret-token
USER_ID=42The special key BELL_URL sets the base URL so that path statements work without an explicit url call — the same role the "url" key plays in a JSON environment config.
Bell prints a warning if the .env file is not found, since it is commonly gitignored.
Environments
Environments let you switch between configurations (dev, staging, prod) without changing your Bell files. Define them in a JSON config and load it with an anonymous import.
The config JSON maps environment names to objects. The url key sets the base URL; any other keys become variables.
{
"dev": {
"url": "https://dev.api.example.com",
"apiKey": "dev-key-123"
},
"prod": {
"url": "https://api.example.com",
"apiKey": "prod-key-456"
}
}Use the env keyword to select an environment before running requests.
import "./env-config.json"
# env with no argument prompts the user to choose from all available environments
env
# env with options limits the choices shown to the user
env "dev" | "prod"
# env with a single value sets the environment without prompting
env "prod"
# after env is set, the url and any env variables are available
path "/users"
GETMulti-file Workflows
request
Runs another Bell file inline. Any variables the sub-file exports become available in the calling file.
# request runs another Bell file inline
# exported variables from that file become available here
request "./request-sub.bel"
# token is now available from the sub-file's export
url "https://api.example.com/posts"
header "Authorization" "Bearer {token}"
GETurl "https://api.example.com/login"
body {
"username": "testuser",
"password": "secret"
}
POST
token = response.body.token
export tokenexport
Marks variables to be shared with any file that requests this one.
url "https://api.example.com/login"
body {
"username": "testuser",
"password": "secret"
}
POST
# Save the token from the response
token = response.body.token
userId = response.body.userId
# export makes these variables available to files that request this one
export token, userIdKeywords as Identifiers
Bell keywords like url, body, and headers can be used as variable names in expressions — for example, to log the current URL or read fields from the response body.
log url
log body.data.entry