SReverseby Simpa Labs

API documentation

Generating an OpenAPI specification from an APK

An OpenAPI document describes paths, parameters, request bodies, responses and security schemes in a form generators can consume. Building one from an APK means writing down the interface the app already calls, using the client as the source and live responses as the confirmation.

What the document has to describe

An OpenAPI specification is only useful when a generator can read it and produce a client that works. That sets the bar: every path the app calls has to appear, each operation needs its parameter locations, and each request and response body needs a schema with the right required fields. The endpoint list comes from the client code and from captures. The shapes come from the client models, extended with a small number of real responses.

Write the specification as you recover the API rather than at the end. A path added while the details are fresh carries correct parameter locations, and the document doubles as the inventory that keeps the work from drifting.

Build the endpoint inventory first

Start with a table of method, path and purpose, then grow it until captures produce no new entries.

  • Retrofit interfaces give the method, the path template and the parameter annotations, which is the most direct source when the app uses them.
  • OkHttp interceptors and call builders show dynamically assembled paths that never appear as a single literal.
  • Captured traffic fills the gaps and confirms that a path recorded in code is actually reachable.
  • Native or fully obfuscated clients supply paths through their string tables, which usually store them as templates with placeholders.

Normalise every entry to a path with braces around variable segments. Mixing a concrete identifier and a template for the same route splits one operation into two entries, which then produces duplicate client methods.

Turn client models into schemas

Serialiser annotations carry most of the JSON API specification. A field annotated with a serialised name maps to that key rather than the Kotlin or Java field name. Kotlin serialisation, Gson, Moshi and Jackson each expose the mapping in a slightly different way, and all of them encode the same information: which key on the wire corresponds to which typed field.

Types translate directly. Strings, numbers, booleans and arrays map to the obvious JSON types, and nested model classes become reusable components referenced with a pointer from the parent. Nullability annotations matter as much as types, because a nullable field becomes an optional property and a non-null one becomes required. When a model carries no annotations, treat the mapping as unknown and confirm it against a real response instead of guessing.

Dates and identifiers deserve a second look. A timestamp field typed as a long in the model may be seconds or milliseconds on the wire, and an identifier may arrive as a string even when it looks numeric. Those details belong in the schema description, because a generated client that types them wrongly fails at parse time.

Recover responses from models and live calls

Response models are often partial, and many apps share a common envelope with a status code, a message and a data field whose type varies per operation. Read the envelope once, then specialise the data field per operation using the model the client casts to.

Run each operation against the live API and compare the response with your schema. Record fields that the model omits, mark them as optional, and note any field whose type varies with context. Describe error responses as well, since a generated client needs to distinguish a validation failure from an authentication failure.

Describe headers and security once

Security schemes belong at the top of the document rather than repeated on each operation. A bearer token becomes one scheme referenced by every protected path, and an API key header becomes another. Session cookies are described as cookie parameters on the operations that depend on them.

Derived headers need explicit treatment. A signature, a timestamp or a device identifier is computed per request, and a generated client cannot invent it. Declare those headers as parameters and state in the description that the caller computes them, so the generated code exposes a hook instead of silently dropping the value.

Validate the document against the API

A specification is correct when a client generated from it can complete the flows the app completes. Run every operation through the generated client and compare the responses with the ones the app receives. Any mismatch points at a parameter in the wrong location, a missing required field, or a path template that does not match the server.

Style checks catch the rest. A linter finds unused components, missing operation identifiers and response codes without descriptions, which matters because those are the fields that make generated code pleasant to use.

Where the specification fits a delivery

The document is the API specification at the centre of the deliverable set: the Python and TypeScript clients, the Postman collection and the written reference all describe the same operations. SReverse delivers API documentation built from the app, and the specification is generated alongside the clients under APK to callable API. A reader who wants the wider picture can start from the deliverables overview.

Reviewed 28 September 2026 · SReverse research desk

Start your full APK reconstruction

Send the full APK

Projects start at $120. Choose WhatsApp or email, then attach the APK in the app that opens. We reply within one hour with the next step and send the fixed quote after review.

Want us to contact you?