Request Composer
The Request Composer is Ghost’s built-in API testing tool. It lets you craft HTTP requests, send them, and see the response — all without leaving Ghost. Think of it as a lightweight Postman embedded directly into your traffic inspector.
The killer feature is forking: right-click any captured flow, fork it to the composer, modify one thing (a header, a parameter, the body), send it, and instantly compare the new response with the original. This workflow is incredibly fast for debugging: “what happens if I remove the auth token?” → fork, delete the header, send, compare. Done in 5 seconds.
How to Open
Section titled “How to Open”Three entry points:
| Entry Point | What Happens |
|---|---|
| Toolbar button | Click the paper plane icon in the command bar. Opens a blank composer so you can build a request from scratch. |
| Right-click → “Fork to Composer” | Right-click any flow in the traffic list and select “Fork to Composer” (git fork icon). Opens the composer pre-filled with that flow’s method, URL, headers, and body. A purple “Forked from” badge shows the original flow’s details. |
| macOS application menu | View → Compose Request. Opens a blank composer. |
When forked from a flow, Ghost loads the full flow details (headers and body) asynchronously in the background. The composer opens immediately with the basic information and fills in the details when they arrive.
Composer Layout
Section titled “Composer Layout”The composer opens as a slide-over panel on the right side of the screen (over the flow inspector). The traffic list remains visible behind it.
Top Bar
Section titled “Top Bar”┌──────────────────────────────────────────────────────┐│ [GET ▼] [https://api.example.com/endpoint ] [⟲] [▶]│└──────────────────────────────────────────────────────┘- Method selector — dropdown with 7 HTTP methods:
GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS - URL field — text input for the full URL including query parameters. Pressing Enter in this field sends the request immediately.
- cURL import button — opens a full-screen overlay where you can paste a cURL command to populate the composer
- Send button — paper plane icon, highlighted in cyan. Shows a spinner while the request is in flight. Disabled when the URL is empty or a request is already sending.
Keyboard shortcut: Press Cmd+Enter (macOS) or Ctrl+Enter (Windows) from anywhere in the composer to send the request.
Fork Badge
Section titled “Fork Badge”When the composer was opened by forking a flow, a purple badge appears at the top showing the original flow’s method, status code, URL path, and duration. Click the badge to navigate back to that flow in the traffic list.
The composer has up to 4 tabs:
Headers Tab — A key-value editor for request headers. Each row has:
- A name field (fixed width) for the header name (e.g.,
Authorization) - A value field (flexible width) for the header value (e.g.,
Bearer eyJhbG...) - A trash button to remove the row
Click “Add header” at the bottom to add a new row. When forked from a flow, headers are pre-populated — with housekeeping headers automatically excluded (host, content-length, connection, transfer-encoding), since Ghost handles those automatically.
Body Tab — A monospace text area for the request body. This is where you’d put JSON payloads for POST/PUT/PATCH requests. The placeholder shows {"key": "value"} as a hint.
Response Tab — Appears after you send a request. Shows:
- Status badge — color-coded status code (green for 2xx, amber for 4xx, red for 5xx)
- Duration — how long the request took in milliseconds
- Content length — response body size
- Response headers — complete header list
- Response body — syntax-highlighted, with automatic JSON formatting (pretty-printed) when the response is JSON. Capped at a scrollable area (max height 400px).
Compare Tab — Only appears when BOTH conditions are met: (1) you forked from an existing flow, and (2) you’ve received a response. Shows an arrows icon to indicate comparison mode. See the Comparison section below.
cURL Import
Section titled “cURL Import”Click the import button to open a full-screen overlay where you can paste a cURL command. Ghost parses it and populates the method, URL, headers, and body fields automatically.
The parser handles real-world cURL commands including complex quoting and multi-line commands. Supported flags:
| Flag | What It Does |
|---|---|
-X METHOD or --request METHOD | Sets the HTTP method. Also handles combined form -XPOST (no space). |
-H "Name: Value" or --header "Name: Value" | Adds a request header. Can appear multiple times. |
-d "data" or --data "data" | Sets the request body. Also supports --data-raw and --data-binary. Automatically changes the method to POST if it was GET. |
-u user:pass or --user user:pass | Converts to a Basic Authorization header (base64-encoded). |
-A "agent" or --user-agent "agent" | Sets the User-Agent header. |
-b "cookies" or --cookie "cookies" | Sets the Cookie header. |
-e "url" or --referer "url" | Sets the Referer header. |
--url "url" | Explicitly sets the URL (alternative to positional argument). |
--compressed, -s, -k, -v, -L, -i | Recognized and silently skipped (they don’t affect the HTTP request structure). |
The tokenizer handles single-quoted strings, double-quoted strings with backslash escapes, $'...' ANSI-C quoting, and line continuation (\ followed by a newline — common when copying multi-line cURL commands from documentation).
If parsing fails, an error message appears in the overlay explaining what went wrong.
Response Comparison
Section titled “Response Comparison”When you fork a flow and then send a modified version, the Compare tab lets you see exactly what changed between the original response and the new one. This is the composer’s most powerful feature.
What Gets Compared
Section titled “What Gets Compared”| Section | How It’s Compared |
|---|---|
| Status code | Side-by-side display of original vs. new status. A “Changed” badge appears if they differ — this is the first thing you check. |
| Timing | Original vs. new duration with percentage change. Highlighted green if faster (improvement) or yellow if slower (degradation), but only when the difference exceeds 5%. |
| Headers | Case-insensitive header comparison. Each header is color-coded: green dot (added in new response), red dot (removed), yellow dot (value changed). Uses the same diff library as flow comparison. |
| Body | For JSON responses: recursive structural diff that walks every field at every nesting level (up to 10 levels deep). Shows the JSON path (e.g., data.user.email), old value, and new value for every difference. For non-JSON: simple string comparison. |
A summary badge at the top says either “Responses are identical” (nothing changed) or “N differences found” (with the count).
If the original flow was loaded as a summary (without full headers and body), a warning appears: “Original flow details are partial — select the flow in the traffic list first for accurate comparison.”
Replay vs. Compose
Section titled “Replay vs. Compose”Ghost has two ways to re-send a request, and they serve different purposes:
| Feature | Replay | Compose |
|---|---|---|
| How to access | Right-click → “Replay Request” | Right-click → “Fork to Composer” |
| Editable? | No — re-sends the exact same request, byte for byte | Yes — you can change anything before sending |
| Saved to database? | Yes — creates a new flow tagged replay and replay-of:{original_id} | No — the response is ephemeral (disappears when you close the composer) |
| Shows in traffic list? | Yes — appears as a new flow you can inspect | No — only visible in the composer’s Response tab |
| Response body cap | 10 MB | 1 MB |
| Use case | ”Did the server behavior change?” (exact reproduction) | “What happens if I change X?” (experimentation) |
There is also a batch replay option that replays up to 100 flows sequentially with a configurable delay between each request (0-10,000 ms). Each replayed flow gets tagged with batch-replay.
Limits and Security
Section titled “Limits and Security”| Limit | Value | Why |
|---|---|---|
| Request body size | 2 MB | Prevents accidentally sending enormous payloads |
| Response body cap | 1 MB | Keeps the composer UI responsive (large responses are truncated) |
| Request timeout | 30 seconds (default), 60 seconds (maximum) | Prevents requests from hanging indefinitely |
| URL scheme | Only http:// and https:// | No file://, ftp://, or other schemes |
| SSRF protection | DNS-level blocking of loopback, private, and link-local IP addresses | Prevents the compose endpoint from being used to probe internal networks. However, this is intentionally more permissive than typical SSRF guards since the composer is a user-initiated testing tool. |
| Binary responses | Automatically base64-encoded before returning in JSON | Prevents JSON serialization errors for image, protobuf, or other binary responses |
Use Cases
Section titled “Use Cases”Debug a failing request — A POST to /api/checkout returns 400. Fork it, look at the body — maybe a required field is missing. Add the field, send, get 200. You’ve identified the issue.
Test authorization — Fork a request that works. Remove the Authorization header. Send. Does the server correctly return 401? Or does it leak data? This is a basic security check any QA engineer can do.
Compare environments — Copy a cURL command from staging documentation, import it into the composer, change the host to production, send. Compare the responses to verify API parity.
Modify and re-test — A search endpoint returns wrong results. Fork the request, change the query parameter, send. Compare the response body to see exactly which results changed.
Header experimentation — Add an Accept-Language: tr header to see if the API returns Turkish content. Add X-Debug: true to see if the server includes debug information in the response.