diff --git a/Documentation/Extensions.md b/Documentation/Extensions.md
index 38b23c5..ff75537 100644
--- a/Documentation/Extensions.md
+++ b/Documentation/Extensions.md
@@ -21,7 +21,101 @@ Every section is a collapsible block — click a heading to expand it.
## API & Networking
-
+`API` is an `actor` wrapping `URLSession` for typed JSON requests and multipart
+uploads. This is a summary — the **[full guide is in API.md](API.md)** (all
+parameters, error model, client certificates, custom body types, and the
+rationale vs. a hand-rolled `URLSession`).
+
+
+API — typed async requests & uploads
+
+### `API.shared`
+
+The shared `actor` instance. Every call is `await`; nothing runs on the main
+thread unless you hop there yourself.
+
+```swift
+struct User: Decodable, Sendable { let id: Int; let name: String }
+
+let user: User = try await API.shared.request(
+ url: "https://api.example.com/users/{id}",
+ method: .get,
+ pathParams: ["id": "42"]
+)
+```
+
+### `request(url:method:body:pathParams:headers:debug:timeoutInterval:networkServiceType:persistConnection:)`
+
+Sends a request and decodes the JSON response into `T: Decodable & Sendable`.
+Non-2xx responses throw an `NSError` whose `code` is the HTTP status and whose
+`localizedFailureReason` is the response body. When `T == String` the raw body
+is returned without JSON decoding.
+
+```swift
+struct CreateUser: Encodable, Sendable { let name: String }
+
+// JSON body
+let created: User = try await API.shared.request(
+ url: "https://api.example.com/users",
+ method: .post,
+ body: jsonBody(CreateUser(name: "Ana"))
+)
+
+// form-url-encoded body
+let token: Token = try await API.shared.request(
+ url: "https://api.example.com/oauth/token",
+ method: .post,
+ body: .form(["grant_type": "password", "username": "ana"])
+)
+```
+
+### `upload(url:method:form:pathParams:headers:debug:timeoutInterval:networkServiceType:)` and its `onProgress:` overload
+
+Uploads a `multipart/form-data` body serialised to a temp file (removed
+afterwards) and streamed from disk, so large files never fully load into memory.
+
+```swift
+var form = MultipartForm()
+form.field("caption", "Sunset")
+form.file("photo", data: jpegData, filename: "p.jpg")
+form.file("video", url: localVideoURL) // streamed from disk
+
+let result: UploadResult = try await API.shared.upload(
+ url: "https://api.example.com/media",
+ form: form,
+ onProgress: { fraction in
+ Task { @MainActor in progressView.progress = Float(fraction) }
+ }
+)
+```
+
+### Body types — `HTTPBody`
+
+`jsonBody(_:)` wraps any `Encodable & Sendable`; `FormURLEncodedBody` (a.k.a.
+`.form([:])`) percent-escapes every value and never drops one; `RawBody` lets you
+supply the bytes and `Content-Type` yourself. Conform your own type to `HTTPBody`
+and `API` accepts it with no change.
+
+```swift
+struct CSVBody: HTTPBody {
+ let rows: [[String]]
+ func encoded() throws -> (data: Data, contentType: String) {
+ let text = rows.map { $0.joined(separator: ",") }.joined(separator: "\n")
+ return (Data(text.utf8), "text/csv; charset=UTF-8")
+ }
+}
+```
+
+### `await API.shared.setupCertification(certData:password:)`
+
+Registers a client certificate (`.p12`) for mutual-TLS on subsequent requests.
+
+```swift
+let p12 = try Data(contentsOf: certURL)
+await API.shared.setupCertification(certData: p12, password: "cert-pw")
+```
+
+
## Strings & Text
@@ -45,7 +139,106 @@ Every section is a collapsible block — click a heading to expand it.
## Crypto
-
+
+RIPEMD_160 — RIPEMD-160 digest
+
+### `static func hash(_ message: Data) -> Data`
+
+Returns the 20-byte RIPEMD-160 digest of `message`. Pure Swift, no system
+dependency. Mainly useful for Bitcoin-style address hashing (`RIPEMD160(SHA256(x))`).
+
+```swift
+let digest = RIPEMD_160.hash(Data("abc".utf8))
+digest.count // 20
+digest.map { String(format: "%02x", $0) }.joined()
+// "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"
+```
+
+
+
+
+LCECryptoKitManager — OTP / peppered-login bridge (needs the LCECryptoKit binary)
+
+A thin facade over the optional `LCECryptoKit` binary product (enabled by the
+`LCE_ENABLE_CRYPTO_BINARY` build flag). **When the binary is not linked every
+method is a no-op** returning `nil` / `""` / `false`, so calling code still
+compiles and runs.
+
+### `init()` / `init(privateKey:)`
+
+Create a manager. The `privateKey` (a.k.a. "hash key") is only needed by the
+`*WithKey` methods.
+
+```swift
+let crypto = LCECryptoKitManager()
+let keyed = LCECryptoKitManager(privateKey: serverHashKey)
+```
+
+### `static func generateKey() -> String`
+
+Generates a random AES key string.
+
+```swift
+let key = LCECryptoKitManager.generateKey()
+```
+
+### `func encodeTP(email:password:) -> String?` / `func decodeOTP(_:) -> String?`
+
+Encode an email+password pair into an OTP seed hash, and decode it back.
+
+```swift
+let hash = crypto.encodeTP(email: "ana@x.com", password: "s3cr3t")
+let back = crypto.decodeOTP(hash ?? "")
+```
+
+### `func encodeOTPWithKey(email:password:) -> String?` / `func decodeOTPWithKey(_:) -> Bool`
+
+Same as above but bound to the instance's `privateKey`; `decodeOTPWithKey`
+returns whether the hash validates against that key rather than the decoded value.
+
+```swift
+let keyed = LCECryptoKitManager(privateKey: serverHashKey)
+let hash = keyed.encodeOTPWithKey(email: "ana@x.com", password: "s3cr3t")
+let ok = keyed.decodeOTPWithKey(hash ?? "") // Bool
+```
+
+### `static func generateSalt() -> String`
+
+Random salt for the salted/iterated/peppered login flow.
+
+```swift
+let salt = LCECryptoKitManager.generateSalt()
+```
+
+### `static func computeClientHash(email:password:salt:) -> String`
+
+Client-side hash of the credentials with the given salt — sent to the server
+instead of the raw password.
+
+```swift
+let clientHash = LCECryptoKitManager.computeClientHash(
+ email: "ana@x.com", password: "s3cr3t", salt: salt
+)
+```
+
+### `static func computeLoginBearerToken(userId:clientHash:) -> String?`
+
+Derives the login bearer token from a user id and the client hash.
+
+```swift
+let token = LCECryptoKitManager.computeLoginBearerToken(userId: "42", clientHash: clientHash)
+```
+
+### `static func otpEncode(_:) -> String?` / `static func otpDecode(_:) -> String?`
+
+One-time-pad encode/decode of an arbitrary string.
+
+```swift
+let enc = LCECryptoKitManager.otpEncode("secret-value")
+let dec = LCECryptoKitManager.otpDecode(enc ?? "") // "secret-value"
+```
+
+
## Core — the `LCEssentials` namespace