Where a recovered type actually comes from
An extraction pass returns paths, methods, parameters and headers. The wire shape of a response lives in three places, and a client built from all of them survives more than a client built from one sample.
- The app's own model classes describe the API specification. A class with serialized name annotations was written against the server's field names, including fields that rarely appear. Reading it gives you names and nesting that one captured response would miss.
- Captured responses show the real values. Several responses from different accounts and inputs reveal which fields arrive as numbers and which arrive as strings, and which appear only in some cases.
- The documentation states which fields the client reads. A field the client never touches and the server always sends can be typed as unknown rather than guessed at.
Start with the model classes, then confirm each field against captures. A generator that only diffs captured JSON produces types for the fields that happened to appear in your sample, which is how a client ends up requiring a field that is absent half the time.
Model the envelope and the payload separately
Most mobile APIs wrap the payload in a common envelope with a status code, a message and a data field. Type the envelope once and the data object per endpoint. That keeps a change to the envelope from touching every endpoint type, and it gives the client one place to check the application-level status before it reads the payload.
Where the app parses a payload into a generated class, the annotations carry the mapping. A field named user_id in JSON may be userId in the client, and a boolean may arrive as 0 or 1. Copy the mapping from the app rather than inventing your own, because the server checks the request side of that mapping too.
Optional, nullable and absent are three different states
JSON distinguishes a missing key from a key whose value is null, and servers use both to mean different things. A null field may mean the value is not set yet, while a missing field may mean the concept does not apply to this account. Collapse them and the client will treat an unset value as an absent one.
- Mark a field optional when captures show it missing. Collect responses from more than one account before you decide. A flag that appears only for accounts that enabled a feature is optional rather than required.
- Keep null in the type where the server sends it. If a value is null for a new account and a number afterwards, the type needs both and the client code needs a branch.
- Give enums a fallback case. Servers add status values without announcing them, and a decoder that rejects an unknown value turns a new server feature into a client outage.
- Accept numbers that arrive as strings. Identifiers often travel as strings because they exceed the range a JavaScript number holds exactly.
Keep derived request values out of response types
Signatures, timestamps and request identifiers belong to the request builder rather than to any response model. Mixing them into a shared type makes the client hard to follow, because the reader cannot tell which values came off the wire and which the client calculated. Keep a request type holding the caller's inputs, and let the transport layer add what it derives at send time.
Handle time at the boundary. Parse the wire value into a real time type once, and format it back only when building a request. A client that passes ISO strings around as plain strings will eventually compare two of them and get the wrong answer.
Keep the generated client readable
Group endpoints by the feature they serve and keep one request type and one response type per call. A thin transport layer owns headers, signing and error mapping so the endpoint functions stay short enough to read on one screen.
Write a doc comment on each field whose meaning is not obvious, and record the call and the app version that produced the sample. That note is what lets the next person re-check a shape after an app update without repeating the whole extraction.
Where a service publishes an OpenAPI document, a generator saves time. When no document exists, which is the usual case for a recovered API, the endpoint functions are written by hand from the evidence and ship alongside a Postman collection and written documentation. SReverse delivers that package in Python, JavaScript or TypeScript, with projects starting at $120 and most finished in 24 to 72 hours.
Related work
Reviewed 28 September 2026 · SReverse research desk