flow9-sdk
v0.2.0
Published
Official TypeScript SDK for the Flow9 Funnel CRM API.
Downloads
154
Readme
flow9-sdk
Developer-friendly, type-safe TypeScript SDK for the Flow9 CRM Public API — generated from the OpenAPI spec, with hand-written ergonomics under flow9-sdk/extras (webhook verification, retries, pagination, idempotency).
Summary
Wander CRM Public API: The Wander CRM Public API enables third-party systems to integrate with Flow9 Engage CRM. Create leads, manage activities, retrieve customer profiles, and onboard new companies programmatically.
Authentication
Two credential types are accepted (see securitySchemes):
- an API key in the
x-api-keyheader —f9_live_<32 hex>for live traffic,f9_test_<32 hex>for the test sandbox (legacywcrm_live_/wcrm_test_keys still authenticate but are no longer issued). Keys are created in Settings > API Keys or viaPOST /v1/api-keys. - an OAuth 2.1 access token in
Authorization: Bearer <jwt>— what MCP clients and OAuth integrations send. Same scopes, same tenant isolation.
If both are present, x-api-key wins. A request with neither is 401 with a
WWW-Authenticate: Bearer header.
These endpoints are public (no credential) — they are mounted before the auth middleware and apply their own per-IP rate limiting:
GET /v1/healthPOST /v1/onboarding(tenant signup)GET /v1/availability(module availability by country)
/v1/api-keys is the one exception to the two credential types above: it is
authenticated by a Company_Admin session token (Bearer), because a
freshly-onboarded tenant has no API key yet.
Rate limiting and quotas
Three independent controls apply; a request must pass all of them.
- Per key — sliding windows per minute (default 60, counted per
endpoint) and per hour (default 1000, across the key). The
rate_limit_per_dayvalue stored on a key is not enforced in v1; it is kept for forward compatibility only. - Per tenant — one ceiling across every key the company holds (default 600/minute, plus an optional daily cap derived from the plan). This is a protective backstop, not a meter.
- Monthly quota — calls counted against the plan's
api_call_capfor the billing period. Exhausted:429 QUOTA_EXCEEDED; no subscription:402; quota engine unreachable:503 QUOTA_UNAVAILABLE(fails closed).
Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and
X-RateLimit-Reset, describing whichever of the per-key / per-tenant windows
is closest to rejecting. A 429 adds Retry-After. A monthly-quota refusal
adds X-Quota-Limit, X-Quota-Used and X-Quota-Remaining. Full detail:
docs/api/rate-limits.md.
Response Envelope
All responses use a consistent JSON envelope:
{
"success": true | false,
"data": { ... }, // present on success
"error": { "code": "...", "message": "...", "details": ... }, // present on error
"meta": { "request_id": "uuid", "timestamp": "ISO 8601" }
}Table of Contents
SDK Installation
The SDK can be installed with either npm, pnpm, bun or yarn package managers.
NPM
npm add flow9-sdkPNPM
pnpm add flow9-sdkBun
bun add flow9-sdkYarn
yarn add flow9-sdk[!NOTE] This package is published as an ES Module (ESM) only. For applications using CommonJS, use
await import("flow9-sdk")to import and use this package.
Requirements
For supported JavaScript runtimes, please consult RUNTIMES.md.
SDK Example Usage
Example
import { Flow9 } from "flow9-sdk";
const flow9 = new Flow9();
async function run() {
const result = await flow9.health.getHealth();
console.log(result);
}
run();
Authentication
Per-Client Security Schemes
This SDK supports the following security schemes globally:
| Name | Type | Scheme |
| ------------ | ------ | ----------- |
| apiKeyAuth | apiKey | API key |
| bearerAuth | http | HTTP Bearer |
You can set the security parameters through the security optional parameter when initializing the SDK client instance. The selected scheme will be used by default to authenticate with the API for all operations that support it. For example:
import { Flow9 } from "flow9-sdk";
const flow9 = new Flow9({
security: {
apiKeyAuth: "<YOUR_API_KEY_HERE>",
},
});
async function run() {
const result = await flow9.health.getHealth();
console.log(result);
}
run();
Available Resources and Operations
AccessControl
- listRoles - List roles and permission profiles
- getRole - Read one role or profile with its permission matrix
- listVisibilityScopes - Read the role data-scope rules
Activities
- listActivities - List activities for a deal or lead
- createActivity - Create an activity
- getActivity - Get an activity by ID
- updateActivity - Update an activity
- listActivityTypes - List activity types and their fields
APIKeys
- createApiKey - Mint a scoped API key (headless bootstrap)
- listApiKeys - List the tenant's API keys
- revokeApiKey - Revoke an API key
Attachments
- listRecordAttachments - List a record's attachments
- uploadRecordAttachment - Upload a file to a record
- getAttachment - Read an attachment and get a download link
- deleteAttachment - Delete an attachment
Billing
- getSubscription - Read the tenant's subscription
- getUsage - Read the tenant's API quota and prepaid balance
- getCheckoutSession - Poll a Checkout session you started
- createBillingPortalSession - Open the Stripe Billing Portal (card, address, tax id, invoices)
- upgradeSubscription - Move to a higher package (applies immediately)
- downgradeSubscription - Move to a lower package at period end
- setSubscriptionSeats - Set the number of seats
- addSubscriptionModule - Add a module (add-on) to the subscription
- removeSubscriptionModule - Remove a module from the subscription
- cancelSubscription - Cancel at the end of the current period
- reactivateSubscription - Undo a scheduled period-end cancellation
- listInvoices - Invoice history and the pending bill
Campaigns
- listCampaigns - List campaigns
- getCampaign - Read a campaign with its channels, latest runs and delivery stats
- listCampaignRuns - List a campaign's runs
- listRecordMessages - A record's message timeline across SMS, email and WhatsApp
Customers
- getCustomer - Get customer by ID
- listCustomers - Lookup customer by email
Health
- getHealth - Health check
Leads
- createLead - Create a new lead
Messages
- createMessageDraft - Prepare a message for a person to approve and send
- listMessageDrafts - List message drafts
- getMessageDraft - Read a message draft
- withdrawMessageDraft - Withdraw a draft before anyone decides on it
Objects
- createObjectField - Add a custom field to an object
- updateObjectField - Update a custom field
- listObjects - List the objects this key can see
- getObjectSchema - Describe one object's fields
Onboarding
- onboardCompany - Onboard a new company
- checkEmail - Check whether an account already exists for an email
- auditTenantProvisioning - Audit every tenant's provisioning guarantees (dry run)
- repairTenantProvisioning - Repair a tenant's provisioning
- signupTenant - Sign up a new tenant (public)
- getAvailability - Module availability by country (public)
OrgStructure
- listOrgNodes - List the org tree
- createOrgNode - Create an org node
- getOrgNode - Read one org node with its children and members
- updateOrgNode - Rename, re-parent or re-link an org node
- deleteOrgNode - Delete an org node (and, on request, its subtree)
- listOrgNodeMembers - List the users placed on an org node
- setOrgNodeMember - Place a user on an org node, or change their visibility scope
- removeOrgNodeMember - Remove a user from an org node
- listBranches - List branches
- createBranch - Create a branch
- getBranch - Read one branch
- updateBranch - Update a branch
- deleteBranch - Delete a branch
Packages
- listPackages - List the sellable package catalogue
- getPackage - Get one package by code
Records
- batchCreateRecords - Create up to 100 records in one request
- listRecords - List records of an object
- createRecord - Create a record
- listRecordNotes - List notes on a record
- createRecordNote - Add a note to a record
- listRecordTasks - List tasks linked to a record
- createRecordTask - Create a task on a record
- listRecordActivities - List activities logged against a record
- getRecord - Read one record
- updateRecord - Update a record
- deleteRecord - Delete a record
Scheduling
- getSchedulingAvailability - Free/busy for users over a window
- listBookableSlots - Plain bookable slots for named reps
- searchSlots - Search a pool for bookable slots with offer tokens
- listPools - List the pools the caller may route into
- getPool - Read one pool
- peekPoolSuggestion - The rep the policy would pick now (does not advance the rotation)
- createReservation - Hold an offered slot
- getReservation - Read a hold
- releaseReservation - Release a hold
- extendReservation - Extend a hold
- assignFromPool - Pick a rep from a pool and advance the rotation
- listBookings - List bookings
- createBooking - Book a meeting — confirm an offered slot, or let the engine pick
- getBooking - Read one booking
- cancelBooking - Cancel a booking
- rescheduleBooking - Reschedule a booking
SearchAndReports
- searchRecords - Search across objects by a word
- aggregateRecords - Count, sum, average, min or max records — grouped, bucketed, filtered
- getPipelineSummary - Status breakdown for leads or deals
- listSavedReports - The saved report definitions from the report builder
Settings
- listStatuses - List statuses
- createStatus - Create a status
- getStatus - Read one
- updateStatus - Update
- deleteStatus - Delete
- listTags - List tags
- createTag - Create tag
- getTag - Read one
- updateTag - Update
- deleteTag - Delete
- listSources - List sources
- createSource - Create source
- getSource - Read one
- updateSource - Update
- deleteSource - Delete
- listSlaPolicies - List SLA policies
- createSlaPolicy - Create SLA policie
- getSlaPolicy - Read one
- updateSlaPolicy - Update
- deleteSlaPolicy - Delete
- getDuplicateCheck - Read the duplicate-check configuration
- setDuplicateCheck - Set the duplicate-check configuration for one object
Suppression
- listPhoneNumbers - List the workspace's phone numbers
- listDncEntries - List the do-not-call list
- addDncEntry - Suppress a phone number
- listEmailSuppression - List suppressed email addresses
TestMode
- resetTestMode - Reset the test-mode sandbox
Users
- listUsers - List the workspace's users
- createUser - Create a user
- getUser - Read one user
- updateUser - Update a user (name, status, role, branch)
Webhooks
- listWebhooks - List webhook subscriptions
- createWebhook - Create a webhook subscription
- getWebhook - Read a webhook subscription
- updateWebhook - Update a webhook subscription
- deleteWebhook - Delete a webhook subscription
- rotateWebhookSecret - Rotate a subscription's signing secret
- listWebhookDeliveries - List a subscription's deliveries (the delivery log)
- testWebhook - Send a test event to a subscription
- replayWebhookDelivery - Replay a delivery
- listWebhookEvents - List the catalogue of events you can subscribe to
Standalone functions
All the methods listed above are available as standalone functions. These functions are ideal for use in applications running in the browser, serverless runtimes or other environments where application bundle size is a primary concern. When using a bundler to build your application, all unused functionality will be either excluded from the final bundle or tree-shaken away.
To read more about standalone functions, check FUNCTIONS.md.
accessControlGetRole- Read one role or profile with its permission matrixaccessControlListRoles- List roles and permission profilesaccessControlListVisibilityScopes- Read the role data-scope rulesactivitiesCreateActivity- Create an activityactivitiesGetActivity- Get an activity by IDactivitiesListActivities- List activities for a deal or leadactivitiesListActivityTypes- List activity types and their fieldsactivitiesUpdateActivity- Update an activityapiKeysCreateAPIKey- Mint a scoped API key (headless bootstrap)apiKeysListAPIKeys- List the tenant's API keysapiKeysRevokeAPIKey- Revoke an API keyattachmentsDeleteAttachment- Delete an attachmentattachmentsGetAttachment- Read an attachment and get a download linkattachmentsListRecordAttachments- List a record's attachmentsattachmentsUploadRecordAttachment- Upload a file to a recordbillingAddSubscriptionModule- Add a module (add-on) to the subscriptionbillingCancelSubscription- Cancel at the end of the current periodbillingCreateBillingPortalSession- Open the Stripe Billing Portal (card, address, tax id, invoices)billingDowngradeSubscription- Move to a lower package at period endbillingGetCheckoutSession- Poll a Checkout session you startedbillingGetSubscription- Read the tenant's subscriptionbillingGetUsage- Read the tenant's API quota and prepaid balancebillingListInvoices- Invoice history and the pending billbillingReactivateSubscription- Undo a scheduled period-end cancellationbillingRemoveSubscriptionModule- Remove a module from the subscriptionbillingSetSubscriptionSeats- Set the number of seatsbillingUpgradeSubscription- Move to a higher package (applies immediately)campaignsGetCampaign- Read a campaign with its channels, latest runs and delivery statscampaignsListCampaignRuns- List a campaign's runscampaignsListCampaigns- List campaignscampaignsListRecordMessages- A record's message timeline across SMS, email and WhatsAppcustomersGetCustomer- Get customer by IDcustomersListCustomers- Lookup customer by emailhealthGetHealth- Health checkleadsCreateLead- Create a new leadmessagesCreateMessageDraft- Prepare a message for a person to approve and sendmessagesGetMessageDraft- Read a message draftmessagesListMessageDrafts- List message draftsmessagesWithdrawMessageDraft- Withdraw a draft before anyone decides on itobjectsCreateObjectField- Add a custom field to an objectobjectsGetObjectSchema- Describe one object's fieldsobjectsListObjects- List the objects this key can seeobjectsUpdateObjectField- Update a custom fieldonboardingAuditTenantProvisioning- Audit every tenant's provisioning guarantees (dry run)onboardingCheckEmail- Check whether an account already exists for an emailonboardingGetAvailability- Module availability by country (public)onboardingOnboardCompany- Onboard a new companyonboardingRepairTenantProvisioning- Repair a tenant's provisioningonboardingSignupTenant- Sign up a new tenant (public)orgStructureCreateBranch- Create a branchorgStructureCreateOrgNode- Create an org nodeorgStructureDeleteBranch- Delete a branchorgStructureDeleteOrgNode- Delete an org node (and, on request, its subtree)orgStructureGetBranch- Read one branchorgStructureGetOrgNode- Read one org node with its children and membersorgStructureListBranches- List branchesorgStructureListOrgNodeMembers- List the users placed on an org nodeorgStructureListOrgNodes- List the org treeorgStructureRemoveOrgNodeMember- Remove a user from an org nodeorgStructureSetOrgNodeMember- Place a user on an org node, or change their visibility scopeorgStructureUpdateBranch- Update a branchorgStructureUpdateOrgNode- Rename, re-parent or re-link an org nodepackagesGetPackage- Get one package by codepackagesListPackages- List the sellable package cataloguerecordsBatchCreateRecords- Create up to 100 records in one requestrecordsCreateRecord- Create a recordrecordsCreateRecordNote- Add a note to a recordrecordsCreateRecordTask- Create a task on a recordrecordsDeleteRecord- Delete a recordrecordsGetRecord- Read one recordrecordsListRecordActivities- List activities logged against a recordrecordsListRecordNotes- List notes on a recordrecordsListRecords- List records of an objectrecordsListRecordTasks- List tasks linked to a recordrecordsUpdateRecord- Update a recordschedulingAssignFromPool- Pick a rep from a pool and advance the rotationschedulingCancelBooking- Cancel a bookingschedulingCreateBooking- Book a meeting — confirm an offered slot, or let the engine pickschedulingCreateReservation- Hold an offered slotschedulingExtendReservation- Extend a holdschedulingGetBooking- Read one bookingschedulingGetPool- Read one poolschedulingGetReservation- Read a holdschedulingGetSchedulingAvailability- Free/busy for users over a windowschedulingListBookableSlots- Plain bookable slots for named repsschedulingListBookings- List bookingsschedulingListPools- List the pools the caller may route intoschedulingPeekPoolSuggestion- The rep the policy would pick now (does not advance the rotation)schedulingReleaseReservation- Release a holdschedulingRescheduleBooking- Reschedule a bookingschedulingSearchSlots- Search a pool for bookable slots with offer tokenssearchAndReportsAggregateRecords- Count, sum, average, min or max records — grouped, bucketed, filteredsearchAndReportsGetPipelineSummary- Status breakdown for leads or dealssearchAndReportsListSavedReports- The saved report definitions from the report buildersearchAndReportsSearchRecords- Search across objects by a wordsettingsCreateSlaPolicy- Create SLA policiesettingsCreateSource- Create sourcesettingsCreateStatus- Create a statussettingsCreateTag- Create tagsettingsDeleteSlaPolicy- DeletesettingsDeleteSource- DeletesettingsDeleteStatus- DeletesettingsDeleteTag- DeletesettingsGetDuplicateCheck- Read the duplicate-check configurationsettingsGetSlaPolicy- Read onesettingsGetSource- Read onesettingsGetStatus- Read onesettingsGetTag- Read onesettingsListSlaPolicies- List SLA policiessettingsListSources- List sourcessettingsListStatuses- List statusessettingsListTags- List tagssettingsSetDuplicateCheck- Set the duplicate-check configuration for one objectsettingsUpdateSlaPolicy- UpdatesettingsUpdateSource- UpdatesettingsUpdateStatus- UpdatesettingsUpdateTag- UpdatesuppressionAddDncEntry- Suppress a phone numbersuppressionListDncEntries- List the do-not-call listsuppressionListEmailSuppression- List suppressed email addressessuppressionListPhoneNumbers- List the workspace's phone numberstestModeResetTestMode- Reset the test-mode sandboxusersCreateUser- Create a userusersGetUser- Read one userusersListUsers- List the workspace's usersusersUpdateUser- Update a user (name, status, role, branch)webhooksCreateWebhook- Create a webhook subscriptionwebhooksDeleteWebhook- Delete a webhook subscriptionwebhooksGetWebhook- Read a webhook subscriptionwebhooksListWebhookDeliveries- List a subscription's deliveries (the delivery log)webhooksListWebhookEvents- List the catalogue of events you can subscribe towebhooksListWebhooks- List webhook subscriptionswebhooksReplayWebhookDelivery- Replay a deliverywebhooksRotateWebhookSecret- Rotate a subscription's signing secretwebhooksTestWebhook- Send a test event to a subscriptionwebhooksUpdateWebhook- Update a webhook subscription
File uploads
Certain SDK methods accept files as part of a multi-part request. It is possible and typically recommended to upload files as a stream rather than reading the entire contents into memory. This avoids excessive memory consumption and potentially crashing with out-of-memory errors when working with very large files. The following example demonstrates how to attach a file stream to a request.
[!TIP]
Depending on your JavaScript runtime, there are convenient utilities that return a handle to a file without reading the entire contents into memory:
- Node.js v20+: Since v20, Node.js comes with a native
openAsBlobfunction innode:fs.- Bun: The native
Bun.filefunction produces a file handle that can be used for streaming file uploads.- Browsers: All supported browsers return an instance to a
Filewhen reading the value from an<input type="file">element.- Node.js v18: A file stream can be created using the
fileFromhelper fromfetch-blob/from.js.
import { Flow9 } from "flow9-sdk";
import { openAsBlob } from "node:fs";
const flow9 = new Flow9({
security: {
apiKeyAuth: "<YOUR_API_KEY_HERE>",
},
});
async function run() {
const result = await flow9.attachments.uploadRecordAttachment({
object: "<value>",
id: "013b3f02-ad24-4871-9611-2f4b59e140d4",
body: {
file: await openAsBlob("example.file"),
},
});
console.log(result);
}
run();
Retries
Some of the endpoints in this SDK support retries. If you use the SDK without any configuration, it will fall back to the default retry strategy provided by the API. However, the default retry strategy can be overridden on a per-operation basis, or across the entire SDK.
To change the default retry strategy for a single API call, simply provide a retryConfig object to the call:
import { Flow9 } from "flow9-sdk";
const flow9 = new Flow9();
async function run() {
const result = await flow9.health.getHealth({
retries: {
strategy: "backoff",
backoff: {
initialInterval: 1,
maxInterval: 50,
exponent: 1.1,
maxElapsedTime: 100,
},
retryConnectionErrors: false,
},
});
console.log(result);
}
run();
If you'd like to override the default retry strategy for all operations that support retries, you can provide a retryConfig at SDK initialization:
import { Flow9 } from "flow9-sdk";
const flow9 = new Flow9({
retryConfig: {
strategy: "backoff",
backoff: {
initialInterval: 1,
maxInterval: 50,
exponent: 1.1,
maxElapsedTime: 100,
},
retryConnectionErrors: false,
},
});
async function run() {
const result = await flow9.health.getHealth();
console.log(result);
}
run();
Error Handling
Flow9Error is the base class for all HTTP error responses. It has the following properties:
| Property | Type | Description |
| ------------------- | ---------- | --------------------------------------------------------------------------------------- |
| error.message | string | Error message |
| error.statusCode | number | HTTP response status code eg 404 |
| error.headers | Headers | HTTP response headers |
| error.body | string | HTTP body. Can be empty string if no body is returned. |
| error.rawResponse | Response | Raw HTTP response |
| error.data$ | | Optional. Some errors may contain structured data. See Error Classes. |
Example
import { Flow9 } from "flow9-sdk";
import * as errors from "flow9-sdk/models/errors";
const flow9 = new Flow9({
security: {
apiKeyAuth: "<YOUR_API_KEY_HERE>",
},
});
async function run() {
try {
const result = await flow9.onboarding.onboardCompany({
company: {
name: "Acme Travel",
email: "[email protected]",
phone: "+971501234567",
website: "https://acmetravel.com",
city: "Dubai",
country: "UAE",
},
adminUser: {
email: "[email protected]",
fullName: "John Smith",
},
region: "middle_east",
industry: "travel",
});
console.log(result);
} catch (error) {
// The base class for HTTP error responses
if (error instanceof errors.Flow9Error) {
console.log(error.message);
console.log(error.statusCode);
console.log(error.body);
console.log(error.headers);
// Depending on the method different errors may be thrown
if (error instanceof errors.ErrorBody) {
console.log(error.data$.success); // boolean
console.log(error.data$.error); // models.ErrorT
console.log(error.data$.meta); // models.Meta
}
}
}
}
run();
Error Classes
Primary errors:
Flow9Error: The base class for HTTP error responses.ErrorBody: Validation error. *
Network errors:
ConnectionError: HTTP client was unable to make a request to a server.RequestTimeoutError: HTTP request timed out due to an AbortSignal signal.RequestAbortedError: HTTP request was aborted by the client.InvalidRequestError: Any input used to create a request is invalid.UnexpectedClientError: Unrecognised or unexpected error.
Inherit from Flow9Error:
ResponseValidationError: Type mismatch between the data returned from the server and the structure expected by the SDK. Seeerror.rawValuefor the raw value anderror.pretty()for a nicely formatted multi-line string.
* Check the method documentation to see if the error is applicable.
Server Selection
Override Server URL Per-Client
The default server can be overridden globally by passing a URL to the serverURL: string optional parameter when initializing the SDK client instance. For example:
import { Flow9 } from "flow9-sdk";
const flow9 = new Flow9({
serverURL: "https://mwxpyoqrtdfxubdotbmj.supabase.co/functions/v1/public-api",
});
async function run() {
const result = await flow9.health.getHealth();
console.log(result);
}
run();
Custom HTTP Client
The TypeScript SDK makes API calls using an HTTPClient that wraps the native
Fetch API. This
client is a thin wrapper around fetch and provides the ability to attach hooks
around the request lifecycle that can be used to modify the request or handle
errors and response.
The HTTPClient constructor takes an optional fetcher argument that can be
used to integrate a third-party HTTP client or when writing tests to mock out
the HTTP client and feed in fixtures.
The following example shows how to:
- route requests through a proxy server using undici's ProxyAgent
- use the
"beforeRequest"hook to add a custom header and a timeout to requests - use the
"requestError"hook to log errors
import { Flow9 } from "flow9-sdk";
import { ProxyAgent } from "undici";
import { HTTPClient } from "flow9-sdk/lib/http";
const dispatcher = new ProxyAgent("http://proxy.example.com:8080");
const httpClient = new HTTPClient({
// 'fetcher' takes a function that has the same signature as native 'fetch'.
fetcher: (input, init) =>
// 'dispatcher' is specific to undici and not part of the standard Fetch API.
fetch(input, { ...init, dispatcher } as RequestInit),
});
httpClient.addHook("beforeRequest", (request) => {
const nextRequest = new Request(request, {
signal: request.signal || AbortSignal.timeout(5000)
});
nextRequest.headers.set("x-custom-header", "custom value");
return nextRequest;
});
httpClient.addHook("requestError", (error, request) => {
console.group("Request Error");
console.log("Reason:", `${error}`);
console.log("Endpoint:", `${request.method} ${request.url}`);
console.groupEnd();
});
const sdk = new Flow9({ httpClient: httpClient });Debugging
You can setup your SDK to emit debug logs for SDK requests and responses.
You can pass a logger that matches console's interface as an SDK option.
[!WARNING] Beware that debug logging will reveal secrets, like API tokens in headers, in log messages printed to a console or files. It's recommended to use this feature only during local development and not in production.
import { Flow9 } from "flow9-sdk";
const sdk = new Flow9({ debugLogger: console });Development
Maturity
This SDK is in beta, and there may be breaking changes between versions without a major version update. Therefore, we recommend pinning usage to a specific package version. This way, you can install the same version each time without breaking changes unless you are intentionally looking for the latest version.
Contributions
While we value open-source contributions to this SDK, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation. We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release.
