Skip to content

SDKs

Custom code with SDK hooks

Availability

SDK Hooks are available for Business and Enterprise users only.

SDK Hooks enable custom logic to be added to SDK functions and request lifecycles across supported SDKs. These hooks allow for transformations, tracing, logging, validation, and error handling during different stages of the SDK’s lifecycle.

Hooks can be applied to the following lifecycle events:

  • On SDK initialization: Modify the SDK configuration, base server URL, wrap or override the HTTP client, add tracing, inject global headers, and manage authentication.
  • Before request: Cancel an outgoing request, transform the request contents, or add tracing. Access to SDK configuration and operation context.
  • After success: When a successful response is received, add tracing and logging, validate the response, return an error, or transform the raw response before deserialization. Access to SDK configuration and operation context.
  • After error: On connection errors or unsuccessful responses, add tracing and logging or transform the returned error. Access to SDK configuration and operation context.

All hooks (except SDK initialization) receive a HookContext object that provides access to:

  • SDK Configuration: The complete SDK configuration object, allowing hooks to access custom settings, authentication details, and other configuration parameters.
  • Base URL: The base URL being used for the request.
  • Operation ID: The unique identifier for the API operation being called.
  • OAuth2 Scopes: The OAuth2 scopes required for the operation (if applicable).
  • Security Source: The security configuration or source for the operation.
  • Retry Configuration: The retry settings for the operation.

Important

SDK configuration access in hooks is controlled by the sdkHooksConfigAccess feature flag in the generation section of your gen.yaml configuration file.

The sdkHooksConfigAccess feature flag determines whether hooks have access to the complete SDK configuration object:

  • sdkHooksConfigAccess: true (default for newly generated SDKs): Hooks receive full access to the SDK configuration through the HookContext object, and the SDK initialization hook receives the complete configuration object as a parameter.

  • sdkHooksConfigAccess: false (default for SDKs generated before May 2025): Hooks have limited access to SDK configuration, and the SDK initialization hook has a different signature that doesn’t include the configuration parameter.

  • New SDKs (May 2025 and later): The sdkHooksConfigAccess flag defaults to true, providing full configuration access.
  • Existing SDKs (before May 2025): The flag defaults to false to maintain backward compatibility. You can manually set it to true in your gen.yaml file to enable full configuration access.

When sdkHooksConfigAccess is set to false, the SDK initialization hook will have a different signature that doesn’t receive the configuration object as a parameter, limiting the customization options available during SDK initialization.

To enable full SDK configuration access in existing SDKs, add sdkHooksConfigAccess: true under the generation section in your gen.yaml file.

Hooks are supported in SDKs generated with the latest Speakeasy CLI. Each supported language includes a hooks directory in the generated code:

Language
Go
Directory Path
internal/hooks
Python
Directory Path
src/{sdk_name}/_hooks
TypeScript
Directory Path
src/hooks
Java
Directory Path
src/main/java/{package_path}/hooks
C#
Directory Path
{root_path}/Hooks
Ruby
Directory Path
lib/{sdk_name}/sdk_hooks
  1. Create a hook implementation.

Add the custom hook implementation in a new file inside the hooks directory. The generator won’t override files added to this directory.

  1. Locate the registration file.

Find the appropriate registration file for the language:

Language
Go
Registration File Path
internal/hooks/registration.go
Python
Registration File Path
src/{sdk_name}/_hooks/registration.py
TypeScript
Registration File Path
src/hooks/registration.ts
Java
Registration File Path
src/main/java/{package_path}/hooks/SDKHooks.java
C#
Registration File Path
{root_path}/Hooks/HookRegistration.cs
Ruby
Registration File Path
lib/{sdk_name}/sdk_hooks/registration.rb
  1. Instantiate and register the hook.

In the registration file, find the method initHooks/init_hooks/initialize/InitHooks. This method includes a hooks parameter, allowing hooks to be registered for various lifecycle events.

Instantiate the hook here and register it for the appropriate event.

import { Hooks } from "./types";
export function initHooks(hooks: Hooks) {
const myHook = new ExampleHook();
hooks.registerBeforeRequestHook(myHook);
}

Note

The registration file is generated once and will not be overwritten. After the initial generation, you have full control and ownership of it.

Here are example hook implementations for each of the lifecycle events across different languages:

import { SDKOptions } from "../lib/config";
import {
AfterErrorContext,
AfterErrorHook,
AfterSuccessContext,
AfterSuccessHook,
BeforeRequestContext,
BeforeRequestHook,
SDKInitHook,
} from "./types";
export class ExampleHook
implements SDKInitHook, BeforeRequestHook, AfterSuccessHook, AfterErrorHook
{
sdkInit(opts: SDKOptions): SDKOptions {
// modify the SDK configuration, baseURL, or wrap the client used by the SDK here and return the updated options
// Access opts.baseURL, opts.client, and other configuration options
return opts;
}
beforeRequest(hookCtx: BeforeRequestContext, request: Request): Request {
// Access SDK configuration: hookCtx.options
// Access operation details: hookCtx.operationID, hookCtx.baseURL
// modify the request object before it is sent, such as adding headers or query parameters, or throw an error to stop the request from being sent
return request;
}
afterSuccess(hookCtx: AfterSuccessContext, response: Response): Response {
// Access SDK configuration: hookCtx.options
// Access operation details: hookCtx.operationID, hookCtx.baseURL
// modify the response object before deserialization or throw an error to stop the response from being deserialized
return response;
}
afterError(
hookCtx: AfterErrorContext,
response: Response | null,
error: unknown,
): { response: Response | null; error: unknown } {
// Access SDK configuration: hookCtx.options
// Access operation details: hookCtx.operationID, hookCtx.baseURL
// modify the response before it is deserialized as a custom error or the error object before it is returned or throw an error to stop processing of other error hooks and return early
return { response, error };
}
}

Python SDKs support async hooks for non-blocking I/O in async methods. Enable with useAsyncHooks: true in gen.yaml:

python:
useAsyncHooks: true

Key points:

  • Async hooks use async/await syntax and are registered in asyncregistration.py
  • Existing sync hooks automatically work in async contexts (adapted via asyncio.to_thread())
  • Native async hooks provide better performance than adapted sync hooks for I/O-heavy operations

For complete documentation including implementation examples, migration paths, and adapter usage, see Async Hooks for Python.

To add dependencies needed for SDK hooks, configure the additionalDependencies section in the gen.yaml file.

typescript:
additionalDependencies:
# Pass a map of npm package names to their version pattern for `dependencies`, `devDependencies`, or `peerDependencies`.
dependencies:
uuid: ^9.0.1
devDependencies:
"@types/uuid": "^9.0.8"
peerDependencies: {}