Skip to content

Custom code

Custom Code is code you write by hand in the Staging Repository alongside Soxom’s generator output. It lives on extended/* branches and survives regeneration, so you can ship helpers, convenience methods, and bespoke logic without losing them on the next build.

  • Convenience methods Soxom can’t infer from the spec (composite operations, chained calls)
  • Custom serializers or type aliases that depend on internal context
  • Helper utilities — retries with domain-specific semantics, pagination wrappers, etc.
  • Extra types or interfaces the OpenAPI spec doesn’t describe

If the behavior can be expressed in OpenAPI (via vendor extensions like x-soxom-pagination, x-soxom-retries), prefer that — generator output stays canonical and the line count stays low.

The Staging Repository has two parallel branch families:

  • codegen/* — generator output, rewritten on every build
  • extended/* — your hand-written code merged on top of the matching codegen/*

A build runs the generator on a codegen/<branch> and replays your extensions onto an extended/<branch>. Anything in the extension directory for the language is left alone; anything outside is regenerated from scratch.

  1. Clone the project’s Staging Repository.

  2. Check out extended/next for the next build, or extended/<branch> for a preview branch.

  3. Add files in the language-specific extension location:

    LanguagePath
    TypeScriptsrc/extended/
    Python<package>/extended/
    Javasrc/main/java/.../extended/
  4. Open a pull request against extended/next. CI runs the SDK’s test suite and lints; Soxom merges into next once checks pass.

Anything inside the extension directory survives regeneration. Modifications to generator-managed files (everything outside the extension directory) are overwritten on the next build.

A small TypeScript helper that wraps two generated calls:

src/extended/users-with-roles.ts
import { Client } from "../client";
import type { User, Role } from "../models";
export async function listUsersWithRoles(
client: Client,
): Promise<Array<User & { roles: Role[] }>> {
const users = await client.users.list();
return Promise.all(
users.map(async (user) => ({
...user,
roles: await client.users.listRoles(user.id),
})),
);
}

The 20 lines above count against your Custom Code budget for that target.

Custom Code is plan-gated, per target:

PlanLines per target
Professional1,000
Business10,000
EnterpriseUnlimited

The count is the diff between extended/next and the latest codegen/next — anything you added (in any file) counts. Keep extensions confined to the extension directory so the number stays predictable; edits to generator-managed files inflate the count and get overwritten on the next build anyway.

See Usage limits for what happens when you exceed the cap and how to recover.