Skip to content

Releasing v3.27.0#124

Merged
cb-alish merged 1 commit into
masterfrom
release-3.27.0
Jun 30, 2026
Merged

Releasing v3.27.0#124
cb-alish merged 1 commit into
masterfrom
release-3.27.0

Conversation

@cb-alish

@cb-alish cb-alish commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

v3.27.0 (2026-06-30)


Enhancements:

  • Zod-backed request validation — Set enableValidation: true in the client configuration to validate outgoing request parameters against each endpoint's generated Zod schema before the API call is sent. Invalid payloads raise ChargebeeZodValidationError, which carries the offending actionName and the original ZodError (including issues and flatten()) for inspection.
  • Runtime dependency — Added zod (v4) as a runtime dependency to support request validation.

Adds opt-in Zod request validation across the SDK via enableValidation, a lazy schema loader, and a new ChargebeeZodValidationError export. Also introduces generated Zod schemas for many resources, updates public typings and exports, adds zod as a runtime dependency, and bumps version/changelog to v3.27.0.

@snyk-io

snyk-io Bot commented Jun 30, 2026

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Adds an opt-in enableValidation flag to the Chargebee Node SDK (v3.27.0) that validates request parameters against generated Zod v4 schemas before HTTP calls. Introduces ChargebeeZodValidationError, a lazy schema loader, RequestWrapper integration, ~60 generated src/schema/*.schema.ts files, a barrel index, public ESM/CJS exports, tests, and docs.

Changes

Zod Validation Feature

Layer / File(s) Summary
Contract types and ChargebeeZodValidationError class
src/chargebeeZodValidationError.ts, src/types.d.ts, types/index.d.ts, types/core.d.ts
ChargebeeZodValidationError extends Error with actionName and zodError fields; EnvType/Config gain enableValidation?: boolean; ResourceType gains resourceKey, actionName, and validationSchema.
Schema loader, RequestWrapper validation, and apiCall wiring
src/validationLoader.ts, src/RequestWrapper.ts, src/createChargebee.ts
getSchema() lazily imports the matching ./schema/<resource>.schema.js and returns the Zod object or null. _validateParams() calls schema.safeParse and throws ChargebeeZodValidationError. request() runs validation when env.enableValidation is set—GET requests validate params ?? {}, non-GET validate when params != null. _buildResources() stamps resourceKey/actionName on each apiCall.
ESM and CJS public exports
src/chargebee.cjs.ts, src/chargebee.cjs.worker.ts, src/chargebee.esm.ts, src/chargebee.esm.worker.ts
All four entry-point files import and re-export ChargebeeZodValidationError as a named export.
Generated Zod schema modules and barrel
src/schema/index.ts, src/schema/*.schema.ts
Auto-generated barrel re-exports all ~60 schema modules. Each module defines z.looseObject/z.object request-body schemas and z.infer TypeScript types for every action of its resource (addon through webhook_endpoint).
Tests, docs, and version bump
test/validation.test.ts, README.md, CHANGELOG.md, package.json, VERSION, src/environment.ts
Tests cover getSchema() field constraints/caching/null behavior and RequestWrapper integration (no-op when disabled, ChargebeeZodValidationError with actionName/zodError when enabled). README documents enableValidation usage. Version bumped to 3.27.0; zod ^4.3.6 added as a runtime dependency.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes


Comment @coderabbitai help to get the list of available commands.

@cb-alish cb-alish merged commit bf16050 into master Jun 30, 2026
7 of 8 checks passed
@cb-alish cb-alish deleted the release-3.27.0 branch June 30, 2026 08:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/RequestWrapper.ts`:
- Around line 104-117: The non-GET branch in RequestWrapper currently skips
validation when params is undefined, so calls like cb.invoice.charge() bypass
required-field checks and diverge from the documented behavior. Update
RequestWrapper._validateParams usage in RequestWrapper so non-GET requests
validate params ?? {} the same way GET does, ensuring omitted arguments are
treated as an empty object. Also extend the existing tests around the
charge/invoice path to cover the argument-less call case, not just
cb.invoice.charge({}).

In `@src/schema/business_entity.schema.ts`:
- Around line 9-15: Make the transfer payload arrays optional in
CreateTransfersBusinessEntityBodySchema. Update the schema so
active_resource_ids, destination_business_entity_ids, resource_types, and
reason_codes are optional like source_business_entity_ids, ensuring
createTransfers accepts valid contract payloads without rejecting omitted
arrays.

In `@src/schema/coupon_set.schema.ts`:
- Around line 21-23: The AddCouponCodesCouponSetBodySchema currently allows
missing or empty code values, so update the schema used by addCouponCodes to
require code and reject empty arrays. Adjust the zod definition in
AddCouponCodesCouponSetBodySchema so the code field is mandatory and non-empty,
while keeping the existing string length constraint on each coupon code.

In `@src/schema/customer.schema.ts`:
- Around line 888-924: The contact mutation body schemas currently allow empty
payloads because the top-level contact field is optional. Update
AddContactCustomerBodySchema, UpdateContactCustomerBodySchema, and
DeleteContactCustomerBodySchema so contact is required instead of optional,
while keeping the nested contact validators (AddContactCustomerContactSchema,
UpdateContactCustomerContactSchema, DeleteContactCustomerContactSchema)
unchanged. Ensure the exported body types inferred from these schemas continue
to reflect the required contact payload for addContact, updateContact, and
deleteContact.

In `@src/schema/differential_price.schema.ts`:
- Around line 9-125: Tighten the generated request schemas so validation
actually rejects bad payloads: replace the permissive z.looseObject bodies with
strict object schemas in CreateDifferentialPriceBodySchema,
UpdateDifferentialPriceBodySchema, DeleteDifferentialPriceBodySchema,
ListDifferentialPriceBodySchema, and RetrieveDifferentialPriceBodySchema, and
remove the repeated array(T.optional()) pattern in the parent/tier helper
schemas so array elements are required rather than allowing undefined entries.
Apply the same generated-schema fix consistently across the other modules that
use these patterns, so enableValidation catches unknown keys and malformed
arrays before the HTTP call.

In `@src/schema/in_app_subscription.schema.ts`:
- Around line 9-17: The ProcessReceiptInAppSubscriptionProductSchema currently
constrains period_unit with an overly short string limit, which rejects valid
API values. Update the period_unit field in
ProcessReceiptInAppSubscriptionProductSchema to use the API enum values day,
week, month, and year instead of a max-length-only string, while keeping the
rest of the schema unchanged.

In `@src/schema/item.schema.ts`:
- Around line 173-179: The bundle configuration schema is using the wrong
validator: ListItemBundleConfigurationSchema currently reuses
ListItemTypeSchema, which allows plan/addon/charge instead of the
bundle-specific value. Update the type field in
ListItemBundleConfigurationSchema to use a bundle-specific schema that matches
the create/update contract for fixed, and keep the change localized to the
schema definitions so the item filtering path validates the real bundle shape
correctly.

In `@src/schema/ledger_operation.schema.ts`:
- Around line 53-61: The list-operations request schema currently makes
subscription_id required in ListLedgerOperationsLedgerOperationBodySchema, which
blocks valid optional-query calls. Update that schema in
ledger_operation.schema.ts so subscription_id is optional like the other filter
fields, while keeping the existing validators for unit_id, created_at, type, and
sort_by intact.

In `@src/schema/metered_feature.schema.ts`:
- Around line 9-19: The nested array fields in
CreateMeteredFeatureColumnDefinitionsSchema are defined incorrectly: column_name
and data_type should be optional arrays, not arrays of optional elements. Update
the z.object schema so each field itself is optional, while each array still
validates its item type and constraints, and keep
CreateMeteredFeatureBodySchema.column_definitions optional as-is.

In `@src/schema/payment_voucher.schema.ts`:
- Around line 29-71: The voucher list schema exports are using the wrong casing,
so getSchema() cannot resolve them for the
PaymentVoucher.payment_vouchersForInvoice and
PaymentVoucher.payment_vouchersForCustomer actions. Rename the schema symbols
and their exported body types to match the expected identifiers with
“ForInvoice” and “ForCustomer” capitalization, and update the local references
in the same schema module so validation can be found and enabled correctly.

In `@src/schema/usage_event.schema.ts`:
- Around line 28-30: The BatchIngestUsageEventBodySchema currently makes the
events field optional, which allows empty batchIngest payloads to pass
validation. Update the schema in BatchIngestUsageEventBodySchema so events is
required instead of optional, and keep the shape aligned with
BatchIngestUsageEventEventsSchema to ensure enableValidation rejects {} for
batch ingest requests.

In `@src/schema/webhook_endpoint.schema.ts`:
- Around line 9-17: The CreateWebhookEndpointBodySchema currently allows
undefined values inside enabled_events because the inner event enum is marked
optional, so tighten that schema to require every array item to be a valid
event-name string. Update the enabled_events definition in
CreateWebhookEndpointBodySchema so the array element schema is not optional, and
keep the rest of the webhook endpoint fields unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Enterprise

Run ID: 0c0519f2-3cb5-4cb0-87bb-26ed9c1d1688

📥 Commits

Reviewing files that changed from the base of the PR and between 3f5e8e6 and 67e68c3.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (89)
  • CHANGELOG.md
  • README.md
  • VERSION
  • package.json
  • src/RequestWrapper.ts
  • src/chargebee.cjs.ts
  • src/chargebee.cjs.worker.ts
  • src/chargebee.esm.ts
  • src/chargebee.esm.worker.ts
  • src/chargebeeZodValidationError.ts
  • src/createChargebee.ts
  • src/environment.ts
  • src/schema/addon.schema.ts
  • src/schema/address.schema.ts
  • src/schema/alert.schema.ts
  • src/schema/alert_status.schema.ts
  • src/schema/attached_item.schema.ts
  • src/schema/business_entity.schema.ts
  • src/schema/card.schema.ts
  • src/schema/comment.schema.ts
  • src/schema/coupon.schema.ts
  • src/schema/coupon_code.schema.ts
  • src/schema/coupon_set.schema.ts
  • src/schema/credit_note.schema.ts
  • src/schema/currency.schema.ts
  • src/schema/customer.schema.ts
  • src/schema/customer_entitlement.schema.ts
  • src/schema/differential_price.schema.ts
  • src/schema/entitlement.schema.ts
  • src/schema/entitlement_override.schema.ts
  • src/schema/estimate.schema.ts
  • src/schema/event.schema.ts
  • src/schema/export.schema.ts
  • src/schema/feature.schema.ts
  • src/schema/gift.schema.ts
  • src/schema/grant_block.schema.ts
  • src/schema/hosted_page.schema.ts
  • src/schema/in_app_subscription.schema.ts
  • src/schema/index.ts
  • src/schema/invoice.schema.ts
  • src/schema/item.schema.ts
  • src/schema/item_entitlement.schema.ts
  • src/schema/item_family.schema.ts
  • src/schema/item_price.schema.ts
  • src/schema/ledger_account_balance.schema.ts
  • src/schema/ledger_operation.schema.ts
  • src/schema/meter.schema.ts
  • src/schema/metered_feature.schema.ts
  • src/schema/non_subscription.schema.ts
  • src/schema/offer_event.schema.ts
  • src/schema/offer_fulfillment.schema.ts
  • src/schema/omnichannel_one_time_order.schema.ts
  • src/schema/omnichannel_subscription.schema.ts
  • src/schema/omnichannel_subscription_item.schema.ts
  • src/schema/order.schema.ts
  • src/schema/payment_intent.schema.ts
  • src/schema/payment_schedule_scheme.schema.ts
  • src/schema/payment_source.schema.ts
  • src/schema/payment_voucher.schema.ts
  • src/schema/personalized_offer.schema.ts
  • src/schema/plan.schema.ts
  • src/schema/portal_session.schema.ts
  • src/schema/price_variant.schema.ts
  • src/schema/pricing_page_session.schema.ts
  • src/schema/promotional_credit.schema.ts
  • src/schema/promotional_grant.schema.ts
  • src/schema/purchase.schema.ts
  • src/schema/quote.schema.ts
  • src/schema/ramp.schema.ts
  • src/schema/recorded_purchase.schema.ts
  • src/schema/resource_migration.schema.ts
  • src/schema/site_migration_detail.schema.ts
  • src/schema/subscription.schema.ts
  • src/schema/subscription_entitlement.schema.ts
  • src/schema/time_machine.schema.ts
  • src/schema/transaction.schema.ts
  • src/schema/unbilled_charge.schema.ts
  • src/schema/usage.schema.ts
  • src/schema/usage_charge.schema.ts
  • src/schema/usage_event.schema.ts
  • src/schema/usage_file.schema.ts
  • src/schema/usage_summary.schema.ts
  • src/schema/virtual_bank_account.schema.ts
  • src/schema/webhook_endpoint.schema.ts
  • src/types.d.ts
  • src/validationLoader.ts
  • test/validation.test.ts
  • types/core.d.ts
  • types/index.d.ts

Comment thread src/RequestWrapper.ts
Comment on lines +104 to +117
if (this.apiCall.httpMethod === 'GET') {
RequestWrapper._validateParams(
params ?? {},
schema,
this.apiCall.methodName,
);
} else if (params != null) {
RequestWrapper._validateParams(
params,
schema,
this.apiCall.methodName,
);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Omitted non-GET params skip validation, contradicting the docs.

For non-GET, validation only runs when params != null. An argument-less call (e.g. cb.invoice.charge()) leaves params undefined → validation is skipped, the {} body is sent, and required-field schemas never fire. README (Line 129) promises omitted params are validated as {} for every request. Either align the code (preferred, catches missing required fields) or fix the doc.

The existing test only exercises cb.invoice.charge({}) (truthy {}), so it misses this path.

🔧 Align non-GET with documented behavior
-        if (this.apiCall.httpMethod === 'GET') {
-          RequestWrapper._validateParams(
-            params ?? {},
-            schema,
-            this.apiCall.methodName,
-          );
-        } else if (params != null) {
-          RequestWrapper._validateParams(
-            params,
-            schema,
-            this.apiCall.methodName,
-          );
-        }
+        RequestWrapper._validateParams(
+          params ?? {},
+          schema,
+          this.apiCall.methodName,
+        );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (this.apiCall.httpMethod === 'GET') {
RequestWrapper._validateParams(
params ?? {},
schema,
this.apiCall.methodName,
);
} else if (params != null) {
RequestWrapper._validateParams(
params,
schema,
this.apiCall.methodName,
);
}
}
RequestWrapper._validateParams(
params ?? {},
schema,
this.apiCall.methodName,
);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/RequestWrapper.ts` around lines 104 - 117, The non-GET branch in
RequestWrapper currently skips validation when params is undefined, so calls
like cb.invoice.charge() bypass required-field checks and diverge from the
documented behavior. Update RequestWrapper._validateParams usage in
RequestWrapper so non-GET requests validate params ?? {} the same way GET does,
ensuring omitted arguments are treated as an empty object. Also extend the
existing tests around the charge/invoice path to cover the argument-less call
case, not just cb.invoice.charge({}).

Comment on lines +9 to +15
const CreateTransfersBusinessEntityBodySchema = z.looseObject({
active_resource_ids: z.array(z.string().max(50).optional()),
destination_business_entity_ids: z.array(z.string().max(50).optional()),
source_business_entity_ids: z.array(z.string().max(50).optional()).optional(),
resource_types: z.array(z.string().max(50).optional()),
reason_codes: z.array(z.string().max(50).optional()),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the transfer arrays optional.

CreateTransfersBusinessEntityBodySchema currently makes active_resource_ids, destination_business_entity_ids, resource_types, and reason_codes required. That conflicts with the generated contract and will reject valid createTransfers payloads before the API call.

Fix
 const CreateTransfersBusinessEntityBodySchema = z.looseObject({
-  active_resource_ids: z.array(z.string().max(50).optional()),
-  destination_business_entity_ids: z.array(z.string().max(50).optional()),
+  active_resource_ids: z.array(z.string().max(50)).optional(),
+  destination_business_entity_ids: z.array(z.string().max(50)).optional(),
   source_business_entity_ids: z.array(z.string().max(50).optional()).optional(),
-  resource_types: z.array(z.string().max(50).optional()),
-  reason_codes: z.array(z.string().max(50).optional()),
+  resource_types: z.array(z.string().max(50)).optional(),
+  reason_codes: z.array(z.string().max(50)).optional(),
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const CreateTransfersBusinessEntityBodySchema = z.looseObject({
active_resource_ids: z.array(z.string().max(50).optional()),
destination_business_entity_ids: z.array(z.string().max(50).optional()),
source_business_entity_ids: z.array(z.string().max(50).optional()).optional(),
resource_types: z.array(z.string().max(50).optional()),
reason_codes: z.array(z.string().max(50).optional()),
});
const CreateTransfersBusinessEntityBodySchema = z.looseObject({
active_resource_ids: z.array(z.string().max(50)).optional(),
destination_business_entity_ids: z.array(z.string().max(50)).optional(),
source_business_entity_ids: z.array(z.string().max(50).optional()).optional(),
resource_types: z.array(z.string().max(50)).optional(),
reason_codes: z.array(z.string().max(50)).optional(),
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/schema/business_entity.schema.ts` around lines 9 - 15, Make the transfer
payload arrays optional in CreateTransfersBusinessEntityBodySchema. Update the
schema so active_resource_ids, destination_business_entity_ids, resource_types,
and reason_codes are optional like source_business_entity_ids, ensuring
createTransfers accepts valid contract payloads without rejecting omitted
arrays.

Comment on lines +21 to +23
const AddCouponCodesCouponSetBodySchema = z.looseObject({
code: z.array(z.string().max(50).optional()).optional(),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Require coupon codes for addCouponCodes.

The current schema accepts {} and an empty code array, so a no-op request can pass validation and fail later at the API layer. Make code required and non-empty.

Fix
 const AddCouponCodesCouponSetBodySchema = z.looseObject({
-  code: z.array(z.string().max(50).optional()).optional(),
+  code: z.array(z.string().max(50)).min(1),
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const AddCouponCodesCouponSetBodySchema = z.looseObject({
code: z.array(z.string().max(50).optional()).optional(),
});
const AddCouponCodesCouponSetBodySchema = z.looseObject({
code: z.array(z.string().max(50)).min(1),
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/schema/coupon_set.schema.ts` around lines 21 - 23, The
AddCouponCodesCouponSetBodySchema currently allows missing or empty code values,
so update the schema used by addCouponCodes to require code and reject empty
arrays. Adjust the zod definition in AddCouponCodesCouponSetBodySchema so the
code field is mandatory and non-empty, while keeping the existing string length
constraint on each coupon code.

Comment on lines +888 to +924
const AddContactCustomerBodySchema = z.looseObject({
contact: AddContactCustomerContactSchema.optional(),
});
export { AddContactCustomerBodySchema };
export type AddContactCustomerBody = z.infer<
typeof AddContactCustomerBodySchema
>;

//Customer.updateContact

const UpdateContactCustomerContactSchema = z.object({
id: z.string().max(150),
first_name: z.string().max(150).optional(),
last_name: z.string().max(150).optional(),
email: z.string().email().max(70).optional(),
phone: z.string().max(50).optional(),
label: z.string().max(50).optional(),
enabled: z.boolean().default(false).optional(),
send_billing_email: z.boolean().default(false).optional(),
send_account_email: z.boolean().default(false).optional(),
});
const UpdateContactCustomerBodySchema = z.looseObject({
contact: UpdateContactCustomerContactSchema.optional(),
});
export { UpdateContactCustomerBodySchema };
export type UpdateContactCustomerBody = z.infer<
typeof UpdateContactCustomerBodySchema
>;

//Customer.deleteContact

const DeleteContactCustomerContactSchema = z.object({
id: z.string().max(150),
});
const DeleteContactCustomerBodySchema = z.looseObject({
contact: DeleteContactCustomerContactSchema.optional(),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the contact payload required for contact mutations.

addContact, updateContact, and deleteContact all accept an empty body right now because the top-level contact object is optional. That defeats the validation gate for these actions.

Fix
 const AddContactCustomerBodySchema = z.looseObject({
-  contact: AddContactCustomerContactSchema.optional(),
+  contact: AddContactCustomerContactSchema,
 });
@@
 const UpdateContactCustomerBodySchema = z.looseObject({
-  contact: UpdateContactCustomerContactSchema.optional(),
+  contact: UpdateContactCustomerContactSchema,
 });
@@
 const DeleteContactCustomerBodySchema = z.looseObject({
-  contact: DeleteContactCustomerContactSchema.optional(),
+  contact: DeleteContactCustomerContactSchema,
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const AddContactCustomerBodySchema = z.looseObject({
contact: AddContactCustomerContactSchema.optional(),
});
export { AddContactCustomerBodySchema };
export type AddContactCustomerBody = z.infer<
typeof AddContactCustomerBodySchema
>;
//Customer.updateContact
const UpdateContactCustomerContactSchema = z.object({
id: z.string().max(150),
first_name: z.string().max(150).optional(),
last_name: z.string().max(150).optional(),
email: z.string().email().max(70).optional(),
phone: z.string().max(50).optional(),
label: z.string().max(50).optional(),
enabled: z.boolean().default(false).optional(),
send_billing_email: z.boolean().default(false).optional(),
send_account_email: z.boolean().default(false).optional(),
});
const UpdateContactCustomerBodySchema = z.looseObject({
contact: UpdateContactCustomerContactSchema.optional(),
});
export { UpdateContactCustomerBodySchema };
export type UpdateContactCustomerBody = z.infer<
typeof UpdateContactCustomerBodySchema
>;
//Customer.deleteContact
const DeleteContactCustomerContactSchema = z.object({
id: z.string().max(150),
});
const DeleteContactCustomerBodySchema = z.looseObject({
contact: DeleteContactCustomerContactSchema.optional(),
});
const AddContactCustomerBodySchema = z.looseObject({
contact: AddContactCustomerContactSchema,
});
export { AddContactCustomerBodySchema };
export type AddContactCustomerBody = z.infer<
typeof AddContactCustomerBodySchema
>;
//Customer.updateContact
const UpdateContactCustomerContactSchema = z.object({
id: z.string().max(150),
first_name: z.string().max(150).optional(),
last_name: z.string().max(150).optional(),
email: z.string().email().max(70).optional(),
phone: z.string().max(50).optional(),
label: z.string().max(50).optional(),
enabled: z.boolean().default(false).optional(),
send_billing_email: z.boolean().default(false).optional(),
send_account_email: z.boolean().default(false).optional(),
});
const UpdateContactCustomerBodySchema = z.looseObject({
contact: UpdateContactCustomerContactSchema,
});
export { UpdateContactCustomerBodySchema };
export type UpdateContactCustomerBody = z.infer<
typeof UpdateContactCustomerBodySchema
>;
//Customer.deleteContact
const DeleteContactCustomerContactSchema = z.object({
id: z.string().max(150),
});
const DeleteContactCustomerBodySchema = z.looseObject({
contact: DeleteContactCustomerContactSchema,
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/schema/customer.schema.ts` around lines 888 - 924, The contact mutation
body schemas currently allow empty payloads because the top-level contact field
is optional. Update AddContactCustomerBodySchema,
UpdateContactCustomerBodySchema, and DeleteContactCustomerBodySchema so contact
is required instead of optional, while keeping the nested contact validators
(AddContactCustomerContactSchema, UpdateContactCustomerContactSchema,
DeleteContactCustomerContactSchema) unchanged. Ensure the exported body types
inferred from these schemas continue to reflect the required contact payload for
addContact, updateContact, and deleteContact.

Comment on lines +9 to +125
const CreateDifferentialPriceParentPeriodsSchema = z.object({
period_unit: z.array(z.enum(['day', 'week', 'month', 'year']).optional()),
period: z.array(z.array(z.string().optional()).optional()).optional(),
});
const CreateDifferentialPriceTiersSchema = z.object({
starting_unit: z.array(z.number().int().min(1).optional()).optional(),
ending_unit: z.array(z.number().int().optional()).optional(),
price: z.array(z.number().int().min(0).optional()).optional(),
starting_unit_in_decimal: z.array(z.string().max(33).optional()).optional(),
ending_unit_in_decimal: z.array(z.string().max(33).optional()).optional(),
price_in_decimal: z.array(z.string().max(39).optional()).optional(),
pricing_type: z
.array(z.enum(['per_unit', 'flat_fee', 'package']).optional())
.optional(),
package_size: z.array(z.number().int().min(1).optional()).optional(),
});
const CreateDifferentialPriceBodySchema = z.looseObject({
parent_item_id: z.string().max(100),
price: z.number().int().min(0).optional(),
price_in_decimal: z.string().max(39).optional(),
business_entity_id: z.string().max(50).optional(),
parent_periods: CreateDifferentialPriceParentPeriodsSchema.optional(),
tiers: CreateDifferentialPriceTiersSchema.optional(),
});
export { CreateDifferentialPriceBodySchema };
export type CreateDifferentialPriceBody = z.infer<
typeof CreateDifferentialPriceBodySchema
>;

//DifferentialPrice.retrieve

const RetrieveDifferentialPriceBodySchema = z.looseObject({
item_price_id: z.string().max(100),
});
export { RetrieveDifferentialPriceBodySchema };
export type RetrieveDifferentialPriceBody = z.infer<
typeof RetrieveDifferentialPriceBodySchema
>;

//DifferentialPrice.update

const UpdateDifferentialPriceParentPeriodsSchema = z.object({
period_unit: z.array(z.enum(['day', 'week', 'month', 'year']).optional()),
period: z.array(z.array(z.string().optional()).optional()).optional(),
});
const UpdateDifferentialPriceTiersSchema = z.object({
starting_unit: z.array(z.number().int().min(1).optional()).optional(),
ending_unit: z.array(z.number().int().optional()).optional(),
price: z.array(z.number().int().min(0).optional()).optional(),
starting_unit_in_decimal: z.array(z.string().max(33).optional()).optional(),
ending_unit_in_decimal: z.array(z.string().max(33).optional()).optional(),
price_in_decimal: z.array(z.string().max(39).optional()).optional(),
pricing_type: z
.array(z.enum(['per_unit', 'flat_fee', 'package']).optional())
.optional(),
package_size: z.array(z.number().int().min(1).optional()).optional(),
});
const UpdateDifferentialPriceBodySchema = z.looseObject({
item_price_id: z.string().max(100),
price: z.number().int().min(0).optional(),
price_in_decimal: z.string().max(39).optional(),
parent_periods: UpdateDifferentialPriceParentPeriodsSchema.optional(),
tiers: UpdateDifferentialPriceTiersSchema.optional(),
});
export { UpdateDifferentialPriceBodySchema };
export type UpdateDifferentialPriceBody = z.infer<
typeof UpdateDifferentialPriceBodySchema
>;

//DifferentialPrice.delete

const DeleteDifferentialPriceBodySchema = z.looseObject({
item_price_id: z.string().max(100),
});
export { DeleteDifferentialPriceBodySchema };
export type DeleteDifferentialPriceBody = z.infer<
typeof DeleteDifferentialPriceBodySchema
>;

//DifferentialPrice.list

const ListDifferentialPriceItemPriceIdSchema = z.object({
is: z.string().min(1).optional(),
is_not: z.string().min(1).optional(),
starts_with: z.string().min(1).optional(),
in: z.string().regex(RegExp('^\\[(.*)(,.*)*\\]$')).optional(),
not_in: z.string().regex(RegExp('^\\[(.*)(,.*)*\\]$')).optional(),
});
const ListDifferentialPriceItemIdSchema = z.object({
is: z.string().min(1).optional(),
is_not: z.string().min(1).optional(),
starts_with: z.string().min(1).optional(),
in: z.string().regex(RegExp('^\\[(.*)(,.*)*\\]$')).optional(),
not_in: z.string().regex(RegExp('^\\[(.*)(,.*)*\\]$')).optional(),
});
const ListDifferentialPriceIdSchema = z.object({
is: z.string().min(1).optional(),
is_not: z.string().min(1).optional(),
starts_with: z.string().min(1).optional(),
in: z.string().regex(RegExp('^\\[(.*)(,.*)*\\]$')).optional(),
not_in: z.string().regex(RegExp('^\\[(.*)(,.*)*\\]$')).optional(),
});
const ListDifferentialPriceParentItemIdSchema = z.object({
is: z.string().min(1).optional(),
is_not: z.string().min(1).optional(),
starts_with: z.string().min(1).optional(),
in: z.string().regex(RegExp('^\\[(.*)(,.*)*\\]$')).optional(),
not_in: z.string().regex(RegExp('^\\[(.*)(,.*)*\\]$')).optional(),
});
const ListDifferentialPriceBodySchema = z.looseObject({
limit: z.number().int().min(1).max(100).optional(),
offset: z.string().max(1000).optional(),
item_price_id: ListDifferentialPriceItemPriceIdSchema.optional(),
item_id: ListDifferentialPriceItemIdSchema.optional(),
id: ListDifferentialPriceIdSchema.optional(),
parent_item_id: ListDifferentialPriceParentItemIdSchema.optional(),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Tighten these request schemas before shipping validation.

z.looseObject lets unknown keys through, and the repeated array(T.optional()) pattern accepts undefined elements. That means enableValidation can still miss misspelled fields and malformed array payloads, so bad requests may reach the API.

Please generate strict bodies and non-optional element schemas here (and in the other generated modules using the same pattern). As per PR objective, this feature is supposed to validate outgoing params before the HTTP call.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/schema/differential_price.schema.ts` around lines 9 - 125, Tighten the
generated request schemas so validation actually rejects bad payloads: replace
the permissive z.looseObject bodies with strict object schemas in
CreateDifferentialPriceBodySchema, UpdateDifferentialPriceBodySchema,
DeleteDifferentialPriceBodySchema, ListDifferentialPriceBodySchema, and
RetrieveDifferentialPriceBodySchema, and remove the repeated array(T.optional())
pattern in the parent/tier helper schemas so array elements are required rather
than allowing undefined entries. Apply the same generated-schema fix
consistently across the other modules that use these patterns, so
enableValidation catches unknown keys and malformed arrays before the HTTP call.

Comment on lines +53 to +61
const ListLedgerOperationsLedgerOperationBodySchema = z.looseObject({
limit: z.number().int().min(1).max(100).optional(),
offset: z.string().max(1000).optional(),
subscription_id: ListLedgerOperationsLedgerOperationSubscriptionIdSchema,
unit_id: ListLedgerOperationsLedgerOperationUnitIdSchema.optional(),
created_at: ListLedgerOperationsLedgerOperationCreatedAtSchema.optional(),
type: ListLedgerOperationsLedgerOperationTypeSchema.optional(),
sort_by: ListLedgerOperationsLedgerOperationSortBySchema.optional(),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make subscription_id optional for list calls.

listLedgerOperations is documented as having optional query fields, but this schema currently forces every caller to send a top-level subscription_id. That rejects valid unfiltered or unit_id-only requests before the API call is sent.

♻️ Proposed fix
 const ListLedgerOperationsLedgerOperationBodySchema = z.looseObject({
   limit: z.number().int().min(1).max(100).optional(),
   offset: z.string().max(1000).optional(),
-  subscription_id: ListLedgerOperationsLedgerOperationSubscriptionIdSchema,
+  subscription_id: ListLedgerOperationsLedgerOperationSubscriptionIdSchema.optional(),
   unit_id: ListLedgerOperationsLedgerOperationUnitIdSchema.optional(),
   created_at: ListLedgerOperationsLedgerOperationCreatedAtSchema.optional(),
   type: ListLedgerOperationsLedgerOperationTypeSchema.optional(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const ListLedgerOperationsLedgerOperationBodySchema = z.looseObject({
limit: z.number().int().min(1).max(100).optional(),
offset: z.string().max(1000).optional(),
subscription_id: ListLedgerOperationsLedgerOperationSubscriptionIdSchema,
unit_id: ListLedgerOperationsLedgerOperationUnitIdSchema.optional(),
created_at: ListLedgerOperationsLedgerOperationCreatedAtSchema.optional(),
type: ListLedgerOperationsLedgerOperationTypeSchema.optional(),
sort_by: ListLedgerOperationsLedgerOperationSortBySchema.optional(),
});
const ListLedgerOperationsLedgerOperationBodySchema = z.looseObject({
limit: z.number().int().min(1).max(100).optional(),
offset: z.string().max(1000).optional(),
subscription_id: ListLedgerOperationsLedgerOperationSubscriptionIdSchema.optional(),
unit_id: ListLedgerOperationsLedgerOperationUnitIdSchema.optional(),
created_at: ListLedgerOperationsLedgerOperationCreatedAtSchema.optional(),
type: ListLedgerOperationsLedgerOperationTypeSchema.optional(),
sort_by: ListLedgerOperationsLedgerOperationSortBySchema.optional(),
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/schema/ledger_operation.schema.ts` around lines 53 - 61, The
list-operations request schema currently makes subscription_id required in
ListLedgerOperationsLedgerOperationBodySchema, which blocks valid optional-query
calls. Update that schema in ledger_operation.schema.ts so subscription_id is
optional like the other filter fields, while keeping the existing validators for
unit_id, created_at, type, and sort_by intact.

Comment on lines +9 to +19
const CreateMeteredFeatureColumnDefinitionsSchema = z.object({
column_name: z.array(z.string().max(100).optional()),
data_type: z.array(z.enum(['number', 'string']).optional()),
});
const CreateMeteredFeatureBodySchema = z.looseObject({
name: z.string().max(50),
description: z.string().max(250).optional(),
feature_unit: z.string().max(50),
query: z.string().max(500),
column_definitions: CreateMeteredFeatureColumnDefinitionsSchema.optional(),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the nested arrays optional, not their elements.

column_name and data_type are documented as optional arrays, but the current schema requires both fields whenever column_definitions is present and also permits undefined entries inside the arrays. That rejects valid payloads and weakens validation.

♻️ Proposed fix
 const CreateMeteredFeatureColumnDefinitionsSchema = z.object({
-  column_name: z.array(z.string().max(100).optional()),
-  data_type: z.array(z.enum(['number', 'string']).optional()),
+  column_name: z.array(z.string().max(100)).optional(),
+  data_type: z.array(z.enum(['number', 'string'])).optional(),
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const CreateMeteredFeatureColumnDefinitionsSchema = z.object({
column_name: z.array(z.string().max(100).optional()),
data_type: z.array(z.enum(['number', 'string']).optional()),
});
const CreateMeteredFeatureBodySchema = z.looseObject({
name: z.string().max(50),
description: z.string().max(250).optional(),
feature_unit: z.string().max(50),
query: z.string().max(500),
column_definitions: CreateMeteredFeatureColumnDefinitionsSchema.optional(),
});
const CreateMeteredFeatureColumnDefinitionsSchema = z.object({
column_name: z.array(z.string().max(100)).optional(),
data_type: z.array(z.enum(['number', 'string'])).optional(),
});
const CreateMeteredFeatureBodySchema = z.looseObject({
name: z.string().max(50),
description: z.string().max(250).optional(),
feature_unit: z.string().max(50),
query: z.string().max(500),
column_definitions: CreateMeteredFeatureColumnDefinitionsSchema.optional(),
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/schema/metered_feature.schema.ts` around lines 9 - 19, The nested array
fields in CreateMeteredFeatureColumnDefinitionsSchema are defined incorrectly:
column_name and data_type should be optional arrays, not arrays of optional
elements. Update the z.object schema so each field itself is optional, while
each array still validates its item type and constraints, and keep
CreateMeteredFeatureBodySchema.column_definitions optional as-is.

Comment on lines +29 to +71
const PaymentVouchersforinvoicePaymentVoucherStatusSchema = z.object({
is: z.enum(['active', 'consumed', 'expired', 'failure']).optional(),
is_not: z.enum(['active', 'consumed', 'expired', 'failure']).optional(),
in: z.enum(['active', 'consumed', 'expired', 'failure']).optional(),
not_in: z.enum(['active', 'consumed', 'expired', 'failure']).optional(),
});
const PaymentVouchersforinvoicePaymentVoucherSortBySchema = z.looseObject({
asc: z.enum(['date', 'updated_at']).optional(),
desc: z.enum(['date', 'updated_at']).optional(),
});
const PaymentVouchersforinvoicePaymentVoucherBodySchema = z.looseObject({
limit: z.number().int().min(1).max(100).optional(),
offset: z.string().max(1000).optional(),
status: PaymentVouchersforinvoicePaymentVoucherStatusSchema.optional(),
sort_by: PaymentVouchersforinvoicePaymentVoucherSortBySchema.optional(),
});
export { PaymentVouchersforinvoicePaymentVoucherBodySchema };
export type PaymentVouchersforinvoicePaymentVoucherBody = z.infer<
typeof PaymentVouchersforinvoicePaymentVoucherBodySchema
>;

//PaymentVoucher.payment_vouchersForCustomer

const PaymentVouchersforcustomerPaymentVoucherStatusSchema = z.object({
is: z.enum(['active', 'consumed', 'expired', 'failure']).optional(),
is_not: z.enum(['active', 'consumed', 'expired', 'failure']).optional(),
in: z.enum(['active', 'consumed', 'expired', 'failure']).optional(),
not_in: z.enum(['active', 'consumed', 'expired', 'failure']).optional(),
});
const PaymentVouchersforcustomerPaymentVoucherSortBySchema = z.looseObject({
asc: z.enum(['date', 'updated_at']).optional(),
desc: z.enum(['date', 'updated_at']).optional(),
});
const PaymentVouchersforcustomerPaymentVoucherBodySchema = z.looseObject({
limit: z.number().int().min(1).max(100).optional(),
offset: z.string().max(1000).optional(),
status: PaymentVouchersforcustomerPaymentVoucherStatusSchema.optional(),
sort_by: PaymentVouchersforcustomerPaymentVoucherSortBySchema.optional(),
});
export { PaymentVouchersforcustomerPaymentVoucherBodySchema };
export type PaymentVouchersforcustomerPaymentVoucherBody = z.infer<
typeof PaymentVouchersforcustomerPaymentVoucherBodySchema
>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fix the export casing for the voucher list schemas.

getSchema() will look for PaymentVouchersForInvoice... and PaymentVouchersForCustomer..., but these exports are spelled PaymentVouchersforinvoice... and PaymentVouchersforcustomer.... Validation will return null for both actions and silently skip enableValidation here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/schema/payment_voucher.schema.ts` around lines 29 - 71, The voucher list
schema exports are using the wrong casing, so getSchema() cannot resolve them
for the PaymentVoucher.payment_vouchersForInvoice and
PaymentVoucher.payment_vouchersForCustomer actions. Rename the schema symbols
and their exported body types to match the expected identifiers with
“ForInvoice” and “ForCustomer” capitalization, and update the local references
in the same schema module so validation can be found and enabled correctly.

Comment on lines +28 to +30
const BatchIngestUsageEventBodySchema = z.looseObject({
events: BatchIngestUsageEventEventsSchema.optional(),
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Require events for batchIngest.

Making the whole events block optional lets enableValidation accept {} for a batch-ingest call, so malformed requests still reach the API. If empty batches are not supported, this needs to be required here.

♻️ Proposed fix
 const BatchIngestUsageEventBodySchema = z.looseObject({
-  events: BatchIngestUsageEventEventsSchema.optional(),
+  events: BatchIngestUsageEventEventsSchema,
 });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const BatchIngestUsageEventBodySchema = z.looseObject({
events: BatchIngestUsageEventEventsSchema.optional(),
});
const BatchIngestUsageEventBodySchema = z.looseObject({
events: BatchIngestUsageEventEventsSchema,
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/schema/usage_event.schema.ts` around lines 28 - 30, The
BatchIngestUsageEventBodySchema currently makes the events field optional, which
allows empty batchIngest payloads to pass validation. Update the schema in
BatchIngestUsageEventBodySchema so events is required instead of optional, and
keep the shape aligned with BatchIngestUsageEventEventsSchema to ensure
enableValidation rejects {} for batch ingest requests.

Comment on lines +9 to +17
const CreateWebhookEndpointBodySchema = z.looseObject({
name: z.string().max(50),
api_version: z.enum(['v1', 'v2']).optional(),
url: z.string().max(512),
primary_url: z.boolean().default(false).optional(),
disabled: z.boolean().default(false).optional(),
basic_auth_password: z.string().max(250).optional(),
basic_auth_username: z.string().max(250).optional(),
send_card_resource: z.boolean().default(false).optional(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the schema file and inspect the relevant sections with line numbers.
file="src/schema/webhook_endpoint.schema.ts"
wc -l "$file"
sed -n '1,220p' "$file" | cat -n

Repository: chargebee/chargebee-node

Length of output: 9796


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find references to webhook endpoint schema fields and enabled_events handling.
rg -n "enabled_events|basic_auth_password|primary_url|send_card_resource|webhook_endpoint" src || true

Repository: chargebee/chargebee-node

Length of output: 1364


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="src/schema/webhook_endpoint.schema.ts"

# Show the surrounding lines around the reported snippet.
sed -n '1,140p' "$file" | cat -n

# Also inspect any related schema definitions in the same file.
sed -n '140,320p' "$file" | cat -n

Repository: chargebee/chargebee-node

Length of output: 14278


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="src/schema/webhook_endpoint.schema.ts"

echo "=== file length ==="
wc -l "$file"

echo "=== relevant excerpts ==="
sed -n '1,120p' "$file" | nl -ba | sed -n '1,120p'

echo "=== references ==="
rg -n "enabled_events|url: z\.|z\.enum\(\['v1', 'v2'\]\)|webhook endpoint" src || true

Repository: chargebee/chargebee-node

Length of output: 291


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for webhook endpoint docs / schemas / request serialization behavior.
rg -n "webhook endpoint|webhook_endpoints|enabled_events|primary_url|send_card_resource|basic_auth_username|basic_auth_password" README* docs src test examples .github || true

Repository: chargebee/chargebee-node

Length of output: 1730


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check how often URL-like fields are validated and whether this schema differs from the rest of the repo.
rg -n "\.url\(\)|z\.string\(\)\.url|url: z\.string" src | head -n 200

Repository: chargebee/chargebee-node

Length of output: 4026


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the webhook endpoint API definitions.
sed -n '5010,5095p' src/resources/api_endpoints.ts | cat -n

Repository: chargebee/chargebee-node

Length of output: 2158


🏁 Script executed:

#!/bin/bash
set -euo pipefail

node - <<'JS'
const arr = ['a', undefined, 'b'];
console.log(JSON.stringify(arr));
JS

Repository: chargebee/chargebee-node

Length of output: 177


🌐 Web query:

Chargebee webhook endpoint create update API url parameter format enabled_events webhook_endpoints docs

💡 Result:

To manage Chargebee webhook endpoints via the API, use the base URL format: POST https://{site}.chargebee.com/api/v2/webhook_endpoints [1]. Regarding the parameters: - Webhook Endpoints: You can configure a maximum of five webhook endpoints per site [2][3]. - Parameter Format: When creating or updating an endpoint, common parameters include name (required, string, max 50 chars), url (required, string, max 512 chars), api_version (default: v2), primary_url (boolean), and disabled (boolean) [1][2]. - enabled_events: While the Chargebee API documentation lists enabled_events as an attribute [2][4], Chargebee's official knowledge base indicates that you cannot configure event filtering directly within the Chargebee endpoint settings to restrict notifications [3]. Instead, Chargebee recommends filtering webhook messages at your own endpoint to ignore irrelevant events [3]. The URL must use ports 80, 443, 8080, or 8443 [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for webhook endpoint documentation or generated spec sources that define the expected field shape.
rg -n "WebhookEndpoint|webhook endpoint|webhook_endpoints" . --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' || true

Repository: chargebee/chargebee-node

Length of output: 3132


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the API endpoint entries for webhook endpoints.
sed -n '5028,5075p' src/resources/api_endpoints.ts | cat -n

Repository: chargebee/chargebee-node

Length of output: 1321


🌐 Web query:

Chargebee webhook endpoint create API documentation url enabled_events validation

💡 Result:

To create a webhook endpoint in Chargebee, you should use the Create a webhook endpoint API [1]. API Documentation URL The official documentation for creating a webhook endpoint can be found at: https://apidocs.chargebee.com/docs/api/webhook_endpoints/create-a-webhook-endpoint [1]. Enabled Events Validation The enabled_events parameter is an optional field used to define the specific event types that will trigger the webhook [1][2][3]. 1. Format: It is an enumerated string that accepts specific event types [1][2]. 2. Validation/Source: You must ensure that the values provided in enabled_events match the supported event types defined by Chargebee [2][4]. A complete, authoritative list of these supported event types is maintained in the Chargebee Event Types documentation: https://apidocs.chargebee.com/docs/api/events/event-types [4]. 3. If this parameter is omitted or left empty, the behavior typically defaults to sending all events or follows site-specific configurations; however, explicitly defining this list is recommended for better control [1][2]. General Notes - API Endpoint: POST https://{site}.chargebee.com/api/v2/webhook_endpoints [1] - Site Limit: You can create up to five webhook endpoints per site [2][5][6]. - URL Constraints: The target url must use port 80, 443, 8080, or 8443 [1].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect a small section of the schema around enabled_events to confirm the exact nesting.
sed -n '18,30p' src/schema/webhook_endpoint.schema.ts | cat -n

Repository: chargebee/chargebee-node

Length of output: 589


Disallow undefined items in enabled_events.
enabled_events should be an array of event-name strings; making the inner enum optional lets invalid entries through.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/schema/webhook_endpoint.schema.ts` around lines 9 - 17, The
CreateWebhookEndpointBodySchema currently allows undefined values inside
enabled_events because the inner event enum is marked optional, so tighten that
schema to require every array item to be a valid event-name string. Update the
enabled_events definition in CreateWebhookEndpointBodySchema so the array
element schema is not optional, and keep the rest of the webhook endpoint fields
unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants