This SDK is generated by Fern. Most files under src/main/java/com/deepgram/ are auto-generated and should not be edited directly. Some files are hand-written or carry manual patches and are listed in .fernignore to prevent the generator from overwriting them.
When a new Fern generator release is available, we prepare the repo so the generator can overwrite previously frozen files, then re-apply manual patches after reviewing the diff.
Every entry in .fernignore falls into one of two categories. The comment above each entry in .fernignore indicates which category it belongs to, but when in doubt, apply these rules:
These files are entirely hand-written or are maintained independently from Fern. The generator would delete or replace them with something unrelated. They must stay in .fernignore at all times.
How to identify:
- The file was created by us, not by Fern, such as custom client wrappers or transport abstractions
- The file is a doc, config, build, or CI artifact we maintain independently
- The file lives outside the generated Java package tree or is a manually maintained test/example
Current permanently frozen files:
src/main/java/com/deepgram/DeepgramClient.java,src/main/java/com/deepgram/AsyncDeepgramClient.java,src/main/java/com/deepgram/DeepgramClientBuilder.java,src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java- custom wrapper entrypoints that add Bearer auth, session ID support, and custom transport behavior on top of Fern's generated API clientsrc/main/java/com/deepgram/core/transport/- hand-written transport abstractionsrc/main/java/com/deepgram/resources/listen/v1/websocket/ListenV1ErrorException.javaandsrc/main/java/com/deepgram/resources/listen/v1/websocket/ListenV1ConfigureFailureException.java- hand-written exceptions carrying Listen V1 serverErrorandConfigureFailureframes to the genericonErrorhandler when no typed handler is registered; thrown by the frozen listen v1V1WebSocketClient. No Fern equivalentbuild.gradle,settings.gradle,gradle/,gradlew,gradlew.bat,mvnw,mvnw.cmd,.mvn/,pom.xml,Makefile- build and project configurationREADME.md,CHANGELOG.md,CONTRIBUTING.md,LICENSE,docs/- docssrc/test/- manually maintained testsexamples/- manually maintained examples.editorconfig,.githooks/,.github/,.gitignore- repo and CI configurationcontext7.json- generated-documentation context configuration maintained outside Ferntarget/- build outputCLAUDE.md,AGENTS.md,.claude/,.agents/- agent files and agent-agnostic skills
Note: .fernignore also includes flat-path variants such as src/main/java/DeepgramClient.java as defensive entries for alternate local generation layouts. Those files do not currently exist in this repo checkout and should not be treated as current maintained source files.
These files are Fern-generated but carry manual patches to fix issues in the generator output. We freeze them to protect our patches between regenerations, but unfreeze them before a regen so we can compare the new output against our patches.
How to identify:
- The file exists in Fern's output - if you removed it from
.fernignoreand ran the generator, Fern would produce a version of it - Our version is a modified copy of what Fern generates
Current temporarily frozen files:
src/main/java/com/deepgram/resources/agent/v1/websocket/V1WebSocketClient.java- Agent V1 already delivers every raw control frame throughonMessage(String). Suppress generic errors for unknown frames only when a raw handler is registered; otherwise retain legacyonErrordelivery. Preserve the generated Think Live, dynamic-injection, and frustration handlers during reconciliation.src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ConversationText.javarestores the legacycontent(...): _FinalStagebuilder stage and defaults omitted or emptyconversational_behaviortodefault. The schema now requires the wire field, while this shim preserves source and binary compatibility for callers compiled before Think Live existed.ConversationalBehaviorStageremains as an intentionally retained generated type, even though the restored legacy stage no longer returns it.src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkLiveUpdated.java- fields-less event type with a manualhashCode()patch. Fern generatesequals()but no matchinghashCode(), violating the Object contract; retain the patch, freeze entry, andRegenTypesTest.FieldsLessMessageContractassertion until Fern fixes the pattern.src/main/java/com/deepgram/core/ClientOptions.java- preserves release-please version markers, correct SDK header constants, and the accurateclose()lifecycle Javadoc that Fern currently overwrites; use the standard.bakswap/restore workflow during regen review. Since generator 4.18.0 Fern emits agetSdkVersion()helper readingPackage.getImplementationVersion()instead of a literal. That does resolve in the published artifact (CI publishes via./mvnw deploy -P release, andpom.xml's maven-jar-plugin setsaddDefaultImplementationEntries=true, so the JAR manifest carriesImplementation-Version), but it resolves tonullunder Gradle and in tests, where it silently falls back to a hardcoded literal the generator does not keep current. We keep the explicit literals because they are correct in every context and because.github/release-please-config.jsonalready lists this file inextra-files, so release-please bumps it alongsidepom.xml,build.gradle, and.fern/metadata.json. Fern also emitsUser-Agentwith acom.deepgram.prefix while leavingX-Fern-SDK-Nameon thecom.deepgram:Maven-coordinate form; we keep both on the colon form. Fern 4.22.1 tracks and closes child WebSockets before shutting down SDK-owned OkHttp resources; the permanent custom client wrappers inherit it, so do not add wrapper-local lifecycle code and retain Fern's lifecycle implementation during reconciliation.src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java- carries bug fixes formaxRetries(0)semantics ("connect once, don't retry") and a configurableconnectionTimeoutMsfield (was hardcoded 4000ms), plus anapplyOptionsOverride(...)hook used byTransportWebSocketFactoryto apply per-transport reconnect policy; pull this back out once the fixes are upstreamed into the Fern generator. Use the standard.bakswap/restore workflow during regen review.src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.javaandsrc/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java- forward-compat patch (both clients). Fern's generatedhandleIncomingMessagedispatcher routes any unrecognized message type toonErrorwith "Update your SDK version...", which makes a benign new server control frame look fatal to a deployed client. Patched so the unrecognized-type branch is a no-op — the raw frame is already delivered viaonMessage(String)earlier in the method, so consumers still see it. Mirrors the JS/Python SDKs' forward-compat behavior and is regression-guarded bysrc/test/java/com/deepgram/SpeakV2ForwardCompatTest.javaandsrc/test/java/com/deepgram/ListenV2ForwardCompatTest.java. These two clients also carry the streaming query-param patches described in the next entry. Listen V2 additionally tracks a successfully sentCloseStreamand treats a following no-status (1005) close as terminal; queued or rejected controls still reconnect, andsrc/test/java/com/deepgram/ListenV2ServerCloseTest.javaguards all three paths. Use the standard.bakswap/restore workflow during regen review; re-apply the no-op and terminal-close safeguards after regen.src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.javaandsrc/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java(and the v2 clients above) - streaming query-param patches on the generatedconnect()builders. Two fixes: (1) multi-value serialization — array-valued params (listen:keyterm,keywords,replace,search,tag,extra,language_hint; speak:tag) were serialized withString.valueOf(union.get()), collapsing aListinto one param (keyterm=[a, b]) instead of repeats (keyterm=a&keyterm=b); (2) anadditionalPropertiesescape hatch — the builder exposesadditionalProperty(key, value)for unmodeled params (e.g.no_delay) butconnect()never emitted them to the URL. Both patched to route throughQueryStringMapper(arraysAsRepeats=true), matching the REST path. Listen V1 also forwards a parsed serverErrortoonErroras aListenV1ErrorException(carrying the typedListenV1Error) when noonErrorMessagehandler is registered, preserving the prior generic error-handler behavior. Use the standard.bakswap/restore workflow during regen review; re-apply after regen and unfreeze once the generator emits array params as repeats and serializesadditionalPropertieson the WSconnect()path (tracked as an upstream Fern request).src/main/java/com/deepgram/resources/listen/v1/websocket/V1ConnectOptions.java,src/main/java/com/deepgram/resources/listen/v1/media/requests/ListenV1RequestUrl.java, andsrc/main/java/com/deepgram/resources/listen/v1/media/requests/MediaTranscribeRequestOctetStream.java- keep newly addedlanguageHint(...)methods on public_FinalStagebuilder interfaces as default no-ops. Generated concrete builders override them normally; the defaults preserve source and binary compatibility for third-party implementations compiled before the optional setting existed. Re-apply after regen until Fern emits compatible defaults.- Fields-less message types carrying a manual
hashCode()patch (Fern generatesequals()but nohashCode()for these, violating the Object contract):src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java,src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java,src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java,src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java, and theAgentV1*event typessrc/main/java/com/deepgram/resources/agent/v1/types/{AgentV1ListenUpdated,AgentV1SpeakUpdated,AgentV1AgentAudioDone,AgentV1SettingsApplied,AgentV1UserStartedSpeaking,AgentV1KeepAlive,AgentV1ThinkUpdated,AgentV1PromptUpdated,AgentV1ForceEndTurn}.java. Use the standard.bakswap/restore workflow during regen review; drop the patches and unfreeze all of them once the generator emits a matching equals/hashCode pair for fields-less types (tracked as an upstream Fern request). src/main/java/com/deepgram/types/DeepgramModel.java- restores theFLUX_RENEE_ENconstant that generator 4.18.0 dropped. Renee is an unlisted voice: the spec owner removed it from the catalog on purpose (#project-flux-tts, 2026-08-19), but it still synthesizes (POST /v2/speak?model=flux-renee-enreturns 200 with valid audio), so dropping the constant would be a source break for 0.8.0+ callers over a catalog delisting alone. The constant and itsVisitormethod are marked@Deprecatedwith a note pointing callers to a listed voice. Five touchpoints: the constant, theValueenum entry, thevisit()case, thevalueOf()case, and theVisitormethod (the constant andVisitormethod carry the@Deprecatedtag and Javadoc). This file is unlike the other temporarily frozen ones — it receives frequent additive spec changes (4.18.0 alone added 25 constants), so on the next regen do NOT restore the.bakwholesale. Diff the.bakagainst the newly generated file, carry forward every new voice, and re-apply only theFLUX_RENEE_ENtouchpoints. The spec will not list the voice again, so drop the Renee touchpoints at the next major release; keep the file frozen until then for the reasons above.- Union default-variant fix on the agent listen-provider unions:
src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java,src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java,src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.versionis an optional discriminator, so a provider payload without it is valid (and is what 0.7.x emits), but Fern points@JsonTypeInfodefaultImplat the empty-bodied_UnknownValue, so such a payload deserializes to an unknown variant carryingnull—getProvider()returnsnulland re-serialization emits{"provider":null}, silently dropping the provider on the wire. Patched todefaultImpl = V2Valueon each; guarded bysrc/test/java/com/deepgram/AgentSettingsProviderDefaultTest.java. Use the standard.bakswap/restore workflow during regen review; drop the patches and unfreeze once the generator stops defaulting unions to the empty_UnknownValue(tracked as an upstream Fern request). src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailureCode.javamakesVisitor.visitControlCombinationInvalid()a default method that delegates tovisitUnknown("CONTROL_COMBINATION_INVALID"). This restores source compatibility forVisitorimplementations written against 0.8.0–0.10.x, before the value existed (0.11.0 shipped the method as abstract and listed it as breaking), while retaining the new generated enum value. Guarded bysrc/test/java/com/deepgram/RegenTypesTest.java. Use the standard.bakswap/restore workflow during regen review so new failure codes from the spec are carried forward, and re-apply only the default method. Keep frozen until the next major release.
- Create a new branch off
mainnamed<YOUR_INITIALS>/sdk-gen-<YYYY-MM-DD>(e.g.gh/sdk-gen-2026-07-09). Use your own initials as the prefix. - Push the branch and create a PR titled
chore: SDK regeneration <YYYY-MM-DD>(empty commit if needed). - Read
.fernignoreand classify each entry using the rules above. - For each temporarily frozen file only:
- Copy the file to
<filename>.bakalongside the original. - In
.fernignore, replace the original path with the.bakpath. This protects our patched version from the generator while allowing Fern to overwrite the original.
- Copy the file to
- Never touch permanently frozen entries. Leave them in
.fernignoreas-is. - Commit as
chore: unfreeze files pending regenand push. - The branch is now ready for the Fern generator to push changes.
The .bak files are our manually patched versions protected by .fernignore. The original paths now contain the freshly generated versions. By comparing the two, we can see what the generator now produces versus what we had patched.
-
Diff each
.bakfile against the new generated version to understand what changed and whether our patches are still needed. -
Re-apply any patches that are still necessary to the newly generated files. For
src/main/java/com/deepgram/core/ClientOptions.java, use the standard.bakrestore flow if the generated output still overwrites the Deepgram SDK header constants and// x-release-please-versionmarkers. -
In
.fernignore, replace each.bakpath back to the original path for files that still need patches. -
Remove
.fernignoreentries entirely for any files where the generator now produces correct output. -
Delete all
.bakfiles once review is complete. -
Sweep for newly generated files that need an existing patch class. Diffing
.bakfiles only catches regressions in files we already froze — a brand-new generated type has no.bakand will slip through. At minimum, re-run the fields-lesshashCode()sweep:for f in $(grep -rl 'instanceof' src/main/java/com/deepgram --include='*.java'); do grep -q 'public boolean equals' "$f" && ! grep -q 'public int hashCode' "$f" && echo "$f" done
Ignore
core/Nullable.javaandcore/NullableNonemptyFilter.java(Jackson utility classes, not message types). Anything else in the list needs thehashCode()patch, a.fernignoreentry, an entry in the list above, and aRegenTypesTest.FieldsLessMessageContractassertion.ListenV2ForceEndTurnreachedmainunpatched this way. -
Run checks (
./gradlew spotlessCheck test compileExamples) to verify.testcovers unit/wire tests including the README snippet compilation;compileExamplesseparately compiles the hand-maintainedexamples/directory and catches stale API call sites thattestalone would miss.spotlessCheckis required — CI runs it as a separate job, sotest compileExamplesalone can pass locally while CI fails. Re-applying patches by hand almost always trips it: inserted comments and renamed types change line lengths, which changes how the formatter wraps chained calls. Run./gradlew spotlessApplyto fix, then re-check. -
Commit as
chore: re-apply manual patches after regenand push.
release-please tags the release, then the publish job in .github/workflows/release-please.yml runs ./mvnw deploy -DskipTests -P release. The Maven Wrapper (mvnw, .mvn/wrapper/maven-wrapper.properties) pins Maven 3.9.16 so the build does not depend on the runner's Maven.
- Do not bump the wrapper to Maven 3.10+ until the Central publishing flow handles it. Maven 3.10 adds
maven-metadata-local.xmlto the Central bundle, and Central rejects the deployment (this blocked 0.11.1 on 2026-10-12). Upgradingcentral-publishing-maven-pluginpast 0.7.0 may fix this; verify the bundle contents with./mvnw deploy -DskipTests -P release -Dgpg.skip=true -DskipPublishing=true(then inspecttarget/central-publishing/central-bundle.zip) before changing the pin. - If the Central publish fails after release-please has tagged, fix the cause on
main, then run the Retry Maven Central Publish workflow (.github/workflows/retry-maven-central-publish.yml) with the release tag (for example,v0.11.1). It builds the tagged commit with the wrapper frommain, and refuses tags that don't match the version in.github/.release-please-manifest.json. Maven Central versions are immutable, so a version that already published cannot be re-uploaded.