Inspect traffic
Generate types from JSON
Since 0.2.0 Requires Cloak Proxy 0.2.0 or later.
Turn a captured JSON response or request body into TypeScript, Swift Codable, Python dataclass, Go or Kotlin types, named from the URL and ready to copy.
Generate Types reads a JSON body your app really sent or received and writes matching type definitions in TypeScript, Swift, Python, Go or Kotlin. Use it to model an API you don’t have a schema for, to start the client code for a new endpoint, or to check what a response actually contains against the types you already have.
Generate types from a body
Section titled “Generate types from a body”-
Right-click a request in the traffic list and choose Generate Types from Response….
For a request that sent JSON, such as a
POST, choose Generate Types from Request Body… instead. Each item only appears when that body is JSON. Compressed bodies are decompressed first. -
Choose a Language.
-
Check the name of the top-level type. It’s taken from the URL; type another one in Root type name to change it.
-
Click Copy (or press ↩) and paste the code into your project. The button reads Copied until you change the language or name.
TypeScript generated from a users response. Nested objects become their own types.
- Language: TypeScript, Swift, Python, Go or Kotlin.
- Root type name. Empty: named from the URL.
- An empty array (or one with mixed items): the item type is unknown.
- An optional field: null in this body or missing in some items.
- Copy the code to the clipboard.
The Generate Types sheet
Section titled “The Generate Types sheet”| Control | What it does |
|---|---|
| Language | TypeScript, Swift (Codable), Python (dataclass), Go or Kotlin (data class). Cloak Proxy remembers your choice. |
| Root type name | The name of the top-level type. Leave it empty to use the name suggested from the URL. Whatever you type is turned into a type name, so user profile becomes UserProfile. |
| Code | The generated types. You can select and copy part of it. |
| Copy | Copies all the code. |
| Close | Closes the sheet (Esc). |
Under the code, the sheet reminds you that types are inferred from this one body. If the body can’t be read as JSON, the sheet says This body isn’t JSON.
Languages
Section titled “Languages”The same body in each language. This response comes from GET https://api.example.com/v1/users/42:
{ "id": 42, "name": "Ada Park", "email": "ada@example.com", "score": 9.5, "created_at": "2026-01-15T10:00:00Z", "tags": ["admin"], "teams": [ { "id": 1, "name": "Mobile" }, { "id": 2, "name": "Web", "lead": "Ada Park" } ], "manager": null}export interface User { id: number; name: string; email: string; score: number; created_at: string; tags: string[]; teams: Team[]; manager?: null;}
export interface Team { id: number; name: string; lead?: string;}import Foundation
struct User: Codable { let id: Int let name: String let email: String let score: Double let createdAt: String let tags: [String] let teams: [Team] let manager: String?
enum CodingKeys: String, CodingKey { case id case name case email case score case createdAt = "created_at" case tags case teams case manager }}
struct Team: Codable { let id: Int let name: String let lead: String?}from dataclasses import dataclassfrom typing import Any, List, Optional
@dataclassclass User: id: int name: str email: str score: float created_at: str tags: List[str] teams: List["Team"] manager: Optional[Any] = None
@dataclassclass Team: id: int name: str lead: Optional[str] = Nonetype User struct { Id int64 `json:"id"` Name string `json:"name"` Email string `json:"email"` Score float64 `json:"score"` CreatedAt string `json:"created_at"` Tags []string `json:"tags"` Teams []Team `json:"teams"` Manager *interface{} `json:"manager,omitempty"`}
type Team struct { Id int64 `json:"id"` Name string `json:"name"` Lead *string `json:"lead,omitempty"`}@Serializabledata class User( @SerialName("id") val id: Long, @SerialName("name") val name: String, @SerialName("email") val email: String, @SerialName("score") val score: Double, @SerialName("created_at") val createdAt: String, @SerialName("tags") val tags: List<String>, @SerialName("teams") val teams: List<Team>, @SerialName("manager") val manager: JsonElement? = null)
@Serializabledata class Team( @SerialName("id") val id: Long, @SerialName("name") val name: String, @SerialName("lead") val lead: String? = null)What each language produces
Section titled “What each language produces”| Language | Each object becomes | Optional fields | Field names |
|---|---|---|---|
| TypeScript | An export interface |
field?: type |
The JSON keys as they are. Keys that aren’t valid identifiers, such as 2fa or user-id, are quoted. |
| Swift (Codable) | A struct … : Codable, after import Foundation |
let field: Type? |
camelCase. When any name differs from its key, a CodingKeys enum maps every property to its key. Swift keywords such as default and class are wrapped in backticks. |
| Python (dataclass) | A @dataclass class, after the dataclasses and typing imports |
field: Optional[type] = None, placed after the required fields, because a dataclass can’t have a field with a default before one without |
snake_case. There’s no mapping back to the original keys, so convert camelCase keys when you load the data. |
| Go | A type … struct |
A pointer with omitempty: *string and json:"lead,omitempty" |
PascalCase, with the original key in the json tag. id becomes Id. |
| Kotlin (data class) | A @Serializable data class for kotlinx.serialization |
Type? = null |
camelCase, with the original key in @SerialName. |
The Go output has no package line and isn’t aligned; run gofmt on it. The Kotlin output has no imports; add the kotlinx.serialization imports for Serializable, SerialName and, if it’s used, JsonElement. Only Swift output escapes keywords: if a key is a keyword in another language, such as class in Python, rename that field yourself.
JSON values and their types
Section titled “JSON values and their types”| JSON value | TypeScript | Swift | Python | Go | Kotlin |
|---|---|---|---|---|---|
| String | string |
String |
str |
string |
String |
Whole number, such as 42 |
number |
Int |
int |
int64 |
Long |
Number with a decimal point or exponent, such as 9.5 |
number |
Double |
float |
float64 |
Double |
true or false |
boolean |
Bool |
bool |
bool |
Boolean |
| Object | A named type | A named type | A named type | A named type | A named type |
| Array | Item[] |
[Item] |
List[Item] |
[]Item |
List<Item> |
null only, or mixed types |
null or unknown |
String |
Any |
interface{} |
JsonElement |
How types are inferred
Section titled “How types are inferred”Cloak Proxy works only from the one body you picked, so the result is as good as the example:
- Objects become named types. Each nested object gets its own type, named from its key:
addressbecomesAddress,geobecomesGeo. If two different objects would get the same name, the second gets a number:Address2. - Array items are merged. All the items in an array are combined into one item type, named from the singular of the key:
teamsgivesTeam,categoriesgivesCategory,addressesgivesAddress. - Missing and null fields are optional. A field that’s missing from some items in an array, or that’s
null, becomes optional. - Whole and decimal numbers. A number without a decimal point or exponent is an integer type. If some items have
1and others1.5for the same field, it becomes a decimal type. - Conflicting types fall back to a catch-all. A field that’s a string in one item and a number in another, or that’s only ever
null, gets the catch-all type in the last row of the table above. - Empty arrays have an unknown item type, such as
unknown[]in TypeScript. - Top-level arrays and values. A body that’s an array of objects produces the item type. A body that’s an array of strings or numbers, or a single value, becomes a type alias, such as
export type Tag = string[];in TypeScript. - Names come from keys. Keys are split into words at punctuation and lowercase-to-uppercase changes, then joined in each language’s style. A name that would start with a digit gets a prefix:
Tfor type names and Go fields, and_for Swift, Python and Kotlin fields. TypeScript quotes the key instead.
The suggested type name
Section titled “The suggested type name”When Root type name is empty, the top-level type is named after the last meaningful part of the URL’s path, in singular:
| Request | Suggested name |
|---|---|
GET /v1/users |
User |
GET /v1/users/42 |
User |
GET /v1/users/3f2a9c1e-7b4d-4e8a-9c1f-0a2b3c4d5e6f |
User |
GET /api/categories |
Category |
GET /v2/user-profiles |
UserProfile |
POST /v1/orders (request body) |
OrderRequest |
GET / |
Root |
Numbers, long hexadecimal IDs and UUIDs, and version segments such as v1 are skipped. Types generated from a request body get Request added to the name.
Generate types from an AI assistant
Section titled “Generate types from an AI assistant”The MCP server has a read-only generate_types tool. It takes a captured request’s id, and optionally side (response, the default, or request), language (typescript, swift, python, go or kotlin) and root_name. See the MCP tools reference.