# Retrofit Admin & Architecture Guide

This guide is for engineers who need to understand how Retrofit works beneath the annotation surface: the runtime request pipeline, the converter/call-adapter SPI, the multi-module repository layout, the build and release system, and the library's security and operational characteristics. Where the [User Guide](/square/retrofit/user-guide/) explains how to *use* Retrofit, this guide explains how Retrofit is *built* — enough to extend it, integrate it deeply, debug release-only failures, or contribute to it.

Covers Retrofit at commit `8e83a24` (release line **3.0.0**, development version `3.1.0-SNAPSHOT`).

---

## 1. Executive Summary

Retrofit is a type-safe HTTP client for Android and the JVM, maintained by Square under the Apache 2.0 license. Its central idea: you declare an API as an annotated Java/Kotlin **interface**, and Retrofit generates a working implementation at runtime using a `java.lang.reflect.Proxy`. All actual networking is delegated to [OkHttp](https://square.github.io/okhttp/); Retrofit's job is the *adaptation layer* — turning a method invocation into an OkHttp request, and an OkHttp response back into your declared type.

| Attribute | Value |
|---|---|
| Language | Java (core), with Kotlin (`KotlinExtensions.kt`) and Kotlin modules |
| License | Apache License 2.0 |
| Group / coordinates | `com.squareup.retrofit2` |
| Current release | `3.0.0` (2025-05-15) |
| Minimum runtime | Java 8+ or Android API 21+ |
| Core runtime dependency | OkHttp 4.12 (`api` scope → transitive; brings transitive Kotlin) |
| Build system | Gradle (multi-module), Java toolchain pinned to JDK 8 language level |
| Distribution | Maven Central, multi-release JARs, a BOM |
| Module count | Core + 11 converter modules + 6 adapter modules + mock + BOM + response-type-keeper |

**Architectural one-liner:** a dynamic proxy resolves each interface method, once, into a cached `ServiceMethod` that pairs a parsed request template (`RequestFactory`) with a response converter and a call adapter; invoking the method builds an `OkHttpCall` and adapts it to the declared return type.

---

## 2. System Architecture

### 2.1 High-level request pipeline

```
   Your interface (annotated)
            │  retrofit.create(MyApi.class)
            ▼
   java.lang.reflect.Proxy ── InvocationHandler
            │  method invoked
            ▼
   Retrofit.loadServiceMethod(service, method)   ◄── ConcurrentHashMap cache (per Method)
            │  (parse once, reuse forever)
            ▼
   ServiceMethod.parseAnnotations
       ├── RequestFactory.parseAnnotations  ── parses @GET/@Path/@Query/... into a request template
       └── HttpServiceMethod.parseAnnotations
              ├── resolves a CallAdapter  (from callAdapterFactories)
              └── resolves a response Converter<ResponseBody, T>  (from converterFactories)
            │
            ▼
   HttpServiceMethod.invoke(args)
       └── new OkHttpCall<>(requestFactory, args, callFactory, responseConverter)
            │
            ▼
   CallAdapter.adapt(call)  ──►  Call<T> | Observable | CompletableFuture | suspend result
            │
            ▼
   okhttp3.Call.Factory (OkHttpClient)  ── real HTTP I/O
```

### 2.2 The proxy and per-method caching — `Retrofit.create`

`Retrofit.create(Class<T>)` returns a `Proxy.newProxyInstance` whose `InvocationHandler`:

1. Defers `java.lang.Object` methods (`equals`, `hashCode`, `toString`) to normal invocation.
2. For Kotlin/Java **default (interface) methods**, invokes them directly via the platform's `Reflection` strategy.
3. For everything else, calls `loadServiceMethod(service, method).invoke(proxy, args)`.

`loadServiceMethod` is the concurrency-critical core. It maintains a `ConcurrentHashMap<Method, Object>` whose values progress monotonically through three states:

1. **absent** — no thread has begun parsing this method.
2. **a lock object** — one thread is parsing; others block on that lock.
3. **a `ServiceMethod`** — parsing complete; the happy path returns it immediately.

The first thread to `putIfAbsent` a fresh lock wins, parses annotations, stores the result, and releases. On a parsing exception it removes the lock so other waiters retry (and observe the real exception, rather than a cryptic `ClassCastException` — a deliberate fix from 2.11.0). This makes interface parsing thread-safe and pay-once.

> [!NOTE]
> Annotation parsing uses reflection and is comparatively expensive — which is exactly why each `ServiceMethod` is built once and cached. Build your `Retrofit` instance and its proxies once and reuse them.

### 2.3 Parsing annotations — `ServiceMethod` → `RequestFactory` + `HttpServiceMethod`

`ServiceMethod.parseAnnotations` does two things:

- Builds a `RequestFactory` from the method's HTTP annotations — the reusable template that knows how to turn `args` into an `okhttp3.Request` (path substitution, query/header/field/part handlers, body converter selection).
- Validates the return type (rejects `void` and unresolvable type variables/wildcards), then delegates to `HttpServiceMethod.parseAnnotations`.

`HttpServiceMethod.parseAnnotations` resolves the two pluggable pieces and chooses a concrete subclass:

| Subclass | When | Behavior |
|---|---|---|
| `CallAdapted` | Normal (non-suspend) methods | `callAdapter.adapt(call)` |
| `SuspendForResponse` | Kotlin `suspend fun … : Response<T>` | `awaitResponse(call, continuation)` |
| `SuspendForBody` | Kotlin `suspend fun … : T` | `await` / `awaitNullable` / `awaitUnit` |

For `suspend` functions, Retrofit synthesizes an adapter type of `Call<ResponseType>` (unwrapping `Response<T>` when present) and forces the `SkipCallbackExecutor` annotation so the coroutine isn't double-dispatched through the callback executor. It also rejects `suspend fun` returning `Call<T>` (always incorrect) and enforces that `@HEAD` uses `Void`/`Unit`.

> [!IMPORTANT]
> `SuspendForBody` deliberately catches **all** `Throwable` subtypes (except fatal `VirtualMachineError`/`ThreadDeath`/`LinkageError`) and routes them through `KotlinExtensions.suspendAndThrow`. This avoids Java's `UndeclaredThrowableException` when a checked exception is thrown synchronously across the proxy boundary — a subtle but important correctness property of coroutine support.

### 2.4 Executing the request — `OkHttpCall`

`HttpServiceMethod.invoke` constructs an `OkHttpCall<>` that wraps the `requestFactory`, the invocation `args`, the `okhttp3.Call.Factory`, and the response converter, then hands it to the call adapter. `OkHttpCall` lazily creates the underlying `okhttp3.Call`, executes (`execute()`) or enqueues (`enqueue()`) it, applies the response converter to a successful body, and surfaces error bodies via `Response.errorBody()`. A single `OkHttpCall` is single-use; `clone()` produces a fresh one.

### 2.5 Platform abstraction — `Platform`

`Platform` is a static-initializer singleton that selects runtime strategies by inspecting `System.getProperty("java.vm.name")`:

| VM | Callback executor | Reflection strategy | Built-in factories |
|---|---|---|---|
| `Dalvik` (Android), SDK ≥ 24 | `AndroidMainExecutor` (main thread) | `Reflection.Android24` | `BuiltInFactories.Java8` |
| `Dalvik`, SDK < 24 | `AndroidMainExecutor` | `Reflection` | `BuiltInFactories` |
| `RoboVM` | none (synchronous) | `Reflection` | `BuiltInFactories` |
| default JVM | none (synchronous) | `Reflection.Java8` | `BuiltInFactories.Java8` |

This is why callbacks land on the Android **main thread** but on the **calling background thread** on the plain JVM, and why default-interface-method invocation differs by Java version (handled through a multi-release JAR — see [§5.2](#52-multi-release-jar)).

### 2.6 Factory ordering — `Retrofit.Builder.build`

Order matters because Retrofit tries factories in list order until one returns non-null.

**Converter factories** are assembled as:

```
[ BuiltInConverters ]  +  [ your converters, in add order ]  +  [ platform default converters ]
```

`BuiltInConverters` is added **first** and cannot be overridden — this both prevents you from breaking core behavior (raw `ResponseBody`/`RequestBody`/`Void`/`Unit` handling) and ensures correct behavior when a user converter (Moshi, Gson) would otherwise greedily consume all types. The platform defaults at the tail include the `Optional` converters.

**Call adapter factories** are assembled as:

```
[ your adapters, in add order ]  +  [ platform default adapters ]
```

The defaults include `DefaultCallAdapterFactory` (plain `Call<T>`) and, on Java 8+, `CompletableFutureCallAdapterFactory`. The callback `Executor` (Android main thread, or null on the JVM) is threaded into the default call adapters at build time.

> [!TIP]
> The resolution loop exposes a clean diagnostic: when no factory matches, Retrofit throws an exception that lists the factories it *Skipped* and *Tried*. Read that list to see exactly which converters/adapters were registered and in what order.

---

## 3. The Extension SPI

Retrofit's extensibility rests on two abstract factory classes. Every converter and adapter module in the repository is a thin implementation of one of them.

### 3.1 `Converter.Factory`

```java
public interface Converter<F, T> {
  @Nullable T convert(F value) throws IOException;

  abstract class Factory {
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(
        Type type, Annotation[] annotations, Retrofit retrofit) { return null; }

    public @Nullable Converter<?, RequestBody> requestBodyConverter(
        Type type, Annotation[] parameterAnnotations,
        Annotation[] methodAnnotations, Retrofit retrofit) { return null; }

    public @Nullable Converter<?, String> stringConverter(
        Type type, Annotation[] annotations, Retrofit retrofit) { return null; }
  }
}
```

A factory overrides whichever of the three it supports and returns `null` for types it doesn't handle:

- **`responseBodyConverter`** — `ResponseBody` → your type (the `Call<T>` body).
- **`requestBodyConverter`** — your type → `RequestBody` (for `@Body`, `@Part`, `@PartMap`).
- **`stringConverter`** — your type → `String` (for `@Field`, `@FieldMap`, `@Header`, `@HeaderMap`, `@Path`, `@Query`, `@QueryMap`). When none matches, Retrofit falls back to `BuiltInConverters.ToStringConverter` (`Object.toString()`).

Helpers `getParameterUpperBound(int, ParameterizedType)` and `getRawType(Type)` (delegating to `Utils`) make generic-type inspection ergonomic.

### 3.2 `CallAdapter.Factory`

```java
public interface CallAdapter<R, T> {
  Type responseType();      // body type used to prepare the Call (e.g. Repo for Call<Repo>)
  T adapt(Call<R> call);    // wrap the Call into the target type

  abstract class Factory {
    public abstract @Nullable CallAdapter<?, ?> get(
        Type returnType, Annotation[] annotations, Retrofit retrofit);
  }
}
```

`get(...)` inspects the method's declared return type and returns an adapter, or `null` to defer to the next factory. `responseType()` tells Retrofit which body type to deserialize into — note it is typically *not* the same as the method's return type (for `Single<User>` the response type is `User`).

### 3.3 Worked example: a delegating converter

The `Optional` converters (Guava/Java 8) are the canonical "delegating" pattern — they don't parse bytes; they call `retrofit.nextResponseBodyConverter(...)` for the inner type and wrap a possibly-null result in an `Optional`. This is why an `Optional` converter must be registered *before* the converter that does the real parsing: it needs to delegate to it.

---

## 4. Repository Structure

The repository is a Gradle multi-module build. The core library is small; most modules are thin SPI implementations plus their test suites.

```
retrofit/                          ← repository root
├── retrofit/                      ← CORE library (retrofit2.*)
│   ├── src/main/java/retrofit2/   ← Retrofit, ServiceMethod, OkHttpCall, etc.
│   │   ├── http/                  ← the 25 annotations (@GET, @Path, ...)
│   │   └── internal/              ← EverythingIsNonNull (internal-only)
│   ├── src/main/java14/           ← multi-release: Java 14 default-method reflection
│   ├── src/main/java16/           ← multi-release: Java 16 default-method reflection
│   ├── android-test/              ← Android instrumentation tests
│   ├── java-test/  kotlin-test/   ← JVM + Kotlin unit tests
│   ├── robovm-test/               ← RoboVM (iOS) platform test
│   └── test-helpers/
├── retrofit-converters/           ← gson, jackson, moshi, protobuf, wire,
│   │                                simplexml, jaxb, jaxb3, kotlinx-serialization,
│   │                                scalars, guava, java8
├── retrofit-adapters/             ← rxjava, rxjava2, rxjava3, guava, java8, scala
├── retrofit-mock/                 ← MockRetrofit, BehaviorDelegate, NetworkBehavior
├── retrofit-bom/                  ← bill-of-materials (java-platform)
├── retrofit-response-type-keeper/ ← annotation processor for shrinker keep-rules
├── samples/                       ← runnable examples (SimpleService, Crawler, ...)
├── website/                       ← Astro Starlight docs site (src/content/docs/*)
└── build.gradle / settings.gradle
```

> [!NOTE]
> The authoritative prose documentation lives in `website/src/content/docs/` (`index.md`, `declarations.md`, `configuration.md`, `download.mdx`, `contributing.md`). The `website/public/` tree contains *generated* Dokka/Javadoc HTML for published versions and is build output, not source.

### 4.1 Core type inventory (`retrofit2`)

| Type | Role |
|---|---|
| `Retrofit` / `Retrofit.Builder` | Configuration entry point + proxy factory |
| `ServiceMethod` (abstract) | Per-method parsed model; `parseAnnotations` |
| `RequestFactory` | Parsed HTTP-annotation template → `okhttp3.Request` |
| `HttpServiceMethod` (+ `CallAdapted`, `SuspendForResponse`, `SuspendForBody`) | Binds request template to converter + adapter; bridges coroutines |
| `OkHttpCall` | `Call<T>` backed by an `okhttp3.Call` |
| `Call` / `Callback` / `Response` | Public request/response API |
| `Converter` / `CallAdapter` | Extension SPI |
| `BuiltInConverters` / `BuiltInFactories` | Default conversions and factory sets |
| `DefaultCallAdapterFactory` / `CompletableFutureCallAdapterFactory` | Default adapters |
| `OptionalConverterFactory` | Public delegating converter for `Optional` |
| `Platform` / `Reflection` / `AndroidMainExecutor` | Runtime-platform strategy |
| `ParameterHandler` / `RequestBuilder` | Per-parameter request assembly |
| `Invocation` | Captures service `Class`, method, and arguments for interceptors |
| `HttpException` | Thrown for non-2xx responses on body-returning suspend calls |
| `KotlinExtensions.kt` | `await`/`awaitResponse`/`suspendAndThrow` coroutine bridge |
| `Utils` | Type utilities (raw type, bounds, validation) |

---

## 5. Build System

### 5.1 Gradle configuration

The root `build.gradle` applies cross-cutting configuration to all `subprojects`:

- **Java toolchain language level 8** (`JavaLanguageVersion.of(8)`) — the library targets Java 8 bytecode for maximum compatibility.
- **Error Prone** with several checks escalated to `ERROR`: `MissingFail`, `MissingOverride`, `UnusedException`, `UnusedMethod`, `UnusedNestedClass`, `UnusedVariable`, `WildcardImport`.
- **Spotless** formatting: `googleJavaFormat` for Java, `ktfmt` (Google style) for Kotlin.
- **Animal Sniffer** signature checks: every `java-library` is verified against the `java18` signature and (except the Java 8-specific converter/adapter modules) the Android API level 21 signature — this is how the library guarantees its "Java 8+ / Android API 21+" floor.
- A `copyWebsiteDocs` task assembles Dokka/Javadoc output into `website/public/3.x` for publishing.

### 5.2 Multi-release JAR

The core `retrofit` module compiles three source sets — `main` (Java 8), `java14`, and `java16` — and packages the version-specific classes under `META-INF/versions/{14,16}` via a multi-release JAR. These hold the reflection needed to invoke interface **default methods** on newer JDKs. The JAR manifest sets `Automatic-Module-Name: retrofit2` and `Multi-Release: true`. Each module sets its own automatic module name (e.g. `retrofit2.converter.gson`, `retrofit2.adapter.rxjava3`).

> [!CAUTION]
> Static-analysis tools unaware of multi-release JARs may flag the Java 14/16 bytecode classes. This is expected and harmless; the correct class is selected by the runtime JDK.

### 5.3 Dependency scopes

The core module's `build.gradle` declares OkHttp as `api` (so it leaks transitively to consumers — intentional, since you interact with OkHttp types) and Android + kotlinx-coroutines as `compileOnly` (optional at runtime; present only when you use Android callbacks or coroutines). Each converter/adapter module declares `api projects.retrofit` plus its target library (`api libs.gson`, `api libs.rxjava3`, etc.), keeping the dependency graph explicit per module.

### 5.4 Testing matrix

The `java-test` module is configured to run the unit suite across **every JDK from 8 through 24** (a `testJdkN` task per version, using Azul Zul toolchains), wiring each into `check`. There are dedicated Kotlin (`kotlin-test`), Android instrumentation (`android-test`), and RoboVM (`robovm-test`) suites so coroutine behavior, the Android main-thread executor, and the iOS/RoboVM platform path are all exercised.

---

## 6. Operations & Integration

Retrofit is a library, not a running service — "operations" here means integrating and operating it correctly inside an application.

### 6.1 Recommended integration shape

```java
// One client, one Retrofit, reused process-wide.
OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(Duration.ofSeconds(10))
    .addInterceptor(authInterceptor)
    .addInterceptor(loggingInterceptor)
    .build();

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.example.com/")   // MUST end in /
    .client(client)
    .addConverterFactory(MoshiConverterFactory.create())
    .addCallAdapterFactory(RxJava3CallAdapterFactory.create())
    .validateEagerly(isDebugBuild)         // surface bad declarations at startup
    .build();
```

- **Singletons.** A `Retrofit` instance is immutable and thread-safe; share it. The connection pool and dispatcher live on the `OkHttpClient`.
- **`validateEagerly(true)`** parses and validates *every* method in the interface at `create(...)` time. Enable in debug/CI to catch malformed declarations at startup rather than on first call; leave off in production to avoid the upfront reflection cost.
- **Timeouts, retries, caching, auth** all belong on the `OkHttpClient`, not Retrofit.

### 6.2 Threading model

| Return type | Where work runs | Where the result is delivered |
|---|---|---|
| `Call.execute()` | Calling thread (blocks) | Calling thread |
| `Call.enqueue()` | OkHttp dispatcher thread | Android: main thread · JVM: the dispatcher thread |
| RxJava `Observable`/`Single`/… | Per RxJava `subscribeOn`/`observeOn` | As scheduled |
| `CompletableFuture` | OkHttp dispatcher thread | Completing thread |
| `suspend fun` | OkHttp dispatcher thread | Resumes on the coroutine's dispatcher |

The Android main-thread callback behavior comes from `AndroidMainExecutor` selected in `Platform`; on the JVM there is no callback executor, so callbacks run on the background request thread.

### 6.3 Error handling model

- For `Call`, `Callback.onResponse` fires for **every** HTTP response. Check `Response.isSuccessful()` (status in `[200,300)`); parse `errorBody()` for failures. `onFailure` fires only for I/O or request/response processing errors.
- For body-returning `suspend` functions, a non-2xx response throws `HttpException` (carrying `code()`, `message()`, and the full `response()`), while network failures throw `IOException`.

### 6.4 Performance characteristics

- **First call per method** pays reflection cost (annotation parsing); subsequent calls hit the `ServiceMethod` cache.
- **Streaming.** `@Streaming` avoids buffering large response bodies into memory; the caller must consume and close the `ResponseBody`.
- **Deferred serialization.** Since 2.12.0, first-party converters (Gson, Jackson, Moshi, Protobuf, Wire) offer a `withStreaming()` factory that defers request-body serialization to HTTP-write time — moving conversion off the calling thread when using `Call.enqueue`.

---

## 7. Security Analysis

Retrofit is a client library with a deliberately small security surface; most concerns live in how you configure it and OkHttp.

| Area | Posture / guidance |
|---|---|
| **Transport security** | TLS, certificate pinning, and trust configuration are OkHttp's responsibility. Configure `OkHttpClient` (e.g. `CertificatePinner`) and pass it to `.client(...)`. Always use `https://` base URLs. |
| **Credentials** | Inject `Authorization`/API-key headers via an OkHttp `Interceptor`, not per-method `@Header` (avoids leaking secrets into many call sites and into logs). |
| **URL encoding** | `@Path`/`@Query`/`@Field` values are URL-encoded by default. Setting `encoded = true` opts out — only do so for values you have already correctly encoded, or you risk request-splitting/injection. |
| **Header injection** | `allowUnsafeNonAsciiValues = true` permits non-ASCII header values that are not strictly HTTP-compliant; use only when a server requires it, and never with untrusted input. |
| **Untrusted input → URL** | `@Url` lets the whole URL come from a runtime value. Validate/whitelist host and scheme when the value originates from user or server data, since a full URL on the endpoint can replace the base host entirely. |
| **Deserialization** | Body parsing is delegated to the chosen converter (Gson/Jackson/etc.). Deserialization-safety (polymorphic type handling, resource limits) is governed by that library's configuration, not Retrofit. |
| **Shrinker correctness** | R8 rules ship automatically; ProGuard requires manual rules (`retrofit2.pro`) or generic type info can be stripped, causing release-only failures (see §8). |
| **Dependency provenance** | Publications are signed (`signAllPublications=true`) and pushed to Maven Central; verify checksums/signatures in supply-chain-sensitive builds. |

Vulnerability reporting follows the repository's `BUG-BOUNTY.md` policy.

---

## 8. Troubleshooting (Operational)

### `IllegalArgumentException: Could not locate ResponseBody converter for <type>` / `… RequestBody converter …`

The factory list had no match. The exception lists *Skipped* and *Tried* factories — confirm the converter is registered and ordered correctly (delegating/`Optional` converters before greedy ones like Moshi/Gson). Remember `BuiltInConverters` is always first and immovable.

### `IllegalArgumentException: Could not locate call adapter for <type>`

No `CallAdapter.Factory` handled the return type. Register the matching adapter (`addCallAdapterFactory(...)`). The default factories only handle `Call<T>` and (Java 8+) `CompletableFuture<T>`.

### Release build fails with `Method return type must not include a type variable or wildcard` or `Unable to create converter`

Shrinking stripped generic signatures. Ensure R8 (rules automatic), or add `retrofit2.pro` for ProGuard plus OkHttp/Okio rules. For interfaces whose runtime type needs exceed static rules, use the `retrofit-response-type-keeper` annotation processor.

### `ClassCastException` from a second caller after a parsing failure

Fixed in 2.11.0 — the parsing exception is now visible to all concurrent callers, not just the first. If you observe this, you are on a pre-2.11 version; upgrade.

### `UndeclaredThrowableException` from a `suspend` function

Should not occur on 3.x — `SuspendForBody`/`SuspendForResponse` intercept synchronous throwables and route them through the continuation. If seen, verify you are not wrapping the suspend call in a custom proxy that bypasses this handling.

### JAXB converter ignores `@XmlJavaTypeAdapter`

Open issue [#3349](https://github.com/square/retrofit/issues/3349) in `JaxbConverterFactory`. Track upstream; work around by relocating the adapter to a binding level the JAXB implementation honors.

---

## 9. Extending Retrofit

### 9.1 Writing a converter factory

```java
public final class YamlConverterFactory extends Converter.Factory {
  public static YamlConverterFactory create() { return new YamlConverterFactory(); }

  @Override public Converter<ResponseBody, ?> responseBodyConverter(
      Type type, Annotation[] annotations, Retrofit retrofit) {
    return body -> yaml.loadAs(body.charStream(), (Class<?>) type);
  }

  @Override public Converter<?, RequestBody> requestBodyConverter(
      Type type, Annotation[] p, Annotation[] m, Retrofit retrofit) {
    return value -> RequestBody.create(yaml.dump(value), MediaType.get("application/yaml"));
  }
}
```

Return `null` from any method for types you don't handle so Retrofit can try the next factory. Install with `addConverterFactory(YamlConverterFactory.create())`.

### 9.2 Writing a call adapter factory

Extend `CallAdapter.Factory`, inspect `returnType` in `get(...)`, and return a `CallAdapter` whose `responseType()` is the body type and whose `adapt(Call<R>)` wraps the call into your target type (delegating to `call.execute()`/`call.enqueue(...)`). The built-in RxJava/Guava/Java 8 adapters are the reference implementations; the `samples/` module includes `ErrorHandlingAdapter` and `DynamicBaseUrl` as worked extension examples.

### 9.3 Reading request metadata with `Invocation`

`Invocation` (attached to the OkHttp request as a tag) exposes the service `Class<?>`, the invoked `Method`, and the arguments. An OkHttp interceptor can read it for logging, metrics, or routing — `samples/InvocationMetrics.java` demonstrates per-endpoint timing. The newer `Invocation.annotationUrl` returns the original URL declared on the method annotation.

---

## 10. Contributing

Contribution flow (from `website/src/content/docs/contributing.md`):

1. Fork the repository and open a pull request from a branch.
2. Follow existing conventions and style — formatting is enforced by Spotless (`googleJavaFormat` / `ktfmt` Google style).
3. Ensure the build passes: `./gradlew build` (`gradlew.bat build` on Windows). This runs Error Prone, Animal Sniffer signature checks, Spotless, and the multi-JDK test matrix.
4. Sign the [Individual Contributor License Agreement](https://squ.re/sign-the-cla) before code can be merged.

### Release & distribution

- `gradle.properties` pins `GROUP=com.squareup.retrofit2`; the development version is `3.1.0-SNAPSHOT`.
- Publishing uses `com.vanniktech.maven.publish` with `mavenCentralPublishing=true`, `mavenCentralAutomaticPublishing=true`, and `signAllPublications=true`.
- In-development snapshots are published to the Central Portal Snapshots repository.
- The `retrofit-bom` module publishes a constraints-only platform so consumers can align all module versions.

---

## 11. Version & Compatibility Notes

| Aspect | Detail |
|---|---|
| Current release | `3.0.0` (2025-05-15) — upgraded to Kotlin-based OkHttp 4.12 (from 3.14) |
| Binary compatibility | 3.x is **forward binary-compatible** with 2.x; 2.x-compiled libraries run on 3.x |
| Transitive Kotlin | Introduced in 3.0 via OkHttp 4.12 |
| Recent additions (Unreleased) | Explicit keep rules for RxJava `Result` types; `allowoptimization` flags; `Invocation.annotationUrl` |
| 2.12.0 | Streaming serialization (`withStreaming()`) for Gson/Jackson/Moshi/Protobuf/Wire; primitive `@Tag` support |
| 2.11.0 | `OptionalConverterFactory` made public; concurrent parsing-exception visibility fix |
| 2.10.0 | kotlinx.serialization + JAXB 3 converters; BOM; `Unit` response type; non-ASCII header values; response-type-keeper processor |

---

## 12. Quick Reference

| Need | API |
|---|---|
| Build a client | `new Retrofit.Builder().baseUrl(url).build()` |
| Add JSON support | `.addConverterFactory(GsonConverterFactory.create())` |
| Add a return-type adapter | `.addCallAdapterFactory(RxJava3CallAdapterFactory.create())` |
| Custom HTTP client | `.client(okHttpClient)` |
| Validate at startup | `.validateEagerly(true)` |
| Derive a variant config | `retrofit.newBuilder()…build()` |
| Generate an implementation | `retrofit.create(MyApi.class)` |
| Resolve a converter manually | `retrofit.responseBodyConverter(type, annotations)` |
| Resolve an adapter manually | `retrofit.callAdapter(returnType, annotations)` |
| Inspect registered factories | `retrofit.converterFactories()` / `retrofit.callAdapterFactories()` |
| Single-use call → fresh copy | `call.clone()` |
| Non-2xx as exception (coroutines) | catch `HttpException` |

---

_Generated June 2026 from square/retrofit at 8e83a24. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
